@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,1655 @@
1
+ // The learner: candidates in, curated lessons out, and a person told when the
2
+ // curation did not happen.
3
+ //
4
+ // The loop, daemon-side and deterministic up to the curator's own judgement:
5
+ //
6
+ // 1. MINE (src/learner/miner.ts) — at EOD − `lessons_mine_lead_min`, and on
7
+ // the operator's word (`qcr lessons mine`): every human outcome becomes a
8
+ // row of `lesson_candidates`, deduplicated by a deterministic ref, with a
9
+ // class. The first mine also turns the digests' lost "Learnings" into
10
+ // candidates (`source = digest-backfill`), and a post-approval recheck's
11
+ // APPROVE → REQUEST_CHANGES reversal is one too (`source = recheck`): the
12
+ // reviewer's own conclusion, labelled so, offered after every other
13
+ // source.
14
+ // 2. PACKET (src/learner/packet.ts) — at the day's EOD fire: candidates
15
+ // first offered more than `lessons_expiry_ops_days` ago are expired
16
+ // (recorded on the row, in an event, and in the packet's own list — never
17
+ // silent); up to `lessons_packet_new_max` queued ones are offered for the first
18
+ // time; every undecided offered candidate is carried forward. Clustered
19
+ // by class with the 90-day recurrence, the fixed / left-open counterweight
20
+ // and the lessons already covering the class. Written to
21
+ // `state/eod/<day>/outcomes.{json,md}`; the sweep prompt points at it
22
+ // (`prepareEod` → wire.ts's `dayContext`).
23
+ // 3. RECEIPT — the curator answers with the whitelisted command
24
+ // `eod-curation-receipt` (src/learner/receipt.ts): per candidate,
25
+ // adopted / declined with a reason / refused (the write failed).
26
+ // 4. VERIFY — an adoption counts only when a lesson in `experiences/`
27
+ // carries a typed header whose evidence cites the candidate
28
+ // (`cand:c<id>`, src/memory/lessons.ts). Checked against memory while it
29
+ // reads healthy; a claimed lesson memory does not hold, or a refused
30
+ // write, wakes `eod_lessons_not_persisted` and puts the candidate back
31
+ // in the packet. No receipt `lessons_receipt_wait_min` after the sweep
32
+ // was delivered wakes `eod_curation_missing`. Both are wake kinds and
33
+ // ride status.json `memory.curation` — the board banner, the desktop
34
+ // notification and `qcr status`.
35
+ //
36
+ // Also measured: the look-for : don't-raise balance of the lessons in force
37
+ // (`lessonBalance`), the learner's health metric.
38
+ //
39
+ // Nothing here writes memory or GitHub, and nothing here is on the round
40
+ // path. Its tables live on the state database beside the outcome capture's.
41
+ import { withinBudget } from "@quickchatai/pi-quickreview/src/budget.js";
42
+ import { mkdirSync, renameSync, writeFileSync } from "node:fs";
43
+ import { join } from "node:path";
44
+ import { MEMORY_DEFAULTS } from "../config.js";
45
+ import { localWallClockToInstant, zoneDateString, zonedParts, parseHHMM } from "../eod.js";
46
+ import type { FileOutcome } from "../improvements.js";
47
+ import { jsonColumn } from "../lessonledger.js";
48
+ import { EXPERIENCES_AREA, areaOfId } from "../memory/layout.js";
49
+ import {
50
+ coveringLessons,
51
+ lessonBalance,
52
+ parseLesson,
53
+ type IndexedLesson,
54
+ } from "../memory/lessons.js";
55
+ import type { StateStore } from "../state.js";
56
+ import {
57
+ chainRoots,
58
+ outcomeRows,
59
+ findingClass,
60
+ lensIdsOf,
61
+ mineDigestBackfill,
62
+ mineFates,
63
+ mineFeedback,
64
+ mineHumanRecall,
65
+ minePostMerge,
66
+ mineRecheckFlips,
67
+ mineTaggedComments,
68
+ RECALL_SCAN_LIMITS,
69
+ reviewFateCandidates,
70
+ tableExists,
71
+ type CandidateDraft,
72
+ type CandidateKind,
73
+ type CandidateSource,
74
+ type Polarity,
75
+ type RecallGit,
76
+ } from "./miner.js";
77
+ import {
78
+ addDays,
79
+ dayDiff,
80
+ majorityPolarity,
81
+ packetPointer,
82
+ packetMarkdownRel,
83
+ packetRelDir,
84
+ renderCurationSection,
85
+ renderPacketMarkdown,
86
+ type ExpiredCandidate,
87
+ type OutcomesPacket,
88
+ type PacketCandidate,
89
+ type PacketCluster,
90
+ } from "./packet.js";
91
+ import { candidateLabel, type CurationReceipt } from "./receipt.js";
92
+ import type { CurationStatus, UnresolvedCuration } from "./status.js";
93
+
94
+ export { curationPhrase, type CurationStatus } from "./status.js";
95
+
96
+ export const LEARNER_WAKE = Object.freeze({
97
+ missing: "eod_curation_missing",
98
+ notPersisted: "eod_lessons_not_persisted",
99
+ } as const);
100
+
101
+ export interface LearnerConfig {
102
+ /** Mine this many minutes before the EOD sweep. */
103
+ lessons_mine_lead_min: number;
104
+ /** How far back a mine reads human outcomes. */
105
+ lessons_mine_days: number;
106
+ /** No receipt this long after the sweep was delivered wakes
107
+ * `eod_curation_missing`. */
108
+ lessons_receipt_wait_min: number;
109
+ /** An offered candidate undecided this many ops days expires (recorded). */
110
+ lessons_expiry_ops_days: number;
111
+ /** At most this many candidates are offered for the first time per packet;
112
+ * the rest wait (queued) for a later one. Carried candidates are never
113
+ * capped. */
114
+ lessons_packet_new_max: number;
115
+ /** A bot-approved merged PR's later commits are read for this long. */
116
+ lessons_recall_window_days: number;
117
+ /** The window a cluster's recurrence and counterweight are counted over. */
118
+ lessons_recurrence_days: number;
119
+ /** The digests whose "Learnings" never reached memory (inclusive). */
120
+ lessons_digest_backfill_from: string;
121
+ lessons_digest_backfill_to: string;
122
+ }
123
+
124
+ /** The learner's knobs at their shipped values — the `[memory]` defaults
125
+ * (src/config.ts MEMORY_DEFAULTS), which hold each value once. */
126
+ export const LEARNER_DEFAULTS: LearnerConfig = Object.freeze({
127
+ lessons_mine_lead_min: MEMORY_DEFAULTS.lessons_mine_lead_min,
128
+ lessons_mine_days: MEMORY_DEFAULTS.lessons_mine_days,
129
+ lessons_receipt_wait_min: MEMORY_DEFAULTS.lessons_receipt_wait_min,
130
+ lessons_expiry_ops_days: MEMORY_DEFAULTS.lessons_expiry_ops_days,
131
+ lessons_packet_new_max: MEMORY_DEFAULTS.lessons_packet_new_max,
132
+ lessons_recall_window_days: MEMORY_DEFAULTS.lessons_recall_window_days,
133
+ lessons_recurrence_days: MEMORY_DEFAULTS.lessons_recurrence_days,
134
+ lessons_digest_backfill_from: MEMORY_DEFAULTS.lessons_digest_backfill_from,
135
+ lessons_digest_backfill_to: MEMORY_DEFAULTS.lessons_digest_backfill_to,
136
+ });
137
+
138
+ /** The `[memory] lessons_*` knobs (src/config.ts) as the learner takes them,
139
+ * with floors — a mistyped 0 must not expire every candidate at once or wake
140
+ * the moment the sweep is delivered. One mapping, for wire.ts and the CLI. */
141
+ export function learnerKnobsOf(m: LearnerConfig): Partial<LearnerConfig> {
142
+ return {
143
+ lessons_mine_lead_min: Math.max(0, m.lessons_mine_lead_min),
144
+ lessons_mine_days: Math.max(1, m.lessons_mine_days),
145
+ lessons_receipt_wait_min: Math.max(1, m.lessons_receipt_wait_min),
146
+ lessons_expiry_ops_days: Math.max(1, m.lessons_expiry_ops_days),
147
+ lessons_packet_new_max: Math.max(0, m.lessons_packet_new_max),
148
+ lessons_recall_window_days: Math.max(1, m.lessons_recall_window_days),
149
+ lessons_recurrence_days: Math.max(1, m.lessons_recurrence_days),
150
+ lessons_digest_backfill_from: m.lessons_digest_backfill_from,
151
+ lessons_digest_backfill_to: m.lessons_digest_backfill_to,
152
+ };
153
+ }
154
+
155
+ /** Where the learner reads the lessons in force: the store's sweep (the
156
+ * daemon's OpenViking store). Ids are `<area>/<file>`. */
157
+ export interface LessonSource {
158
+ sweep(): Promise<{
159
+ entries: ReadonlyArray<{ id: string; text: string }>;
160
+ unread: readonly string[];
161
+ }>;
162
+ }
163
+
164
+ export interface LearnerDeps {
165
+ store: StateStore;
166
+ /** The sandbox root: `state/eod/<day>/` is written under it and
167
+ * `digest/<date>.md` read from it. */
168
+ root: string;
169
+ /** The EOD sweep's wall clock (`[eod] time` / `timezone`) — packets are
170
+ * named by its date, and the mine runs `lessons_mine_lead_min` before it. */
171
+ eodTime: string;
172
+ timeZone: string;
173
+ botLogin: string;
174
+ /** `[github] base_branch` — the base of a PR no round recorded one for. */
175
+ baseBranch: string;
176
+ config?: Partial<LearnerConfig>;
177
+ /** Post-merge recall (the base clone). Absent ⇒ that source is skipped. */
178
+ git?: RecallGit;
179
+ /** The lessons in force. Absent (memory switched off) ⇒ coverage and the
180
+ * balance read unknown and no adoption is ever verified. */
181
+ lessons?: LessonSource;
182
+ /** Memory health: verification and coverage read memory only while true. */
183
+ healthy?: () => boolean;
184
+ /** Raise a wake anomaly (wire.ts: `store.logAnomaly(kind, null, null,
185
+ * detail, message)`). */
186
+ wake?: (kind: string, detail: string, message: string) => void;
187
+ now?: () => Date;
188
+ log?: (msg: string, extra?: unknown) => void;
189
+ }
190
+
191
+ const SCHEMA = `
192
+ CREATE TABLE IF NOT EXISTS lesson_candidates (
193
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
194
+ -- the dedup key: one human outcome is one candidate, whichever mine saw it
195
+ ref TEXT NOT NULL UNIQUE,
196
+ source TEXT NOT NULL,
197
+ kind TEXT NOT NULL,
198
+ polarity TEXT NOT NULL,
199
+ class TEXT NOT NULL,
200
+ lens_ids TEXT NOT NULL DEFAULT '[]',
201
+ pr INTEGER,
202
+ round_no INTEGER,
203
+ finding_key TEXT,
204
+ path TEXT,
205
+ line INTEGER,
206
+ summary TEXT NOT NULL,
207
+ quote TEXT,
208
+ evidence TEXT NOT NULL DEFAULT '[]',
209
+ occurred_at TEXT NOT NULL,
210
+ mined_at TEXT NOT NULL,
211
+ -- the ops day it first rode a packet; NULL = queued, never offered
212
+ offered_day TEXT,
213
+ last_offered_day TEXT,
214
+ offers INTEGER NOT NULL DEFAULT 0,
215
+ -- the ops day a refused write or an unfound adoption re-opened it: its
216
+ -- expiry clock restarts there (NULL = it runs from offered_day)
217
+ reopened_day TEXT,
218
+ -- open | adopting (the receipt said adopted; memory not checked yet) |
219
+ -- adopted (a lesson header cites it) | declined | expired |
220
+ -- withdrawn (its human outcome changed since it was mined — never deleted)
221
+ status TEXT NOT NULL DEFAULT 'open',
222
+ decided_at TEXT,
223
+ decided_day TEXT,
224
+ reason TEXT,
225
+ lesson_ids TEXT,
226
+ claimed_lesson TEXT,
227
+ receipt_day TEXT,
228
+ receipt_at TEXT,
229
+ updated_at TEXT NOT NULL
230
+ );
231
+ CREATE INDEX IF NOT EXISTS lesson_candidates_status ON lesson_candidates (status, offered_day);
232
+ CREATE INDEX IF NOT EXISTS lesson_candidates_class ON lesson_candidates (class, occurred_at);
233
+ CREATE TABLE IF NOT EXISTS eod_curations (
234
+ day TEXT PRIMARY KEY,
235
+ packet_at TEXT,
236
+ offered TEXT NOT NULL DEFAULT '[]',
237
+ new_today TEXT NOT NULL DEFAULT '[]',
238
+ expired TEXT NOT NULL DEFAULT '[]',
239
+ fired_at TEXT,
240
+ receipt_at TEXT,
241
+ receipt TEXT,
242
+ missing_woken_at TEXT,
243
+ -- [{id, why}] adoptions memory does not hold and refused writes
244
+ not_persisted TEXT NOT NULL DEFAULT '[]',
245
+ -- when the list last went from empty to non-empty (the incident's start)
246
+ not_persisted_since TEXT,
247
+ -- the not_persisted items (id + why) a wake has already named
248
+ not_persisted_woken TEXT NOT NULL DEFAULT '[]',
249
+ updated_at TEXT NOT NULL
250
+ );
251
+ CREATE TABLE IF NOT EXISTS learner_marks (
252
+ key TEXT PRIMARY KEY,
253
+ value TEXT,
254
+ at TEXT NOT NULL
255
+ );
256
+ CREATE TABLE IF NOT EXISTS learner_recall_scans (
257
+ pr INTEGER PRIMARY KEY,
258
+ merged_at TEXT NOT NULL,
259
+ base_ref TEXT,
260
+ attempts INTEGER NOT NULL DEFAULT 0,
261
+ last_scan_at TEXT,
262
+ merge_commit TEXT,
263
+ touches INTEGER,
264
+ done INTEGER NOT NULL DEFAULT 0,
265
+ outcome TEXT
266
+ );
267
+ `;
268
+
269
+ export type CandidateStatus =
270
+ "open" | "adopting" | "adopted" | "declined" | "expired" | "withdrawn";
271
+
272
+ /** A candidate in one of these is decided: it leaves the not-persisted lists,
273
+ * and a refused write re-opens only an expired one (`applyReceipt`). */
274
+ const SETTLED_STATUSES = ["adopted", "declined", "expired", "withdrawn"] as const;
275
+ const SETTLED_SQL = SETTLED_STATUSES.map((s) => `'${s}'`).join(", ");
276
+
277
+ export interface CandidateRow {
278
+ id: number;
279
+ ref: string;
280
+ source: CandidateSource;
281
+ kind: CandidateKind;
282
+ polarity: Polarity;
283
+ class: string;
284
+ lens_ids: string;
285
+ pr: number | null;
286
+ round_no: number | null;
287
+ finding_key: string | null;
288
+ path: string | null;
289
+ line: number | null;
290
+ summary: string;
291
+ quote: string | null;
292
+ evidence: string;
293
+ occurred_at: string;
294
+ mined_at: string;
295
+ offered_day: string | null;
296
+ last_offered_day: string | null;
297
+ offers: number;
298
+ reopened_day: string | null;
299
+ status: CandidateStatus;
300
+ decided_at: string | null;
301
+ decided_day: string | null;
302
+ reason: string | null;
303
+ lesson_ids: string | null;
304
+ claimed_lesson: string | null;
305
+ receipt_day: string | null;
306
+ receipt_at: string | null;
307
+ updated_at: string;
308
+ }
309
+
310
+ interface CurationRow {
311
+ day: string;
312
+ packet_at: string | null;
313
+ offered: string;
314
+ new_today: string;
315
+ expired: string;
316
+ fired_at: string | null;
317
+ receipt_at: string | null;
318
+ receipt: string | null;
319
+ missing_woken_at: string | null;
320
+ not_persisted: string;
321
+ not_persisted_since: string | null;
322
+ not_persisted_woken: string;
323
+ updated_at: string;
324
+ }
325
+
326
+ export interface MineReport {
327
+ /** New candidate rows. */
328
+ added: number;
329
+ /** Drafts per source (new or already known). */
330
+ seen: Partial<Record<CandidateSource, number>>;
331
+ added_by_source: Partial<Record<CandidateSource, number>>;
332
+ recall: { scanned: number; unknown: number } | null;
333
+ digest: { files: number; absent: number; unreadable: number; added: number } | null;
334
+ /** Undecided fate candidates re-read against the outcome capture as it
335
+ * stands: revised in place, withdrawn (their fate is no longer a
336
+ * candidate's, or their chain's root changed), and withdrawn ones
337
+ * reinstated. */
338
+ fates: { revised: number; withdrawn: number; reinstated: number };
339
+ /** Undecided feedback / tagged-comment candidates whose finding link (and
340
+ * so class, lens and anchor) the outcome capture made or moved since they
341
+ * were minted. */
342
+ relinked: number;
343
+ errors: string[];
344
+ }
345
+
346
+ const DAY_MS = 86_400_000;
347
+ /** A lesson written moments before its receipt may not be listed yet. */
348
+ const VERIFY_GRACE_MS = 10 * 60_000;
349
+ /** How long `prepareEod` waits for a mine it had to start itself. */
350
+ const PREPARE_MINE_BUDGET_MS = 60_000;
351
+ /** How long a lesson read for the packet may take. */
352
+ const LESSON_READ_BUDGET_MS = 20_000;
353
+ /** With no adoption waiting, the lessons in force are re-read this often. */
354
+ const LESSON_REFRESH_MS = 30 * 60_000;
355
+ /** How many curation days back the status looks for an unresolved failure. */
356
+ const UNRESOLVED_SCAN_DAYS = 30;
357
+
358
+ /** `p`, or "timeout" once `ms` passed. A rejection reads as "timeout" too —
359
+ * the caller's fallback is the same — but its cause goes to `onReject`, so a
360
+ * failure is never mistaken for a slow answer in the log. */
361
+ async function withTimeout<T>(
362
+ p: Promise<T>,
363
+ ms: number,
364
+ onReject: (err: unknown) => void,
365
+ ): Promise<T | "timeout"> {
366
+ const result = await withinBudget(
367
+ p.catch((error: unknown) => {
368
+ onReject(error);
369
+ return "timeout" as const;
370
+ }),
371
+ ms,
372
+ );
373
+ return result.kind === "done" ? result.value : "timeout";
374
+ }
375
+
376
+ /** The ops day a candidate's expiry clock runs from: the day it was
377
+ * re-opened, else its first offer. */
378
+ function clockDay(c: Pick<CandidateRow, "offered_day" | "reopened_day">): string {
379
+ return c.reopened_day ?? c.offered_day ?? "";
380
+ }
381
+
382
+ function writeAtomic(path: string, body: string): void {
383
+ const tmp = `${path}.tmp-${process.pid}`;
384
+ writeFileSync(tmp, body);
385
+ renameSync(tmp, path);
386
+ }
387
+
388
+ /** Shared admission for the status incident and the durable wake. */
389
+ function missingCuration(
390
+ row: CurationRow,
391
+ laterReceipt: boolean,
392
+ now: number,
393
+ waitMs: number,
394
+ ): boolean {
395
+ const fired = Date.parse(row.fired_at ?? "");
396
+ return (
397
+ !laterReceipt &&
398
+ row.receipt_at === null &&
399
+ jsonColumn<number[]>(row.offered, []).length > 0 &&
400
+ Number.isFinite(fired) &&
401
+ (row.missing_woken_at !== null || now - fired >= waitMs)
402
+ );
403
+ }
404
+
405
+ export class Learner {
406
+ private readonly store: StateStore;
407
+ private readonly cfg: LearnerConfig;
408
+ private readonly now: () => Date;
409
+ private readonly log: (msg: string, extra?: unknown) => void;
410
+ private mining: Promise<MineReport> | null = null;
411
+ private timer: NodeJS.Timeout | null = null;
412
+ private ticking = false;
413
+ /** The last complete read of the lessons in force. */
414
+ private lessonsRead: { lessons: IndexedLesson[]; complete: boolean; at: string } | null = null;
415
+
416
+ constructor(private readonly deps: LearnerDeps) {
417
+ this.store = deps.store;
418
+ this.cfg = { ...LEARNER_DEFAULTS, ...(deps.config ?? {}) };
419
+ this.now = deps.now ?? (() => new Date());
420
+ this.log = deps.log ?? (() => {});
421
+ this.store.db.exec(SCHEMA);
422
+ }
423
+
424
+ private ts(): string {
425
+ return this.now().toISOString();
426
+ }
427
+
428
+ /** The EOD date `now` falls on, in the EOD timezone. */
429
+ private today(): string {
430
+ return zoneDateString(this.now(), this.deps.timeZone);
431
+ }
432
+
433
+ private mark(key: string): string | null {
434
+ const row = this.store.db.prepare("SELECT value FROM learner_marks WHERE key = ?").get(key) as
435
+ { value: string | null } | undefined;
436
+ return row ? (row.value ?? "") : null;
437
+ }
438
+
439
+ private setMark(key: string, value: string): void {
440
+ this.store.db
441
+ .prepare(
442
+ "INSERT INTO learner_marks (key, value, at) VALUES (?, ?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value, at = excluded.at",
443
+ )
444
+ .run(key, value, this.ts());
445
+ }
446
+
447
+ // ---- 1. mine ---------------------------------------------------------------
448
+
449
+ /** Insert drafts; returns how many were new per source. */
450
+ private insert(drafts: readonly CandidateDraft[]): Partial<Record<CandidateSource, number>> {
451
+ const at = this.ts();
452
+ const stmt = this.store.db.prepare(
453
+ `INSERT OR IGNORE INTO lesson_candidates
454
+ (ref, source, kind, polarity, class, lens_ids, pr, round_no, finding_key, path, line,
455
+ summary, quote, evidence, occurred_at, mined_at, updated_at)
456
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
457
+ );
458
+ const added: Partial<Record<CandidateSource, number>> = {};
459
+ this.store.db.transaction(() => {
460
+ for (const d of drafts) {
461
+ const r = stmt.run(
462
+ d.ref,
463
+ d.source,
464
+ d.kind,
465
+ d.polarity,
466
+ d.class,
467
+ JSON.stringify(d.lens_ids),
468
+ d.pr,
469
+ d.round_no,
470
+ d.finding_key,
471
+ d.path,
472
+ d.line,
473
+ d.summary,
474
+ d.quote,
475
+ JSON.stringify(d.evidence),
476
+ d.occurred_at,
477
+ at,
478
+ at,
479
+ );
480
+ if (r.changes > 0) added[d.source] = (added[d.source] ?? 0) + 1;
481
+ }
482
+ })();
483
+ return added;
484
+ }
485
+
486
+ /**
487
+ * Mine every source once (single-flight: a mine already running is joined,
488
+ * never doubled). A source that throws is reported in `errors`; the others
489
+ * still land.
490
+ */
491
+ mine(): Promise<MineReport> {
492
+ if (this.mining) return this.mining;
493
+ this.mining = this.mineOnce().finally(() => {
494
+ this.mining = null;
495
+ });
496
+ return this.mining;
497
+ }
498
+
499
+ private async mineOnce(): Promise<MineReport> {
500
+ const now = this.now();
501
+ const since = this.mineSince(now);
502
+ const report: MineReport = {
503
+ added: 0,
504
+ seen: {},
505
+ added_by_source: {},
506
+ recall: null,
507
+ digest: null,
508
+ fates: { revised: 0, withdrawn: 0, reinstated: 0 },
509
+ relinked: 0,
510
+ errors: [],
511
+ };
512
+ const drafts: CandidateDraft[] = [];
513
+ const run = (name: string, fn: () => CandidateDraft[]): void => {
514
+ try {
515
+ drafts.push(...fn());
516
+ } catch (err) {
517
+ report.errors.push(`${name}: ${err instanceof Error ? err.message : String(err)}`);
518
+ }
519
+ };
520
+ run("fates", () => mineFates(this.store, since));
521
+ // The ledger before the tagged comments: a [feedback] comment the ledger
522
+ // holds is a `feedback_comment`; only a drained one is a `tagged_comment`.
523
+ run("feedback", () => mineFeedback(this.store, since));
524
+ run("tagged comments", () => mineTaggedComments(this.store, since));
525
+ run("human recall", () => mineHumanRecall(this.store, since, this.deps.botLogin));
526
+ // The recheck's own APPROVE → REQUEST_CHANGES reversals — the reviewer's
527
+ // conclusion, so a candidate only, gated on a standing APPROVE and one per
528
+ // (PR, head) (miner.ts header).
529
+ run("recheck reversals", () => mineRecheckFlips(this.store, since));
530
+ // Post-merge recall stores its candidates as it records each read.
531
+ let recallAdded = 0;
532
+ if (this.deps.git) {
533
+ try {
534
+ const r = await minePostMerge(this.store, {
535
+ ...this.recallScope(now, this.deps.git),
536
+ maxPrs: RECALL_SCAN_LIMITS.prsPerMine,
537
+ persist: (d) => {
538
+ recallAdded += this.insert(d).recall ?? 0;
539
+ },
540
+ });
541
+ drafts.push(...r.drafts);
542
+ report.recall = { scanned: r.scanned, unknown: r.unknown };
543
+ } catch (err) {
544
+ report.errors.push(`post-merge: ${err instanceof Error ? err.message : String(err)}`);
545
+ }
546
+ }
547
+ for (const d of drafts) report.seen[d.source] = (report.seen[d.source] ?? 0) + 1;
548
+ report.added_by_source = this.insert(drafts);
549
+ if (recallAdded > 0)
550
+ report.added_by_source.recall = (report.added_by_source.recall ?? 0) + recallAdded;
551
+ try {
552
+ const oldest = this.store.db
553
+ .prepare(
554
+ "SELECT MIN(occurred_at) AS at FROM lesson_candidates WHERE status='open' AND source IN ('feedback','tagged-comment')",
555
+ )
556
+ .get() as { at: string | null };
557
+ const relinkDrafts =
558
+ oldest.at !== null && oldest.at < since
559
+ ? [...mineFeedback(this.store, oldest.at), ...mineTaggedComments(this.store, oldest.at)]
560
+ : drafts;
561
+ report.relinked = this.relink(relinkDrafts);
562
+ } catch (err) {
563
+ report.errors.push(`relink: ${err instanceof Error ? err.message : String(err)}`);
564
+ }
565
+ try {
566
+ report.fates = this.reviseFates(since);
567
+ } catch (err) {
568
+ report.errors.push(`fate revision: ${err instanceof Error ? err.message : String(err)}`);
569
+ }
570
+ // The lost digest learnings — once, until every digest of the range was
571
+ // read (a file that could not be read keeps it owed).
572
+ const digestKey = `digest-backfill:${this.cfg.lessons_digest_backfill_from}..${this.cfg.lessons_digest_backfill_to}`;
573
+ if (this.mark(digestKey) === null) {
574
+ try {
575
+ const d = mineDigestBackfill({
576
+ dir: join(this.deps.root, "digest"),
577
+ from: this.cfg.lessons_digest_backfill_from,
578
+ to: this.cfg.lessons_digest_backfill_to,
579
+ });
580
+ const added = this.insert(d.drafts)["digest-backfill"] ?? 0;
581
+ report.digest = { files: d.files, absent: d.absent, unreadable: d.unreadable, added };
582
+ report.added_by_source["digest-backfill"] = added;
583
+ report.seen["digest-backfill"] = d.drafts.length;
584
+ if (d.unreadable === 0) this.setMark(digestKey, JSON.stringify(report.digest));
585
+ } catch (err) {
586
+ report.errors.push(`digest backfill: ${err instanceof Error ? err.message : String(err)}`);
587
+ }
588
+ }
589
+ report.added = Object.values(report.added_by_source).reduce((n, v) => n + (v ?? 0), 0);
590
+ this.store.logEvent("lessons_mined", null, report);
591
+ return report;
592
+ }
593
+
594
+ /**
595
+ * An undecided `[feedback]` reply, tagged comment or deleted bot comment
596
+ * takes its class, lens and anchor from the outcome capture's link of the
597
+ * bot comment it answered — a link the reconcile sweep or the case backfill
598
+ * may make only after the candidate was mined, and `INSERT OR IGNORE` never
599
+ * revisits a row, so it would stay `feedback.general` with no lens for good.
600
+ * Every mine re-derives them from today's drafts, as `reviseFates` does for
601
+ * fates. Decided candidates are history. A ref two routes both drafted
602
+ * takes its FIRST draft, as the insert does: the ledger's before the
603
+ * tagged comment's.
604
+ */
605
+ private relink(drafts: readonly CandidateDraft[]): number {
606
+ const stmt = this.store.db.prepare(
607
+ `UPDATE lesson_candidates SET class = ?, lens_ids = ?, round_no = ?, finding_key = ?, path = ?,
608
+ line = ?, summary = ?, evidence = ?, updated_at = ?
609
+ WHERE ref = ? AND status = 'open' AND source IN ('feedback', 'tagged-comment')
610
+ AND (class IS NOT ? OR lens_ids IS NOT ? OR round_no IS NOT ? OR finding_key IS NOT ?
611
+ OR path IS NOT ? OR line IS NOT ? OR summary IS NOT ?)`,
612
+ );
613
+ const at = this.ts();
614
+ let n = 0;
615
+ const seen = new Set<string>();
616
+ this.store.db.transaction(() => {
617
+ for (const d of drafts) {
618
+ if (d.source !== "feedback" && d.source !== "tagged-comment") continue;
619
+ if (seen.has(d.ref)) continue;
620
+ seen.add(d.ref);
621
+ const lens = JSON.stringify(d.lens_ids);
622
+ n += stmt.run(
623
+ d.class,
624
+ lens,
625
+ d.round_no,
626
+ d.finding_key,
627
+ d.path,
628
+ d.line,
629
+ d.summary,
630
+ JSON.stringify(d.evidence),
631
+ at,
632
+ d.ref,
633
+ d.class,
634
+ lens,
635
+ d.round_no,
636
+ d.finding_key,
637
+ d.path,
638
+ d.line,
639
+ d.summary,
640
+ ).changes;
641
+ }
642
+ })();
643
+ return n;
644
+ }
645
+
646
+ /** The oldest event a mine reads: `lessons_mine_days` before `now`. */
647
+ private mineSince(now: Date): string {
648
+ return new Date(now.getTime() - this.cfg.lessons_mine_days * DAY_MS).toISOString();
649
+ }
650
+
651
+ /** The start of the recurrence window a cluster counts over. */
652
+ private recurrenceSince(): string {
653
+ return new Date(this.now().getTime() - this.cfg.lessons_recurrence_days * DAY_MS).toISOString();
654
+ }
655
+
656
+ /** What post-merge recall reads — the same for the day's mine and the
657
+ * ticks between mines, which differ only in their budgets. */
658
+ private recallScope(now: Date, git: RecallGit) {
659
+ return {
660
+ sinceIso: this.mineSince(now),
661
+ now,
662
+ windowDays: this.cfg.lessons_recall_window_days,
663
+ baseBranch: this.deps.baseBranch,
664
+ git,
665
+ log: this.log,
666
+ };
667
+ }
668
+
669
+ /**
670
+ * Post-merge recall between mines (`RECALL_SCAN_LIMITS.prsPerTick` PRs and
671
+ * `budgetMs` a tick): the day's mine alone reads too few merged PRs to keep
672
+ * up with the merge rate or drain a backlog. Git only, read-only; what it
673
+ * finds is inserted at once and offered by the next packet. Never throws.
674
+ */
675
+ private async recallStep(): Promise<void> {
676
+ const git = this.deps.git;
677
+ if (!git) return;
678
+ let added = 0;
679
+ try {
680
+ const r = await minePostMerge(this.store, {
681
+ ...this.recallScope(this.now(), git),
682
+ maxPrs: RECALL_SCAN_LIMITS.prsPerTick,
683
+ budgetMs: RECALL_SCAN_LIMITS.budgetMs,
684
+ persist: (d) => {
685
+ added += this.insert(d).recall ?? 0;
686
+ },
687
+ });
688
+ if (r.scanned > 0 || added > 0)
689
+ this.log(
690
+ `learner: post-merge recall read ${r.scanned} merged PR(s), ${added} new candidate(s)${r.unknown ? `, ${r.unknown} unknown` : ""}`,
691
+ );
692
+ } catch (err) {
693
+ this.log("learner: post-merge recall failed", err);
694
+ }
695
+ }
696
+
697
+ /**
698
+ * Re-read the undecided fate candidates against the outcome capture's
699
+ * outcomes as they stand (`reviewFateCandidates`): the capture re-decides a
700
+ * fate on every pass and may join a posting to an earlier chain later, so a
701
+ * candidate minted once is never trusted as written. One whose chain still
702
+ * holds a candidate fate is revised in place (its kind, polarity and summary
703
+ * follow the fate); one that no longer does is withdrawn — recorded on the
704
+ * row, in an event and in the mine's report, never deleted; and a withdrawn
705
+ * one whose chain holds a candidate fate again is reinstated (its ref is
706
+ * the same, so the mine's insert could not bring it back). Queued and
707
+ * offered candidates alike; a
708
+ * decided one is history and is left as it is.
709
+ */
710
+ private reviseFates(sinceIso: string): MineReport["fates"] {
711
+ const db = this.store.db;
712
+ const hasOutcomes = tableExists(this.store, "finding_outcomes");
713
+ const freshOutcome = hasOutcomes
714
+ ? "OR pr IN (SELECT pr FROM finding_outcomes WHERE COALESCE(decided_at,fate_changed_at)>=?)"
715
+ : "";
716
+ const rows = this.candidates(
717
+ `source = 'fate' AND (status='open' OR (status='withdrawn' AND (decided_at>=? ${freshOutcome}))) ORDER BY id`,
718
+ sinceIso,
719
+ ...(hasOutcomes ? [sinceIso] : []),
720
+ );
721
+ const out = { revised: 0, withdrawn: 0, reinstated: 0 };
722
+ if (rows.length === 0) return out;
723
+ const reviews = reviewFateCandidates(this.store, rows);
724
+ const at = this.ts();
725
+ const day = this.today();
726
+ const changes: Array<{ id: number; change: string; why?: string }> = [];
727
+ const refresh = (c: CandidateRow, d: CandidateDraft) => [
728
+ d.kind,
729
+ d.polarity,
730
+ d.class,
731
+ JSON.stringify(d.lens_ids),
732
+ d.summary,
733
+ d.quote,
734
+ JSON.stringify(d.evidence),
735
+ d.path,
736
+ d.line,
737
+ // The event's time moves only with its fate: a recurrence window
738
+ // counts the human outcome, not the capture's re-reading of it.
739
+ d.kind !== c.kind ? d.occurred_at : c.occurred_at,
740
+ ];
741
+ db.transaction(() => {
742
+ for (const c of rows) {
743
+ const r = reviews.get(c.id);
744
+ if (!r) continue;
745
+ if (r.kind === "gone") {
746
+ if (c.status !== "open") continue;
747
+ db.prepare(
748
+ `UPDATE lesson_candidates SET status = 'withdrawn', decided_at = ?, decided_day = ?, reason = ?, updated_at = ?
749
+ WHERE id = ? AND status = 'open'`,
750
+ ).run(at, day, `withdrawn: ${r.why}`, at, c.id);
751
+ out.withdrawn += 1;
752
+ changes.push({ id: c.id, change: "withdrawn", why: r.why });
753
+ continue;
754
+ }
755
+ const d = r.draft;
756
+ if (c.status === "withdrawn") {
757
+ db.prepare(
758
+ `UPDATE lesson_candidates SET status = 'open', decided_at = NULL, decided_day = NULL, reason = ?,
759
+ reopened_day = CASE WHEN offered_day IS NULL THEN NULL ELSE ? END,
760
+ kind = ?, polarity = ?, class = ?, lens_ids = ?, summary = ?, quote = ?, evidence = ?,
761
+ path = ?, line = ?, occurred_at = ?, updated_at = ?
762
+ WHERE id = ? AND status = 'withdrawn'`,
763
+ ).run(`reinstated: its fate is ${d.kind} again`, day, ...refresh(c, d), at, c.id);
764
+ out.reinstated += 1;
765
+ changes.push({ id: c.id, change: "reinstated" });
766
+ continue;
767
+ }
768
+ if (
769
+ d.kind === c.kind &&
770
+ d.polarity === c.polarity &&
771
+ d.class === c.class &&
772
+ JSON.stringify(d.lens_ids) === c.lens_ids &&
773
+ d.summary === c.summary &&
774
+ d.quote === c.quote &&
775
+ d.path === c.path &&
776
+ d.line === c.line
777
+ )
778
+ continue;
779
+ db.prepare(
780
+ `UPDATE lesson_candidates SET kind = ?, polarity = ?, class = ?, lens_ids = ?, summary = ?, quote = ?,
781
+ evidence = ?, path = ?, line = ?, occurred_at = ?, updated_at = ?
782
+ WHERE id = ? AND status = 'open'`,
783
+ ).run(...refresh(c, d), at, c.id);
784
+ out.revised += 1;
785
+ changes.push({ id: c.id, change: d.kind !== c.kind ? `${c.kind} → ${d.kind}` : "revised" });
786
+ }
787
+ })();
788
+ if (changes.length > 0) this.store.logEvent("lesson_candidates_revised", null, { changes });
789
+ return out;
790
+ }
791
+
792
+ // ---- 2. the packet ---------------------------------------------------------
793
+
794
+ private candidates(where: string, ...args: unknown[]): CandidateRow[] {
795
+ return this.store.db
796
+ .prepare(`SELECT * FROM lesson_candidates WHERE ${where}`)
797
+ .all(...args) as CandidateRow[];
798
+ }
799
+
800
+ /** Read the lessons in force (experiences/), bounded; null when memory is
801
+ * off, unhealthy or does not answer in time. Remembers a complete read. */
802
+ private async readLessons(): Promise<{ lessons: IndexedLesson[]; complete: boolean } | null> {
803
+ if (!this.deps.lessons) return null;
804
+ if (this.deps.healthy && !this.deps.healthy()) return null;
805
+ const res = await withTimeout(this.deps.lessons.sweep(), LESSON_READ_BUDGET_MS, (err) =>
806
+ this.log("learner: the lessons read failed", err),
807
+ );
808
+ if (res === "timeout") return null;
809
+ const lessons: IndexedLesson[] = [];
810
+ for (const e of res.entries) {
811
+ if (areaOfId(e.id) !== EXPERIENCES_AREA) continue;
812
+ const p = parseLesson(e.text);
813
+ if (p) lessons.push({ ...p, id: e.id });
814
+ }
815
+ // Newest first (ids carry their stamp), so a coverage list leads with the
816
+ // freshest lesson.
817
+ lessons.sort((a, b) => (a.id < b.id ? 1 : a.id > b.id ? -1 : 0));
818
+ const complete = res.unread.length === 0;
819
+ if (complete) this.lessonsRead = { lessons, complete, at: this.ts() };
820
+ return { lessons, complete };
821
+ }
822
+
823
+ /** Per class, the candidates whose event fell in the window, whatever
824
+ * became of them — offered, queued, decided or expired — except the
825
+ * withdrawn (minted from an outcome that has since changed). */
826
+ private recurrence(sinceIso: string): Map<string, number> {
827
+ return new Map(
828
+ (
829
+ this.store.db
830
+ .prepare(
831
+ "SELECT class, COUNT(*) AS n FROM lesson_candidates WHERE occurred_at >= ? AND status <> 'withdrawn' GROUP BY class",
832
+ )
833
+ .all(sinceIso) as Array<{ class: string; n: number }>
834
+ ).map((r) => [r.class, r.n]),
835
+ );
836
+ }
837
+
838
+ /** Per class, the posted findings humans fixed and left open in the
839
+ * window — the counterweight a cluster shows. */
840
+ private counterweights(sinceIso: string): Map<string, { fixed: number; ignored: number }> {
841
+ const out = new Map<string, { fixed: number; ignored: number }>();
842
+ if (!tableExists(this.store, "finding_outcomes")) return out;
843
+ const prs = this.store.db
844
+ .prepare(
845
+ "SELECT DISTINCT pr FROM finding_outcomes WHERE fate IN ('fixed','open_at_close') AND COALESCE(decided_at,fate_changed_at)>=?",
846
+ )
847
+ .all(sinceIso) as Array<{ pr: number }>;
848
+ const finding = this.store.db.prepare(
849
+ "SELECT lens_ids,lens,kind FROM round_findings WHERE pr=? AND round_no=? AND finding_key=?",
850
+ );
851
+ for (const { pr } of prs)
852
+ for (const { root, end } of chainRoots(outcomeRows(this.store, pr))) {
853
+ if (
854
+ !["fixed", "open_at_close"].includes(end.fate) ||
855
+ (end.decided_at ?? end.fate_changed_at) < sinceIso
856
+ )
857
+ continue;
858
+ const f = finding.get(pr, root.round_no, root.finding_key) as
859
+ { lens_ids: string | null; lens: string; kind: string | null } | undefined;
860
+ if (!f) continue;
861
+ const cls = findingClass(lensIdsOf(f.lens_ids, f.lens), f.kind);
862
+ const count = out.get(cls) ?? { fixed: 0, ignored: 0 };
863
+ if (end.fate === "fixed") count.fixed++;
864
+ else count.ignored++;
865
+ out.set(cls, count);
866
+ }
867
+ return out;
868
+ }
869
+
870
+ /**
871
+ * Build and write the packet for EOD day `day`: expire, offer, carry,
872
+ * cluster. Idempotent per day — a second build (a retried fire) offers no
873
+ * more than the day's cap and expires nothing twice.
874
+ */
875
+ async buildPacket(day: string): Promise<OutcomesPacket> {
876
+ const at = this.ts();
877
+ const db = this.store.db;
878
+ // Expire: offered, undecided, past the window — counted from its first
879
+ // offer, or from the day a refused write or an unfound adoption re-opened
880
+ // it (that candidate was promised the next packet, and gets a full window).
881
+ const expired: ExpiredCandidate[] = [];
882
+ db.transaction(() => {
883
+ for (const c of this.candidates("status = 'open' AND offered_day IS NOT NULL")) {
884
+ if (dayDiff(clockDay(c), day) < this.cfg.lessons_expiry_ops_days) continue;
885
+ db.prepare(
886
+ `UPDATE lesson_candidates SET status = 'expired', decided_at = ?, decided_day = ?, reason = ?, updated_at = ?
887
+ WHERE id = ? AND status = 'open'`,
888
+ ).run(
889
+ at,
890
+ day,
891
+ c.reopened_day
892
+ ? `undecided ${this.cfg.lessons_expiry_ops_days} ops days after it was re-opened (${c.reopened_day}; first offered ${c.offered_day})`
893
+ : `undecided ${this.cfg.lessons_expiry_ops_days} ops days after it was first offered (${c.offered_day})`,
894
+ at,
895
+ c.id,
896
+ );
897
+ expired.push({
898
+ id: c.id,
899
+ label: candidateLabel(c.id),
900
+ class: c.class,
901
+ summary: c.summary,
902
+ offered_day: c.offered_day!,
903
+ });
904
+ }
905
+ if (expired.length > 0)
906
+ this.store.logEvent("lesson_candidates_expired", null, {
907
+ day,
908
+ ids: expired.map((e) => e.id),
909
+ after_ops_days: this.cfg.lessons_expiry_ops_days,
910
+ });
911
+ })();
912
+ // Candidates expired by an earlier build of the same day stay listed —
913
+ // the ones its record lists, and the ones its expiry committed before
914
+ // that build was cut short (the rows say so: expired on this day).
915
+ const prior = this.curation(day);
916
+ const priorExpired = prior ? jsonColumn<number[]>(prior.expired, []) : [];
917
+ const expiredOnDay = this.candidates("status = 'expired' AND decided_day = ? ORDER BY id", day);
918
+ for (const id of [...priorExpired, ...expiredOnDay.map((c) => c.id)]) {
919
+ if (expired.some((e) => e.id === id)) continue;
920
+ const c = this.candidates("id = ?", id)[0];
921
+ if (c)
922
+ expired.push({
923
+ id: c.id,
924
+ label: candidateLabel(c.id),
925
+ class: c.class,
926
+ summary: c.summary,
927
+ offered_day: c.offered_day ?? day,
928
+ });
929
+ }
930
+ // Offer: queued candidates, look-for first, up to the day's cap — the
931
+ // reviewer's own recheck reversals after every other source, so its own
932
+ // conclusions never crowd a person's outcome out of the packet.
933
+ const offeredToday = (
934
+ db.prepare("SELECT COUNT(*) AS n FROM lesson_candidates WHERE offered_day = ?").get(day) as {
935
+ n: number;
936
+ }
937
+ ).n;
938
+ const slots = Math.max(0, this.cfg.lessons_packet_new_max - offeredToday);
939
+ if (slots > 0) {
940
+ const fresh = db
941
+ .prepare(
942
+ `SELECT id FROM lesson_candidates WHERE status = 'open' AND offered_day IS NULL
943
+ ORDER BY CASE source WHEN 'recheck' THEN 1 ELSE 0 END,
944
+ CASE polarity WHEN 'look-for' THEN 0 ELSE 1 END,
945
+ CASE source WHEN 'recall' THEN 0 WHEN 'fate' THEN 1 WHEN 'feedback' THEN 2
946
+ WHEN 'tagged-comment' THEN 2 ELSE 3 END,
947
+ occurred_at DESC, id
948
+ LIMIT ?`,
949
+ )
950
+ .all(slots) as Array<{ id: number }>;
951
+ const offer = db.prepare(
952
+ "UPDATE lesson_candidates SET offered_day = ?, updated_at = ? WHERE id = ? AND offered_day IS NULL",
953
+ );
954
+ db.transaction(() => {
955
+ for (const f of fresh) offer.run(day, at, f.id);
956
+ })();
957
+ }
958
+ // Carry: every offered candidate still undecided rides today's packet.
959
+ db.prepare(
960
+ `UPDATE lesson_candidates SET offers = offers + 1, last_offered_day = ?, updated_at = ?
961
+ WHERE status = 'open' AND offered_day IS NOT NULL AND (last_offered_day IS NULL OR last_offered_day <> ?)`,
962
+ ).run(day, at, day);
963
+ const offered = this.candidates(
964
+ "status = 'open' AND offered_day IS NOT NULL ORDER BY class, occurred_at DESC, id",
965
+ );
966
+ const queued = (
967
+ db
968
+ .prepare(
969
+ "SELECT COUNT(*) AS n FROM lesson_candidates WHERE status = 'open' AND offered_day IS NULL",
970
+ )
971
+ .get() as { n: number }
972
+ ).n;
973
+
974
+ // Cluster, with recurrence, the counterweight and coverage.
975
+ const since = this.recurrenceSince();
976
+ const recurrence = this.recurrence(since);
977
+ const weights = this.counterweights(since);
978
+ const read = await this.readLessons();
979
+ const byClass = new Map<string, CandidateRow[]>();
980
+ for (const c of offered) byClass.set(c.class, [...(byClass.get(c.class) ?? []), c]);
981
+ const toPacket = (c: CandidateRow): PacketCandidate => ({
982
+ id: c.id,
983
+ label: candidateLabel(c.id),
984
+ kind: c.kind,
985
+ source: c.source,
986
+ polarity: c.polarity,
987
+ class: c.class,
988
+ lens_ids: jsonColumn<string[]>(c.lens_ids, []),
989
+ pr: c.pr,
990
+ path: c.path,
991
+ line: c.line,
992
+ summary: c.summary,
993
+ quote: c.quote,
994
+ evidence: jsonColumn<string[]>(c.evidence, []),
995
+ occurred_at: c.occurred_at,
996
+ offered_day: c.offered_day!,
997
+ expires_after: addDays(clockDay(c), this.cfg.lessons_expiry_ops_days - 1),
998
+ new: c.offered_day === day,
999
+ });
1000
+ // A partial read proves nothing absent: the lessons it found do cover the
1001
+ // class, but "none found" is unknown, and a balance over part of memory
1002
+ // is no balance at all.
1003
+ const coverage = (cls: string, lensIds: readonly string[]): string[] | null => {
1004
+ if (!read) return null;
1005
+ const found = coveringLessons(read.lessons, cls, lensIds);
1006
+ return read.complete || found.length > 0 ? found : null;
1007
+ };
1008
+ const clusters: PacketCluster[] = [...byClass.entries()].map(([cls, rows]) => {
1009
+ const lensIds = [...new Set(rows.flatMap((r) => jsonColumn<string[]>(r.lens_ids, [])))];
1010
+ const w = weights.get(cls) ?? { fixed: 0, ignored: 0 };
1011
+ return {
1012
+ class: cls,
1013
+ polarity: majorityPolarity(rows),
1014
+ recurrence: recurrence.get(cls) ?? 0,
1015
+ fixed: w.fixed,
1016
+ ignored: w.ignored,
1017
+ covering: coverage(cls, lensIds),
1018
+ candidates: rows.map(toPacket),
1019
+ };
1020
+ });
1021
+ // The reviewer's own recheck reversals after every other cluster —
1022
+ // the same demotion as the first offer, because a busy reversal class
1023
+ // would otherwise lead the packet on recurrence alone; then misses first,
1024
+ // then the busiest classes.
1025
+ const reviewerOnly = (c: PacketCluster): boolean =>
1026
+ c.candidates.every((x) => x.source === "recheck");
1027
+ clusters.sort(
1028
+ (a, b) =>
1029
+ Number(reviewerOnly(a)) - Number(reviewerOnly(b)) ||
1030
+ Number(b.polarity === "look-for") - Number(a.polarity === "look-for") ||
1031
+ b.recurrence - a.recurrence ||
1032
+ a.class.localeCompare(b.class),
1033
+ );
1034
+ const fresh = offered.filter((c) => c.offered_day === day).length;
1035
+ const packet: OutcomesPacket = {
1036
+ day,
1037
+ generated_at: at,
1038
+ expiry_ops_days: this.cfg.lessons_expiry_ops_days,
1039
+ recurrence_days: this.cfg.lessons_recurrence_days,
1040
+ offered: offered.length,
1041
+ new: fresh,
1042
+ carried: offered.length - fresh,
1043
+ queued,
1044
+ expired,
1045
+ balance: read?.complete ? lessonBalance(read.lessons) : null,
1046
+ lessons_read: !this.deps.lessons
1047
+ ? "off"
1048
+ : read === null
1049
+ ? "unreadable"
1050
+ : read.complete
1051
+ ? "ok"
1052
+ : "partial",
1053
+ clusters,
1054
+ };
1055
+ const dir = join(this.deps.root, packetRelDir(day));
1056
+ mkdirSync(dir, { recursive: true });
1057
+ writeAtomic(join(dir, "outcomes.json"), `${JSON.stringify(packet, null, 2)}\n`);
1058
+ writeAtomic(join(this.deps.root, packetMarkdownRel(day)), renderPacketMarkdown(packet));
1059
+ db.prepare(
1060
+ `INSERT INTO eod_curations (day, packet_at, offered, new_today, expired, updated_at)
1061
+ VALUES (?, ?, ?, ?, ?, ?)
1062
+ ON CONFLICT(day) DO UPDATE SET packet_at = excluded.packet_at, offered = excluded.offered,
1063
+ new_today = excluded.new_today, expired = excluded.expired, updated_at = excluded.updated_at`,
1064
+ ).run(
1065
+ day,
1066
+ at,
1067
+ JSON.stringify(offered.map((c) => c.id)),
1068
+ JSON.stringify(offered.filter((c) => c.offered_day === day).map((c) => c.id)),
1069
+ JSON.stringify(expired.map((e) => e.id)),
1070
+ at,
1071
+ );
1072
+ this.store.logEvent("eod_packet_written", null, {
1073
+ day,
1074
+ offered: packet.offered,
1075
+ new: packet.new,
1076
+ carried: packet.carried,
1077
+ queued,
1078
+ expired: expired.length,
1079
+ lessons_read: packet.lessons_read,
1080
+ });
1081
+ return packet;
1082
+ }
1083
+
1084
+ /**
1085
+ * The EOD sweep's day context (wire.ts → `dayContext`): mine first when the
1086
+ * day's mine has not run (bounded — a slow git read never holds the sweep),
1087
+ * then build the packet. `lines` is the pointer the digest carries;
1088
+ * `curation` the instructions the prompt renders after the seed.
1089
+ */
1090
+ async prepareEod(
1091
+ day: string,
1092
+ ): Promise<{ lines: string; curation: string; packet: OutcomesPacket }> {
1093
+ if (this.mark(`mined:${day}`) === null) {
1094
+ const r = await withTimeout(this.mine(), PREPARE_MINE_BUDGET_MS, (err) =>
1095
+ this.log("learner: the EOD mine failed", err),
1096
+ );
1097
+ if (r !== "timeout") this.setMark(`mined:${day}`, this.ts());
1098
+ }
1099
+ const packet = await this.buildPacket(day);
1100
+ return { lines: packetPointer(packet), curation: renderCurationSection(packet), packet };
1101
+ }
1102
+
1103
+ /** The sweep for `day` reached the overseer (`EodTimerDeps.onFire`): the
1104
+ * receipt is owed from now. */
1105
+ eodFired(day: string): void {
1106
+ const at = this.ts();
1107
+ this.store.db
1108
+ .prepare(
1109
+ `INSERT INTO eod_curations (day, fired_at, updated_at) VALUES (?, ?, ?)
1110
+ ON CONFLICT(day) DO UPDATE SET fired_at = excluded.fired_at, updated_at = excluded.updated_at`,
1111
+ )
1112
+ .run(day, at, at);
1113
+ }
1114
+
1115
+ private curation(day: string): CurationRow | undefined {
1116
+ return this.store.db.prepare("SELECT * FROM eod_curations WHERE day = ?").get(day) as
1117
+ CurationRow | undefined;
1118
+ }
1119
+
1120
+ // ---- 3. the receipt -----------------------------------------------------------
1121
+
1122
+ /**
1123
+ * Apply a validated `eod-curation-receipt`. All or nothing: a day with no
1124
+ * packet, a candidate id the learner never minted, or one that day's packet
1125
+ * did not offer, refuses the whole receipt (the curator fixes it and writes
1126
+ * it again) — a receipt decides what its packet put in front of the
1127
+ * curator, never a queued candidate nobody showed it. `adopted` waits for
1128
+ * memory to show the lesson (`adopting`);
1129
+ * `declined` is final; `refused` keeps the candidate open and wakes
1130
+ * `eod_lessons_not_persisted` at once. A candidate already settled since
1131
+ * the packet — adopted (memory showed its lesson), withdrawn or expired —
1132
+ * keeps its status whatever the receipt says: memory's evidence and the
1133
+ * learner's own record outrank a later claim (only `refused` re-opens an
1134
+ * expired one, with a fresh clock).
1135
+ */
1136
+ applyReceipt(receipt: CurationReceipt): FileOutcome {
1137
+ const row = this.curation(receipt.day);
1138
+ if (!row?.packet_at)
1139
+ return { ok: false, reason: `no outcomes packet was written for ${receipt.day}` };
1140
+ const ids = receipt.decisions.flatMap((d) => d.candidates);
1141
+ const offered = new Set(jsonColumn<number[]>(row.offered, []));
1142
+ const known = new Set(
1143
+ ids.length === 0
1144
+ ? []
1145
+ : (
1146
+ this.store.db
1147
+ .prepare(
1148
+ `SELECT id FROM lesson_candidates WHERE id IN (${ids.map(() => "?").join(",")})`,
1149
+ )
1150
+ .all(...ids) as Array<{ id: number }>
1151
+ ).map((r) => r.id),
1152
+ );
1153
+ const unknown = ids.filter((id) => !known.has(id));
1154
+ if (unknown.length > 0)
1155
+ return {
1156
+ ok: false,
1157
+ reason: `unknown candidate id(s): ${unknown.map(candidateLabel).join(", ")} — nothing was recorded; send the receipt again without them`,
1158
+ };
1159
+ const notOffered = ids.filter((id) => !offered.has(id));
1160
+ if (notOffered.length > 0)
1161
+ return {
1162
+ ok: false,
1163
+ reason: `candidate id(s) the ${receipt.day} packet did not offer: ${notOffered.map(candidateLabel).join(", ")} — nothing was recorded; decide only what that packet offered`,
1164
+ };
1165
+ const at = this.ts();
1166
+ const today = this.today();
1167
+ const db = this.store.db;
1168
+ const counts = { adopted: 0, declined: 0, refused: 0 };
1169
+ const refused: Array<{ id: number; why: string }> = [];
1170
+ db.transaction(() => {
1171
+ for (const d of receipt.decisions) {
1172
+ for (const id of d.candidates) {
1173
+ if (d.decision === "adopted") {
1174
+ // An adoption memory already showed stands; an undecided one
1175
+ // waits for the lesson to be found; a settled one keeps its
1176
+ // decision and the reason for it — only the receipt is recorded.
1177
+ db.prepare(
1178
+ `UPDATE lesson_candidates SET status = CASE WHEN status IN ('open', 'adopting') THEN 'adopting' ELSE status END,
1179
+ claimed_lesson = CASE WHEN status IN ('open', 'adopting') THEN ? ELSE claimed_lesson END,
1180
+ reason = CASE WHEN status IN ('open', 'adopting') THEN COALESCE(?, reason) ELSE reason END,
1181
+ receipt_day = ?, receipt_at = ?, updated_at = ?
1182
+ WHERE id = ?`,
1183
+ ).run(d.lesson ?? null, d.reason ?? null, receipt.day, at, at, id);
1184
+ counts.adopted += 1;
1185
+ } else if (d.decision === "declined") {
1186
+ // Only an undecided candidate is declined: a declination never
1187
+ // overwrites a memory-verified adoption, a withdrawal or an expiry.
1188
+ db.prepare(
1189
+ `UPDATE lesson_candidates SET status = 'declined', decided_at = ?, decided_day = ?, reason = ?,
1190
+ receipt_day = ?, receipt_at = ?, updated_at = ? WHERE id = ? AND status IN ('open', 'adopting')`,
1191
+ ).run(at, receipt.day, d.reason ?? null, receipt.day, at, at, id);
1192
+ db.prepare(
1193
+ "UPDATE lesson_candidates SET receipt_day = ?, receipt_at = ?, updated_at = ? WHERE id = ? AND status NOT IN ('open', 'adopting')",
1194
+ ).run(receipt.day, at, at, id);
1195
+ counts.declined += 1;
1196
+ } else {
1197
+ // A refused write puts the candidate back in the packet with a
1198
+ // fresh expiry clock (it was promised the next packet); one
1199
+ // already adopted, declined or withdrawn stays as it is.
1200
+ const before = (
1201
+ db.prepare("SELECT status FROM lesson_candidates WHERE id = ?").get(id) as {
1202
+ status: CandidateStatus;
1203
+ }
1204
+ ).status;
1205
+ const keep =
1206
+ (SETTLED_STATUSES as readonly string[]).includes(before) && before !== "expired";
1207
+ if (keep)
1208
+ db.prepare(
1209
+ "UPDATE lesson_candidates SET receipt_day = ?, receipt_at = ?, updated_at = ? WHERE id = ?",
1210
+ ).run(receipt.day, at, at, id);
1211
+ else
1212
+ db.prepare(
1213
+ `UPDATE lesson_candidates SET status = 'open', reopened_day = ?, decided_at = NULL, decided_day = NULL,
1214
+ reason = ?, receipt_day = ?, receipt_at = ?, updated_at = ? WHERE id = ?`,
1215
+ ).run(today, `write refused: ${d.reason ?? ""}`, receipt.day, at, at, id);
1216
+ counts.refused += 1;
1217
+ if (!keep)
1218
+ refused.push({
1219
+ id,
1220
+ why: `the curator reported the write refused: ${d.reason ?? "no reason"}`,
1221
+ });
1222
+ }
1223
+ }
1224
+ }
1225
+ db.prepare(
1226
+ "UPDATE eod_curations SET receipt_at = ?, receipt = ?, updated_at = ? WHERE day = ?",
1227
+ ).run(
1228
+ at,
1229
+ JSON.stringify({ ...counts, decisions: receipt.decisions.length }),
1230
+ at,
1231
+ receipt.day,
1232
+ );
1233
+ this.store.logEvent("eod_curation_receipt", null, { day: receipt.day, ...counts });
1234
+ if (refused.length > 0) this.notPersisted(receipt.day, refused);
1235
+ })();
1236
+ return { ok: true, day: receipt.day, ...counts };
1237
+ }
1238
+
1239
+ /** Record adoptions memory does not hold / writes refused, and wake when
1240
+ * the list names something no earlier wake named. */
1241
+ private notPersisted(day: string, items: Array<{ id: number; why: string }>): void {
1242
+ this.store.db.transaction(() => {
1243
+ const row = this.curation(day);
1244
+ const prev = jsonColumn<Array<{ id: number; why: string }>>(row?.not_persisted, []);
1245
+ const merged = [...prev];
1246
+ for (const i of items)
1247
+ if (!merged.some((m) => m.id === i.id && m.why === i.why)) merged.push(i);
1248
+ const woken = new Set(jsonColumn<string[]>(row?.not_persisted_woken, []));
1249
+ const key = (m: { id: number; why: string }) => `${m.id}:${m.why}`;
1250
+ const fresh = merged.filter((m) => !woken.has(key(m)));
1251
+ const at = this.ts();
1252
+ // The incident starts when the day's list goes from empty to non-empty.
1253
+ const since = prev.length > 0 && row?.not_persisted_since ? row.not_persisted_since : at;
1254
+ this.store.db
1255
+ .prepare(
1256
+ `INSERT INTO eod_curations (day, not_persisted, not_persisted_since, not_persisted_woken, updated_at)
1257
+ VALUES (?, ?, ?, ?, ?)
1258
+ ON CONFLICT(day) DO UPDATE SET not_persisted = excluded.not_persisted,
1259
+ not_persisted_since = excluded.not_persisted_since,
1260
+ not_persisted_woken = excluded.not_persisted_woken, updated_at = excluded.updated_at`,
1261
+ )
1262
+ .run(day, JSON.stringify(merged), since, JSON.stringify([...woken, ...fresh.map(key)]), at);
1263
+ if (fresh.length === 0) return;
1264
+ const labels = merged.map((m) => candidateLabel(m.id)).join(", ");
1265
+ this.deps.wake?.(
1266
+ LEARNER_WAKE.notPersisted,
1267
+ `EOD ${day}: ${merged.length} lesson(s) not persisted (${labels})`,
1268
+ [
1269
+ `EOD LESSONS NOT PERSISTED — the curation of ${day} did not land ${merged.length} lesson(s) in reviewer memory: ${labels}.`,
1270
+ ...merged.slice(0, 8).map((m) => `- ${candidateLabel(m.id)}: ${m.why.slice(0, 200)}`),
1271
+ "The candidates are open again and ride the next outcomes packet. Check memory health (`qcr status`) and the overseer's viking_write answers; a lesson counts only once its typed header cites the candidate (`cand:c<id>`).",
1272
+ ].join("\n"),
1273
+ );
1274
+ })();
1275
+ }
1276
+
1277
+ // ---- 4. the tick: mine on time, verify, and wake when the receipt is late ----
1278
+
1279
+ /** When today's mine is due: `lessons_mine_lead_min` before the EOD time. */
1280
+ private mineDueAt(): number {
1281
+ const p = zonedParts(this.now(), this.deps.timeZone);
1282
+ const { hour, minute } = parseHHMM(this.deps.eodTime);
1283
+ const fire = localWallClockToInstant(p.year, p.month, p.day, hour, minute, this.deps.timeZone);
1284
+ return fire.getTime() - this.cfg.lessons_mine_lead_min * 60_000;
1285
+ }
1286
+
1287
+ /** One pass: the day's mine when due, the missing-receipt check, and the
1288
+ * verification of adoptions against memory. Never throws. */
1289
+ async tick(): Promise<void> {
1290
+ if (this.ticking) return;
1291
+ this.ticking = true;
1292
+ try {
1293
+ const day = this.today();
1294
+ if (this.now().getTime() >= this.mineDueAt() && this.mark(`mined:${day}`) === null) {
1295
+ try {
1296
+ const r = await this.mine();
1297
+ this.setMark(`mined:${day}`, this.ts());
1298
+ this.log(
1299
+ `learner: mined ${r.added} new lesson candidate(s) for ${day}${r.errors.length ? ` (${r.errors.length} source error(s))` : ""}`,
1300
+ );
1301
+ } catch (err) {
1302
+ this.log("learner: mine failed", err);
1303
+ }
1304
+ }
1305
+ await this.recallStep();
1306
+ try {
1307
+ this.checkMissing();
1308
+ } catch (err) {
1309
+ this.log("learner: receipt check failed", err);
1310
+ }
1311
+ try {
1312
+ await this.verify();
1313
+ } catch (err) {
1314
+ this.log("learner: verification failed", err);
1315
+ }
1316
+ } finally {
1317
+ this.ticking = false;
1318
+ }
1319
+ }
1320
+
1321
+ /** Wake `eod_curation_missing` once per day whose delivered sweep offered
1322
+ * candidates and drew no receipt within the wait — never for a day a later
1323
+ * day's receipt already made good (`unresolved`'s rule: its candidates rode
1324
+ * that later packet), and an empty packet, which owes no receipt, never
1325
+ * takes one of the checked days' places. */
1326
+ checkMissing(): void {
1327
+ const now = this.now().getTime();
1328
+ const waitMs = this.cfg.lessons_receipt_wait_min * 60_000;
1329
+ const rows = this.store.db
1330
+ .prepare(
1331
+ `SELECT * FROM eod_curations WHERE fired_at IS NOT NULL AND receipt_at IS NULL
1332
+ AND missing_woken_at IS NULL AND offered <> '[]'
1333
+ AND day > COALESCE((SELECT MAX(day) FROM eod_curations WHERE receipt_at IS NOT NULL), '')
1334
+ ORDER BY day DESC LIMIT 7`,
1335
+ )
1336
+ .all() as CurationRow[];
1337
+ for (const r of rows) {
1338
+ const offered = jsonColumn<number[]>(r.offered, []);
1339
+ if (offered.length === 0) continue;
1340
+ if (!missingCuration(r, false, now, waitMs)) continue;
1341
+ this.store.db.transaction(() => {
1342
+ const at = this.ts();
1343
+ this.store.db
1344
+ .prepare("UPDATE eod_curations SET missing_woken_at = ?, updated_at = ? WHERE day = ?")
1345
+ .run(at, at, r.day);
1346
+ this.deps.wake?.(
1347
+ LEARNER_WAKE.missing,
1348
+ `EOD ${r.day}: no curation receipt ${this.cfg.lessons_receipt_wait_min} min after the sweep`,
1349
+ [
1350
+ `EOD CURATION MISSING — the EOD sweep for ${r.day} was delivered at ${r.fired_at}, its outcomes packet offered ${offered.length} candidate(s) (\`${packetMarkdownRel(r.day)}\`), and no \`eod-curation-receipt\` arrived within ${this.cfg.lessons_receipt_wait_min} minutes.`,
1351
+ "Nothing says the day's outcomes were curated. The candidates stay open and ride the next packet; undecided, they expire (recorded). Check the overseer session, then curate the packet and write the receipt.",
1352
+ ].join("\n"),
1353
+ );
1354
+ })();
1355
+ }
1356
+ }
1357
+
1358
+ /**
1359
+ * Check adoptions against memory (only while it reads healthy): a lesson
1360
+ * in `experiences/` whose typed header cites a candidate adopts it — with or
1361
+ * without a receipt. An `adopting` candidate whose receipt is older than the
1362
+ * grace, with memory read in full and no lesson citing it, was not
1363
+ * persisted: it goes back to open and wakes. A partial read proves nothing
1364
+ * absent.
1365
+ */
1366
+ async verify(): Promise<void> {
1367
+ try {
1368
+ await this.verifyAgainstMemory();
1369
+ } finally {
1370
+ // State.db only: a decided candidate leaves the lists even while memory
1371
+ // cannot be read.
1372
+ this.pruneNotPersisted();
1373
+ }
1374
+ }
1375
+
1376
+ private async verifyAgainstMemory(): Promise<void> {
1377
+ const waiting = (
1378
+ this.store.db
1379
+ .prepare(
1380
+ "SELECT COUNT(*) AS n FROM lesson_candidates WHERE status = 'adopting' OR (status = 'open' AND offered_day IS NOT NULL)",
1381
+ )
1382
+ .get() as { n: number }
1383
+ ).n;
1384
+ // Memory is read every tick while an adoption waits to be checked or an
1385
+ // offered candidate may be adopted by a lesson written without a receipt
1386
+ // (the store's sweep is incremental: an unchanged file is not re-read),
1387
+ // and otherwise every `LESSON_REFRESH_MS`, for the balance metric.
1388
+ const last = this.lessonsRead ? Date.parse(this.lessonsRead.at) : NaN;
1389
+ if (waiting === 0 && Number.isFinite(last) && this.now().getTime() - last < LESSON_REFRESH_MS)
1390
+ return;
1391
+ const pending = this.candidates("status IN ('open', 'adopting')");
1392
+ const read = await this.readLessons();
1393
+ if (!read) return;
1394
+ const cited = new Map<number, string[]>();
1395
+ for (const l of read.lessons)
1396
+ for (const id of l.candidates) cited.set(id, [...(cited.get(id) ?? []), l.id]);
1397
+ const at = this.ts();
1398
+ const db = this.store.db;
1399
+ const missingByDay = new Map<string, Array<{ id: number; why: string }>>();
1400
+ // `pending` was read before the memory read awaited: a receipt applied
1401
+ // meanwhile may have settled a candidate or re-dated its adoption, so
1402
+ // every write below holds only against the state it was decided from.
1403
+ db.transaction(() => {
1404
+ for (const c of pending) {
1405
+ const lessons = cited.get(c.id);
1406
+ if (lessons) {
1407
+ db.prepare(
1408
+ `UPDATE lesson_candidates SET status = 'adopted', lesson_ids = ?, decided_at = COALESCE(decided_at, ?),
1409
+ decided_day = COALESCE(decided_day, receipt_day, ?), updated_at = ?
1410
+ WHERE id = ? AND status IN ('open', 'adopting')`,
1411
+ ).run(JSON.stringify(lessons), at, this.today(), at, c.id);
1412
+ continue;
1413
+ }
1414
+ if (c.status !== "adopting" || !read.complete) continue;
1415
+ const receiptAt = Date.parse(c.receipt_at ?? "");
1416
+ if (!Number.isFinite(receiptAt) || this.now().getTime() - receiptAt < VERIFY_GRACE_MS)
1417
+ continue;
1418
+ // Back to the packet, with a fresh expiry clock: the wake promised the
1419
+ // next packet, and a candidate first offered days ago must not expire
1420
+ // at it.
1421
+ const reopened = db
1422
+ .prepare(
1423
+ `UPDATE lesson_candidates SET status = 'open', reopened_day = ?, reason = ?, updated_at = ?
1424
+ WHERE id = ? AND status = 'adopting' AND receipt_at IS ?`,
1425
+ )
1426
+ .run(
1427
+ this.today(),
1428
+ `the receipt of ${c.receipt_day} said adopted, but no lesson in experiences/ cites cand:${candidateLabel(c.id)}`,
1429
+ at,
1430
+ c.id,
1431
+ c.receipt_at,
1432
+ );
1433
+ if (reopened.changes === 0) continue;
1434
+ const day = c.receipt_day ?? this.today();
1435
+ missingByDay.set(day, [
1436
+ ...(missingByDay.get(day) ?? []),
1437
+ {
1438
+ id: c.id,
1439
+ why: `adopted${c.claimed_lesson ? ` as ${c.claimed_lesson}` : ""}, but no lesson in experiences/ cites cand:${candidateLabel(c.id)}`,
1440
+ },
1441
+ ]);
1442
+ }
1443
+ for (const [day, items] of missingByDay) this.notPersisted(day, items);
1444
+ })();
1445
+ }
1446
+
1447
+ /** A candidate decided since leaves the not-persisted lists it was on: the
1448
+ * lesson landed after all (adopted — a retry, the next day's curation), or
1449
+ * the curator declined it, or it expired (recorded) or was withdrawn. What
1450
+ * stays on a list is still a lesson nobody has made good. */
1451
+ private pruneNotPersisted(): void {
1452
+ const db = this.store.db;
1453
+ const settled = new Set(
1454
+ (
1455
+ db
1456
+ .prepare(`SELECT id FROM lesson_candidates WHERE status IN (${SETTLED_SQL})`)
1457
+ .all() as Array<{ id: number }>
1458
+ ).map((r) => r.id),
1459
+ );
1460
+ const at = this.ts();
1461
+ for (const row of db
1462
+ .prepare("SELECT day, not_persisted FROM eod_curations WHERE not_persisted <> '[]'")
1463
+ .all() as Array<{ day: string; not_persisted: string }>) {
1464
+ const list = jsonColumn<Array<{ id: number; why: string }>>(row.not_persisted, []);
1465
+ const kept = list.filter((m) => !settled.has(m.id));
1466
+ if (kept.length !== list.length)
1467
+ db.prepare(
1468
+ `UPDATE eod_curations SET not_persisted = ?,
1469
+ not_persisted_since = CASE WHEN ? = 0 THEN NULL ELSE not_persisted_since END, updated_at = ?
1470
+ WHERE day = ?`,
1471
+ ).run(JSON.stringify(kept), kept.length, at, row.day);
1472
+ }
1473
+ }
1474
+
1475
+ start(intervalMs = 300_000): void {
1476
+ if (this.timer) return;
1477
+ void this.tick();
1478
+ this.timer = setInterval(() => void this.tick(), Math.max(10_000, intervalMs));
1479
+ this.timer.unref?.();
1480
+ }
1481
+
1482
+ stop(): void {
1483
+ if (this.timer) clearInterval(this.timer);
1484
+ this.timer = null;
1485
+ }
1486
+
1487
+ // ---- reading back ------------------------------------------------------------
1488
+
1489
+ /**
1490
+ * Every curation failure not made good since, newest first, over the last
1491
+ * `UNRESOLVED_SCAN_DAYS` curation days: a `missing` day (the sweep
1492
+ * delivered, candidates offered, no receipt within the wait) until it or a
1493
+ * later day is receipted — its candidates rode the later packet, so that
1494
+ * receipt decided them or carried them on — and a `not_persisted` day until
1495
+ * every candidate on its list is decided (`pruneNotPersisted`). The next
1496
+ * day's packet does not resolve either: a failure stays in front of a
1497
+ * person until something made it good.
1498
+ */
1499
+ private unresolved(): UnresolvedCuration[] {
1500
+ const db = this.store.db;
1501
+ const rows = db
1502
+ .prepare("SELECT * FROM eod_curations ORDER BY day DESC LIMIT ?")
1503
+ .all(UNRESOLVED_SCAN_DAYS) as CurationRow[];
1504
+ const lists = new Map(
1505
+ rows.map((r) => [r.day, jsonColumn<Array<{ id: number; why: string }>>(r.not_persisted, [])]),
1506
+ );
1507
+ const ids = [...new Set([...lists.values()].flatMap((l) => l.map((m) => m.id)))];
1508
+ const settled = new Set(
1509
+ ids.length === 0
1510
+ ? []
1511
+ : (
1512
+ db
1513
+ .prepare(
1514
+ `SELECT id FROM lesson_candidates WHERE status IN (${SETTLED_SQL}) AND id IN (${ids.map(() => "?").join(",")})`,
1515
+ )
1516
+ .all(...ids) as Array<{ id: number }>
1517
+ ).map((r) => r.id),
1518
+ );
1519
+ const now = this.now().getTime();
1520
+ const waitMs = this.cfg.lessons_receipt_wait_min * 60_000;
1521
+ const out: UnresolvedCuration[] = [];
1522
+ let laterReceipt = false;
1523
+ for (const r of rows) {
1524
+ const open = new Set(
1525
+ (lists.get(r.day) ?? []).filter((m) => !settled.has(m.id)).map((m) => m.id),
1526
+ );
1527
+ if (open.size > 0)
1528
+ out.push({
1529
+ day: r.day,
1530
+ state: "not_persisted",
1531
+ count: open.size,
1532
+ since: r.not_persisted_since ?? r.updated_at,
1533
+ });
1534
+ const offered = jsonColumn<number[]>(r.offered, []);
1535
+ const fired = Date.parse(r.fired_at ?? "");
1536
+ if (missingCuration(r, laterReceipt, now, waitMs))
1537
+ out.push({
1538
+ day: r.day,
1539
+ state: "missing",
1540
+ count: offered.length,
1541
+ since: r.missing_woken_at ?? new Date(fired + waitMs).toISOString(),
1542
+ });
1543
+ if (r.receipt_at !== null) laterReceipt = true;
1544
+ }
1545
+ return out;
1546
+ }
1547
+
1548
+ /** The curation for status.json: the newest unresolved failure when there
1549
+ * is one (a missed day stays reported after the next packet is written),
1550
+ * else the latest day's. */
1551
+ status(): CurationStatus {
1552
+ const db = this.store.db;
1553
+ const count = (where: string, ...args: unknown[]): number =>
1554
+ (
1555
+ db.prepare(`SELECT COUNT(*) AS n FROM lesson_candidates WHERE ${where}`).get(...args) as {
1556
+ n: number;
1557
+ }
1558
+ ).n;
1559
+ const open = count("status = 'open' AND offered_day IS NOT NULL");
1560
+ const queued = count("status = 'open' AND offered_day IS NULL");
1561
+ const balance = this.lessonsRead
1562
+ ? { ...lessonBalance(this.lessonsRead.lessons), as_of: this.lessonsRead.at }
1563
+ : null;
1564
+ const unresolved = this.unresolved();
1565
+ const head = unresolved[0];
1566
+ const incident = unresolved[unresolved.length - 1] ?? null;
1567
+ const row = head
1568
+ ? this.curation(head.day)
1569
+ : (db
1570
+ .prepare(
1571
+ "SELECT * FROM eod_curations WHERE packet_at IS NOT NULL ORDER BY day DESC LIMIT 1",
1572
+ )
1573
+ .get() as CurationRow | undefined);
1574
+ if (!row)
1575
+ return {
1576
+ day: null,
1577
+ state: "no_packet",
1578
+ offered: 0,
1579
+ adopted: 0,
1580
+ declined: 0,
1581
+ verifying: 0,
1582
+ not_persisted: 0,
1583
+ expired_today: 0,
1584
+ open,
1585
+ queued,
1586
+ receipt_at: null,
1587
+ balance,
1588
+ unresolved,
1589
+ incident,
1590
+ };
1591
+ const offered = jsonColumn<number[]>(row.offered, []);
1592
+ let state: CurationStatus["state"];
1593
+ if (head) state = head.state;
1594
+ else if (row.receipt_at !== null) state = "received";
1595
+ else if (row.fired_at === null) state = "packet_ready";
1596
+ else if (offered.length === 0) state = "nothing";
1597
+ else state = "awaiting";
1598
+ return {
1599
+ day: row.day,
1600
+ state,
1601
+ offered: offered.length,
1602
+ adopted: count("receipt_day = ? AND status = 'adopted'", row.day),
1603
+ declined: count("receipt_day = ? AND status = 'declined'", row.day),
1604
+ verifying: count("receipt_day = ? AND status = 'adopting'", row.day),
1605
+ not_persisted:
1606
+ unresolved.find((u) => u.day === row.day && u.state === "not_persisted")?.count ?? 0,
1607
+ expired_today: jsonColumn<unknown[]>(row.expired, []).length,
1608
+ open,
1609
+ queued,
1610
+ receipt_at: row.receipt_at,
1611
+ balance,
1612
+ unresolved,
1613
+ incident,
1614
+ };
1615
+ }
1616
+
1617
+ /** Undecided candidates per class, busiest first — `qcr lessons`. */
1618
+ clusters(): Array<{
1619
+ class: string;
1620
+ polarity: Polarity;
1621
+ open: number;
1622
+ queued: number;
1623
+ recurrence: number;
1624
+ }> {
1625
+ const since = this.recurrenceSince();
1626
+ const rows = this.candidates("status = 'open' ORDER BY class, id");
1627
+ const out = new Map<string, { rows: CandidateRow[]; queued: number }>();
1628
+ for (const r of rows) {
1629
+ const e = out.get(r.class) ?? { rows: [], queued: 0 };
1630
+ e.rows.push(r);
1631
+ if (r.offered_day === null) e.queued += 1;
1632
+ out.set(r.class, e);
1633
+ }
1634
+ const recurrence = this.recurrence(since);
1635
+ return [...out.entries()]
1636
+ .map(([cls, e]) => ({
1637
+ class: cls,
1638
+ polarity: majorityPolarity(e.rows),
1639
+ open: e.rows.length - e.queued,
1640
+ queued: e.queued,
1641
+ recurrence: recurrence.get(cls) ?? 0,
1642
+ }))
1643
+ .sort((a, b) => b.open + b.queued - (a.open + a.queued) || a.class.localeCompare(b.class));
1644
+ }
1645
+
1646
+ /** One candidate row (tests, `qcr lessons`). */
1647
+ candidate(id: number): CandidateRow | undefined {
1648
+ return this.candidates("id = ?", id)[0];
1649
+ }
1650
+
1651
+ /** Every candidate row, oldest first (tests). */
1652
+ allCandidates(): CandidateRow[] {
1653
+ return this.candidates("1 = 1 ORDER BY id");
1654
+ }
1655
+ }