@quickchatai/quickreview 1.2.0 → 1.3.2

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 (350) hide show
  1. package/README.md +116 -53
  2. package/bin/qcr.js +1 -1
  3. package/config.example.toml +25 -7
  4. package/deploy/ansible/README.md +20 -12
  5. package/deploy/ansible/inventory.example.ini +1 -1
  6. package/deploy/collect-secrets.sh +1 -1
  7. package/deploy/qcr-update.sh +1 -1
  8. package/dist/extensions/cli/home.js +14 -0
  9. package/dist/extensions/cli/home.js.map +1 -0
  10. package/dist/extensions/cli/selfimprove.js +18 -0
  11. package/dist/extensions/cli/selfimprove.js.map +1 -0
  12. package/dist/extensions/home.js +350 -0
  13. package/dist/extensions/home.js.map +1 -0
  14. package/dist/extensions/login-route.js +1 -1
  15. package/dist/extensions/login-route.js.map +1 -1
  16. package/dist/extensions/onboarding.js +8 -7
  17. package/dist/extensions/onboarding.js.map +1 -1
  18. package/dist/extensions/selfimprove.js +49 -0
  19. package/dist/extensions/selfimprove.js.map +1 -0
  20. package/dist/extensions/setup-scan.js +23 -592
  21. package/dist/extensions/setup-scan.js.map +1 -1
  22. package/dist/extensions/setup.js +2 -47
  23. package/dist/extensions/setup.js.map +1 -1
  24. package/dist/extensions/vendor/openviking-pi/index.js +4 -0
  25. package/dist/extensions/vendor/openviking-pi/index.js.map +1 -1
  26. package/dist/extensions/vendor/openviking-pi/qcr-hooks.js +3 -1
  27. package/dist/extensions/vendor/openviking-pi/qcr-hooks.js.map +1 -1
  28. package/dist/extensions/vendor/openviking-pi/sync.js +81 -0
  29. package/dist/extensions/vendor/openviking-pi/sync.js.map +1 -1
  30. package/dist/src/archive/restore.js +18 -15
  31. package/dist/src/archive/restore.js.map +1 -1
  32. package/dist/src/botworker.js +13 -2
  33. package/dist/src/botworker.js.map +1 -1
  34. package/dist/src/channelfs.js +3 -26
  35. package/dist/src/channelfs.js.map +1 -1
  36. package/dist/src/cli.js +14 -50
  37. package/dist/src/cli.js.map +1 -1
  38. package/dist/src/config.js +16 -0
  39. package/dist/src/config.js.map +1 -1
  40. package/dist/src/daemon.js +33 -4
  41. package/dist/src/daemon.js.map +1 -1
  42. package/dist/src/debuground.js +1 -0
  43. package/dist/src/debuground.js.map +1 -1
  44. package/dist/src/debugroundpi.js +1 -0
  45. package/dist/src/debugroundpi.js.map +1 -1
  46. package/dist/src/engine/caserunner-sdk.js +15 -251
  47. package/dist/src/engine/caserunner-sdk.js.map +1 -1
  48. package/dist/src/home/adapters.js +349 -0
  49. package/dist/src/home/adapters.js.map +1 -0
  50. package/dist/src/home/advanced.js +318 -0
  51. package/dist/src/home/advanced.js.map +1 -0
  52. package/dist/src/home/advancedtools.js +57 -0
  53. package/dist/src/home/advancedtools.js.map +1 -0
  54. package/dist/src/home/autoreview.js +325 -0
  55. package/dist/src/home/autoreview.js.map +1 -0
  56. package/dist/src/home/daemonstatus.js +32 -0
  57. package/dist/src/home/daemonstatus.js.map +1 -0
  58. package/dist/src/home/devicelogin.js +61 -0
  59. package/dist/src/home/devicelogin.js.map +1 -0
  60. package/dist/src/home/launch.js +202 -0
  61. package/dist/src/home/launch.js.map +1 -0
  62. package/dist/src/home/loginowner.js +205 -0
  63. package/dist/src/home/loginowner.js.map +1 -0
  64. package/dist/src/home/memory.js +122 -0
  65. package/dist/src/home/memory.js.map +1 -0
  66. package/dist/src/home/model.js +811 -0
  67. package/dist/src/home/model.js.map +1 -0
  68. package/dist/src/home/panel.js +520 -0
  69. package/dist/src/home/panel.js.map +1 -0
  70. package/dist/src/home/ports.js +19 -0
  71. package/dist/src/home/ports.js.map +1 -0
  72. package/dist/src/home/review.js +163 -0
  73. package/dist/src/home/review.js.map +1 -0
  74. package/dist/src/home/reviews.js +478 -0
  75. package/dist/src/home/reviews.js.map +1 -0
  76. package/dist/src/home/setup.js +507 -0
  77. package/dist/src/home/setup.js.map +1 -0
  78. package/dist/src/home/setupadapters.js +214 -0
  79. package/dist/src/home/setupadapters.js.map +1 -0
  80. package/dist/src/home/setupcard.js +199 -0
  81. package/dist/src/home/setupcard.js.map +1 -0
  82. package/dist/src/home/setuptools.js +45 -0
  83. package/dist/src/home/setuptools.js.map +1 -0
  84. package/dist/src/home/spend.js +160 -0
  85. package/dist/src/home/spend.js.map +1 -0
  86. package/dist/src/home/switches.js +209 -0
  87. package/dist/src/home/switches.js.map +1 -0
  88. package/dist/src/home/switchon.js +439 -0
  89. package/dist/src/home/switchon.js.map +1 -0
  90. package/dist/src/home/toolnames.js +41 -0
  91. package/dist/src/home/toolnames.js.map +1 -0
  92. package/dist/src/home/tune.js +341 -0
  93. package/dist/src/home/tune.js.map +1 -0
  94. package/dist/src/home/words.js +7 -0
  95. package/dist/src/home/words.js.map +1 -0
  96. package/dist/src/homes.js +14 -0
  97. package/dist/src/homes.js.map +1 -1
  98. package/dist/src/install.js +1 -1
  99. package/dist/src/install.js.map +1 -1
  100. package/dist/src/ipc/workerspec.js +8 -0
  101. package/dist/src/ipc/workerspec.js.map +1 -1
  102. package/dist/src/ledgercheck.js +39 -10
  103. package/dist/src/ledgercheck.js.map +1 -1
  104. package/dist/src/live/publish.js +1 -1
  105. package/dist/src/live/publish.js.map +1 -1
  106. package/dist/src/livecli.js +1 -1
  107. package/dist/src/livecli.js.map +1 -1
  108. package/dist/src/loginrefresh/handover.js +121 -0
  109. package/dist/src/loginrefresh/handover.js.map +1 -0
  110. package/dist/src/loginrefresh/lock.js +54 -3
  111. package/dist/src/loginrefresh/lock.js.map +1 -1
  112. package/dist/src/loginrefresh/refresher.js +33 -5
  113. package/dist/src/loginrefresh/refresher.js.map +1 -1
  114. package/dist/src/loginrefresh/supervisor.js +18 -1
  115. package/dist/src/loginrefresh/supervisor.js.map +1 -1
  116. package/dist/src/npmverbs.js +42 -12
  117. package/dist/src/npmverbs.js.map +1 -1
  118. package/dist/src/onboard/choices.js +6 -3
  119. package/dist/src/onboard/choices.js.map +1 -1
  120. package/dist/src/onboard/engine.js +33 -292
  121. package/dist/src/onboard/engine.js.map +1 -1
  122. package/dist/src/onboard/steps/bringup.js +3 -2
  123. package/dist/src/onboard/steps/bringup.js.map +1 -1
  124. package/dist/src/onboard/steps/configfill.js +18 -7
  125. package/dist/src/onboard/steps/configfill.js.map +1 -1
  126. package/dist/src/onboard/steps/loginshare.js +213 -0
  127. package/dist/src/onboard/steps/loginshare.js.map +1 -0
  128. package/dist/src/onboard/steps/memory.js +14 -2
  129. package/dist/src/onboard/steps/memory.js.map +1 -1
  130. package/dist/src/onboard/steps/repoconfig.js +72 -0
  131. package/dist/src/onboard/steps/repoconfig.js.map +1 -0
  132. package/dist/src/onboard/toolnames.js +2 -2
  133. package/dist/src/onboard/toolnames.js.map +1 -1
  134. package/dist/src/piagentdir.js +13 -0
  135. package/dist/src/piagentdir.js.map +1 -0
  136. package/dist/src/pilogin.js +31 -16
  137. package/dist/src/pilogin.js.map +1 -1
  138. package/dist/src/qcrbin.js +16 -11
  139. package/dist/src/qcrbin.js.map +1 -1
  140. package/dist/src/reviewargs.js +122 -0
  141. package/dist/src/reviewargs.js.map +1 -0
  142. package/dist/src/reviewcli.js +7 -161
  143. package/dist/src/reviewcli.js.map +1 -1
  144. package/dist/src/reviewdetach.js +76 -11
  145. package/dist/src/reviewdetach.js.map +1 -1
  146. package/dist/src/scancli.js +39 -691
  147. package/dist/src/scancli.js.map +1 -1
  148. package/dist/src/selfimprove.js +66 -0
  149. package/dist/src/selfimprove.js.map +1 -0
  150. package/dist/src/session-command-supervisor.js +38 -9
  151. package/dist/src/session-command-supervisor.js.map +1 -1
  152. package/dist/src/setup/adapters.js +5 -223
  153. package/dist/src/setup/adapters.js.map +1 -1
  154. package/dist/src/setup/caserunner.js +15 -249
  155. package/dist/src/setup/caserunner.js.map +1 -1
  156. package/dist/src/setup/engine.js +32 -341
  157. package/dist/src/setup/engine.js.map +1 -1
  158. package/dist/src/setup/facts.js +3 -592
  159. package/dist/src/setup/facts.js.map +1 -1
  160. package/dist/src/setup/findings.js +3 -947
  161. package/dist/src/setup/findings.js.map +1 -1
  162. package/dist/src/setup/presentation.js +3 -8
  163. package/dist/src/setup/presentation.js.map +1 -1
  164. package/dist/src/setup/scan.js +3 -724
  165. package/dist/src/setup/scan.js.map +1 -1
  166. package/dist/src/setup/scanbuild.js +9 -92
  167. package/dist/src/setup/scanbuild.js.map +1 -1
  168. package/dist/src/setup/scanlease.js +3 -250
  169. package/dist/src/setup/scanlease.js.map +1 -1
  170. package/dist/src/setup/scanreport.js +3 -288
  171. package/dist/src/setup/scanreport.js.map +1 -1
  172. package/dist/src/setup/scanrun.js +3 -1732
  173. package/dist/src/setup/scanrun.js.map +1 -1
  174. package/dist/src/setup/scantree.js +3 -256
  175. package/dist/src/setup/scantree.js.map +1 -1
  176. package/dist/src/setup/stage2request.js +3 -258
  177. package/dist/src/setup/stage2request.js.map +1 -1
  178. package/dist/src/setup/unitcut.js +3 -436
  179. package/dist/src/setup/unitcut.js.map +1 -1
  180. package/dist/src/state.js +75 -9
  181. package/dist/src/state.js.map +1 -1
  182. package/dist/src/takeover.js +1 -0
  183. package/dist/src/takeover.js.map +1 -1
  184. package/dist/src/takeoverpi.js +2 -1
  185. package/dist/src/takeoverpi.js.map +1 -1
  186. package/dist/src/tui/alerts.js +4 -1
  187. package/dist/src/tui/alerts.js.map +1 -1
  188. package/dist/src/tui/board.js +2 -2
  189. package/dist/src/tui/board.js.map +1 -1
  190. package/dist/src/tui/companion.js +2 -1
  191. package/dist/src/tui/companion.js.map +1 -1
  192. package/dist/src/tui/deploytab.js +2 -2
  193. package/dist/src/tui/deploytab.js.map +1 -1
  194. package/dist/src/tui/index.js +9 -6
  195. package/dist/src/tui/index.js.map +1 -1
  196. package/dist/src/tui/memory.js +3 -16
  197. package/dist/src/tui/memory.js.map +1 -1
  198. package/dist/src/tui/memoryurl.js +22 -0
  199. package/dist/src/tui/memoryurl.js.map +1 -0
  200. package/dist/src/tui/remote.js +1 -1
  201. package/dist/src/tui/remote.js.map +1 -1
  202. package/dist/src/tui/route.js +2 -2
  203. package/dist/src/tui/route.js.map +1 -1
  204. package/dist/src/tui/setupstate.js +8 -5
  205. package/dist/src/tui/setupstate.js.map +1 -1
  206. package/dist/src/tui/sshtriage.js +1 -1
  207. package/dist/src/tui/sshtriage.js.map +1 -1
  208. package/dist/src/verdicts.js +12 -0
  209. package/dist/src/verdicts.js.map +1 -1
  210. package/dist/src/wire.js +9 -4
  211. package/dist/src/wire.js.map +1 -1
  212. package/dist/src/workerbreaker.js +32 -5
  213. package/dist/src/workerbreaker.js.map +1 -1
  214. package/dist/src/workerworld.js +4 -0
  215. package/dist/src/workerworld.js.map +1 -1
  216. package/docker/README.md +1 -1
  217. package/docker/entrypoint.sh +40 -6
  218. package/docker-compose.yml +16 -1
  219. package/extensions/cli/home.ts +14 -0
  220. package/extensions/cli/selfimprove.ts +18 -0
  221. package/extensions/home.ts +484 -0
  222. package/extensions/login-route.ts +1 -1
  223. package/extensions/onboarding.ts +8 -7
  224. package/extensions/selfimprove.ts +65 -0
  225. package/extensions/setup-scan.ts +79 -766
  226. package/extensions/setup.ts +14 -70
  227. package/extensions/vendor/openviking-pi/NOTICE +5 -0
  228. package/extensions/vendor/openviking-pi/README.md +39 -5
  229. package/extensions/vendor/openviking-pi/index.ts +4 -0
  230. package/extensions/vendor/openviking-pi/patches/0014-one-turn-sync-in-flight.patch +239 -0
  231. package/extensions/vendor/openviking-pi/qcr-hooks.ts +3 -1
  232. package/extensions/vendor/openviking-pi/sync.ts +80 -0
  233. package/extensions/vendor/openviking-pi/upstream.json +2 -1
  234. package/package.json +6 -4
  235. package/prompts/onboarding.md +8 -7
  236. package/schemas/review-result.v1.json +60 -0
  237. package/scripts/deploy.sh +8 -6
  238. package/scripts/qcr-host.sh +28 -5
  239. package/scripts/vm-ghcr-login.sh +7 -4
  240. package/src/archive/restore.ts +18 -21
  241. package/src/botworker.ts +13 -2
  242. package/src/channelfs.ts +3 -26
  243. package/src/cli.ts +13 -54
  244. package/src/config.ts +38 -0
  245. package/src/daemon.ts +34 -4
  246. package/src/debuground.ts +3 -0
  247. package/src/debugroundpi.ts +1 -0
  248. package/src/engine/caserunner-sdk.ts +29 -314
  249. package/src/home/adapters.ts +418 -0
  250. package/src/home/advanced.ts +421 -0
  251. package/src/home/advancedtools.ts +96 -0
  252. package/src/home/autoreview.ts +385 -0
  253. package/src/home/daemonstatus.ts +55 -0
  254. package/src/home/devicelogin.ts +107 -0
  255. package/src/home/launch.ts +257 -0
  256. package/src/home/loginowner.ts +248 -0
  257. package/src/home/memory.ts +176 -0
  258. package/src/home/model.ts +965 -0
  259. package/src/home/panel.ts +666 -0
  260. package/src/home/ports.ts +338 -0
  261. package/src/home/review.ts +206 -0
  262. package/src/home/reviews.ts +565 -0
  263. package/src/home/setup.ts +689 -0
  264. package/src/home/setupadapters.ts +217 -0
  265. package/src/home/setupcard.ts +288 -0
  266. package/src/home/setuptools.ts +57 -0
  267. package/src/home/spend.ts +194 -0
  268. package/src/home/switches.ts +293 -0
  269. package/src/home/switchon.ts +554 -0
  270. package/src/home/toolnames.ts +46 -0
  271. package/src/home/tune.ts +405 -0
  272. package/src/home/words.ts +8 -0
  273. package/src/homes.ts +17 -0
  274. package/src/install.ts +1 -1
  275. package/src/ipc/workerspec.ts +15 -0
  276. package/src/ledgercheck.ts +45 -9
  277. package/src/live/publish.ts +4 -1
  278. package/src/livecli.ts +6 -1
  279. package/src/loginrefresh/handover.ts +138 -0
  280. package/src/loginrefresh/lock.ts +72 -3
  281. package/src/loginrefresh/refresher.ts +34 -5
  282. package/src/loginrefresh/supervisor.ts +25 -2
  283. package/src/npmverbs.ts +51 -11
  284. package/src/onboard/choices.ts +6 -3
  285. package/src/onboard/engine.ts +64 -434
  286. package/src/onboard/steps/bringup.ts +3 -2
  287. package/src/onboard/steps/configfill.ts +23 -7
  288. package/src/onboard/steps/loginshare.ts +258 -0
  289. package/src/onboard/steps/memory.ts +18 -2
  290. package/src/onboard/steps/repoconfig.ts +85 -0
  291. package/src/onboard/toolnames.ts +2 -2
  292. package/src/piagentdir.ts +12 -0
  293. package/src/pilogin.ts +32 -16
  294. package/src/qcrbin.ts +16 -11
  295. package/src/reviewargs.ts +119 -0
  296. package/src/reviewcli.ts +6 -167
  297. package/src/reviewdetach.ts +107 -13
  298. package/src/scancli.ts +75 -826
  299. package/src/selfimprove.ts +90 -0
  300. package/src/session-command-supervisor.ts +41 -8
  301. package/src/setup/adapters.ts +13 -221
  302. package/src/setup/caserunner.ts +34 -273
  303. package/src/setup/engine.ts +62 -496
  304. package/src/setup/facts.ts +3 -627
  305. package/src/setup/findings.ts +3 -1264
  306. package/src/setup/presentation.ts +3 -18
  307. package/src/setup/scan.ts +3 -1033
  308. package/src/setup/scanbuild.ts +20 -114
  309. package/src/setup/scanlease.ts +3 -297
  310. package/src/setup/scanreport.ts +3 -373
  311. package/src/setup/scanrun.ts +3 -2268
  312. package/src/setup/scantree.ts +3 -259
  313. package/src/setup/stage2request.ts +3 -347
  314. package/src/setup/unitcut.ts +3 -523
  315. package/src/state.ts +126 -11
  316. package/src/takeover.ts +5 -0
  317. package/src/takeoverpi.ts +3 -2
  318. package/src/tui/alerts.ts +9 -2
  319. package/src/tui/board.ts +2 -2
  320. package/src/tui/companion.ts +2 -1
  321. package/src/tui/deploytab.ts +2 -2
  322. package/src/tui/index.ts +9 -6
  323. package/src/tui/memory.ts +3 -15
  324. package/src/tui/memoryurl.ts +22 -0
  325. package/src/tui/remote.ts +1 -1
  326. package/src/tui/route.ts +2 -2
  327. package/src/tui/setupstate.ts +20 -16
  328. package/src/tui/sshtriage.ts +1 -1
  329. package/src/verdicts.ts +13 -0
  330. package/src/wire.ts +9 -4
  331. package/src/workerbreaker.ts +41 -5
  332. package/src/workerworld.ts +9 -1
  333. package/dist/src/live/projection.js +0 -450
  334. package/dist/src/live/projection.js.map +0 -1
  335. package/dist/src/live/read.js +0 -60
  336. package/dist/src/live/read.js.map +0 -1
  337. package/dist/src/onboard/launch.js +0 -75
  338. package/dist/src/onboard/launch.js.map +0 -1
  339. package/dist/src/setup/ledger.js +0 -207
  340. package/dist/src/setup/ledger.js.map +0 -1
  341. package/dist/src/setup/stage2.js +0 -617
  342. package/dist/src/setup/stage2.js.map +0 -1
  343. package/dist/src/setup/textsniff.js +0 -89
  344. package/dist/src/setup/textsniff.js.map +0 -1
  345. package/src/live/projection.ts +0 -491
  346. package/src/live/read.ts +0 -86
  347. package/src/onboard/launch.ts +0 -103
  348. package/src/setup/ledger.ts +0 -324
  349. package/src/setup/stage2.ts +0 -926
  350. package/src/setup/textsniff.ts +0 -95
@@ -1,437 +1,4 @@
1
- // The unit cut (setup & scan spec "Unit"): the
2
- // partition of a repo into stack-aware units — a Django app, a package, a
3
- // workspace — that the survey reads, the scan reviews and every report groups
4
- // by. The COMPANION proposes it, the OPERATOR adjusts it, and once saved it is
5
- // reused by every later survey and scan; the companion re-cuts only on request.
6
- //
7
- // A unit OWNS path prefixes (`roots`). A file belongs to the unit whose root is
8
- // the LONGEST prefix of it, so units nest naturally (`backend` and
9
- // `backend/payments` are two units, and `backend/payments/x.py` is the
10
- // second's); an `ignored` prefix is the same kind of claim held by nobody, so
11
- // `backend/vendor` can be carved out of `backend` and a unit inside an ignored
12
- // tree takes its files back. The root `""` claims everything, which is how a
13
- // catch-all "rest" unit is spelt. A SAVED cut is a partition: every file is in
14
- // exactly one unit or deliberately ignored — a file nobody claims refuses the
15
- // save, because a scan that silently skips it would read as "covered".
16
- //
17
- // Pure: the cut is a value, validation and the adjust ops are functions over
18
- // it and a file listing. The engine persists it (src/setup/engine.ts).
19
- import { SCAN_DEFAULTS } from "../config.js";
20
- import { displayRoot, UNIT_KINDS, UNIT_NAME_MAX, UNIT_NAME_RE } from "./facts.js";
21
- // ---- normalisation -----------------------------------------------------------
22
- /** A root as stored: repo-relative, no leading `./` or `/`, no trailing `/`.
23
- * Null for anything that could leave the repo (`..`, an absolute path after
24
- * stripping is fine — it is relative by construction — but `..` is not). */
25
- export function normalizeRoot(raw) {
26
- let r = raw.trim().replace(/\\/g, "/");
27
- while (r.startsWith("./"))
28
- r = r.slice(2);
29
- r = r
30
- .replace(/^\/+/, "")
31
- .replace(/\/+$/, "")
32
- .replace(/\/{2,}/g, "/");
33
- if (r === ".")
34
- r = "";
35
- if (r.split("/").some((s) => s === ".." || s === "."))
36
- return null;
37
- return r;
38
- }
39
- function covers(root, path) {
40
- return root === "" || path === root || path.startsWith(`${root}/`);
41
- }
42
- /** Longest-prefix assignment of every listed file. */
43
- export function assign(cut, files) {
44
- const claims = [
45
- ...cut.units.flatMap((u) => u.roots.map((root) => ({ root, unit: u.name }))),
46
- ...cut.ignored.map((root) => ({ root, unit: null })),
47
- ].sort((a, b) => b.root.length - a.root.length);
48
- const byUnit = new Map(cut.units.map((u) => [u.name, []]));
49
- const ignored = [];
50
- const unassigned = [];
51
- for (const f of files) {
52
- const hit = claims.find((c) => covers(c.root, f));
53
- if (!hit)
54
- unassigned.push(f);
55
- else if (hit.unit === null)
56
- ignored.push(f);
57
- else
58
- byUnit.get(hit.unit).push(f);
59
- }
60
- return { byUnit, ignored, unassigned };
61
- }
62
- const SAMPLE = 8;
63
- /** Refused both when a proposal is read and when a cut is checked. */
64
- const ROOT_IGNORED = "the repo root cannot be ignored — that would leave nothing to review";
65
- function sample(paths) {
66
- const head = paths.slice(0, SAMPLE).join(", ");
67
- return paths.length > SAMPLE ? `${head}, … (+${paths.length - SAMPLE} more)` : head;
68
- }
69
- /**
70
- * Structural refusals (bad names, duplicate names or roots, a root that could
71
- * leave the repo or matches no file, an ignored repo root) and the two
72
- * save-only conditions (a unit that ends up with no files, files no unit
73
- * claims). The cut is expected normalised (see `normalizeCut`).
74
- */
75
- export function checkCut(cut, files) {
76
- const errors = [];
77
- const blocking = [];
78
- if (cut.units.length === 0)
79
- errors.push("a unit cut needs at least one unit");
80
- const names = new Set();
81
- const roots = new Map();
82
- for (const u of cut.units) {
83
- if (!UNIT_NAME_RE.test(u.name) || u.name.includes("..")) {
84
- errors.push(`unit name ${JSON.stringify(u.name)} is not a plain name — letters, digits, '.', '_' and '-', at most ${UNIT_NAME_MAX}`);
85
- }
86
- if (names.has(u.name))
87
- errors.push(`unit name "${u.name}" is used twice`);
88
- names.add(u.name);
89
- if (u.roots.length === 0)
90
- errors.push(`unit "${u.name}" owns no root`);
91
- for (const r of u.roots) {
92
- const owner = roots.get(r);
93
- if (owner !== undefined)
94
- errors.push(`root "${displayRoot(r)}" is claimed by both "${owner}" and "${u.name}"`);
95
- roots.set(r, u.name);
96
- }
97
- }
98
- for (const r of cut.ignored) {
99
- if (r === "")
100
- errors.push(ROOT_IGNORED);
101
- const owner = roots.get(r);
102
- if (owner !== undefined)
103
- errors.push(`root "${r}" is both ignored and owned by "${owner}"`);
104
- roots.set(r, "(ignored)");
105
- }
106
- for (const [r, owner] of roots) {
107
- if (r !== "" && !files.some((f) => covers(r, f))) {
108
- errors.push(`root "${r}" (${owner}) matches no file in the repo`);
109
- }
110
- }
111
- if (errors.length === 0) {
112
- const a = assign(cut, files);
113
- for (const [name, owned] of a.byUnit) {
114
- if (owned.length === 0) {
115
- blocking.push(`unit "${name}" ends up with no files — every file under its roots belongs to a deeper unit or is ignored`);
116
- }
117
- }
118
- if (a.unassigned.length > 0) {
119
- blocking.push(`${a.unassigned.length} file(s) belong to no unit: ${sample(a.unassigned)} — add them to a unit, add a catch-all unit with root "", or ignore them`);
120
- }
121
- }
122
- return { errors, blocking_save: blocking };
123
- }
124
- /** Raw input (a tool call's JSON) → a normalised cut, or the reasons it is not
125
- * one. Normalisation never guesses: an unspellable root is an error, not a
126
- * silently-dropped entry. */
127
- export function normalizeCut(raw) {
128
- const errors = [];
129
- const obj = (raw ?? {});
130
- if (!Array.isArray(obj.units))
131
- return { ok: false, errors: ["`units` must be an array of {name, roots, kind?, note?}"] };
132
- const units = [];
133
- for (const [i, u] of obj.units.entries()) {
134
- const unit = (u ?? {});
135
- if (typeof unit.name !== "string") {
136
- errors.push(`units[${i}]: name must be a string`);
137
- continue;
138
- }
139
- const rawRoots = typeof unit.roots === "string" ? [unit.roots] : unit.roots;
140
- if (!Array.isArray(rawRoots) || rawRoots.some((r) => typeof r !== "string")) {
141
- errors.push(`unit "${unit.name}": roots must be an array of repo-relative paths`);
142
- continue;
143
- }
144
- const roots = normalizeRoots(rawRoots, `unit "${unit.name}"`, errors);
145
- units.push({
146
- name: unit.name.trim(),
147
- roots,
148
- kind: unitKind(unit.kind),
149
- note: typeof unit.note === "string" ? unit.note.trim() : "",
150
- });
151
- }
152
- let ignored = [];
153
- if (obj.ignored !== undefined) {
154
- if (!Array.isArray(obj.ignored) || obj.ignored.some((r) => typeof r !== "string")) {
155
- errors.push("`ignored` must be an array of repo-relative paths");
156
- }
157
- else {
158
- ignored = normalizeRoots(obj.ignored, "ignored", errors);
159
- if (ignored.includes(""))
160
- errors.push(ROOT_IGNORED);
161
- }
162
- }
163
- return errors.length > 0 ? { ok: false, errors } : { ok: true, cut: { units, ignored } };
164
- }
165
- /** A supplied kind, or `custom` for anything that is not one of `UNIT_KINDS`. */
166
- function unitKind(raw) {
167
- return typeof raw === "string" && new Set(UNIT_KINDS).has(raw)
168
- ? raw
169
- : "custom";
170
- }
171
- function normalizeRoots(raw, owner, errors) {
172
- const out = [];
173
- for (const r of raw) {
174
- const n = normalizeRoot(r);
175
- if (n === null)
176
- errors.push(`${owner}: root ${JSON.stringify(r)} leaves the repo`);
177
- else if (!out.includes(n))
178
- out.push(n);
179
- }
180
- return out;
181
- }
182
- export const ADJUST_OPS = [
183
- "rename",
184
- "add",
185
- "remove",
186
- "merge",
187
- "move",
188
- "set_roots",
189
- "ignore",
190
- "unignore",
191
- "note",
192
- ];
193
- function clone(cut) {
194
- return {
195
- units: cut.units.map((u) => ({ ...u, roots: [...u.roots] })),
196
- ignored: [...cut.ignored],
197
- };
198
- }
199
- function strings(v) {
200
- if (typeof v === "string")
201
- return [v];
202
- return Array.isArray(v) && v.every((x) => typeof x === "string") ? v : null;
203
- }
204
- /**
205
- * Apply `ops` in order to a copy of `cut`. Returns the new cut, or every
206
- * reason it could not (unknown unit, unknown op, a root that leaves the repo).
207
- * The structural check of the RESULT is the caller's (`checkCut`) — an op
208
- * sequence may pass through an invalid intermediate state (swap two names).
209
- */
210
- export function applyOps(cut, ops) {
211
- const next = clone(cut);
212
- const errors = [];
213
- const unit = (name, i) => {
214
- const u = typeof name === "string" ? next.units.find((x) => x.name === name) : undefined;
215
- if (!u)
216
- errors.push(`ops[${i}]: no unit named ${JSON.stringify(name)} — units: ${next.units.map((x) => x.name).join(", ")}`);
217
- return u ?? null;
218
- };
219
- const rootsOf = (v, i) => {
220
- const raw = strings(v);
221
- if (raw === null) {
222
- errors.push(`ops[${i}]: roots must be a path or an array of paths`);
223
- return null;
224
- }
225
- return normalizeRoots(raw, `ops[${i}]`, errors);
226
- };
227
- const release = (roots) => {
228
- for (const u of next.units)
229
- u.roots = u.roots.filter((r) => !roots.includes(r));
230
- next.ignored = next.ignored.filter((r) => !roots.includes(r));
231
- };
232
- for (const [i, raw] of ops.entries()) {
233
- const o = (raw ?? {});
234
- switch (o.op) {
235
- case "rename": {
236
- const u = unit(o.unit, i);
237
- if (u && typeof o.to === "string")
238
- u.name = o.to.trim();
239
- else if (u)
240
- errors.push(`ops[${i}]: rename needs \`to\``);
241
- break;
242
- }
243
- case "add": {
244
- const roots = rootsOf(o.roots, i);
245
- if (typeof o.name !== "string")
246
- errors.push(`ops[${i}]: add needs \`name\``);
247
- else if (roots) {
248
- release(roots);
249
- next.units.push({
250
- name: o.name.trim(),
251
- roots,
252
- kind: unitKind(o.kind),
253
- note: typeof o.note === "string" ? o.note.trim() : "added by the operator",
254
- });
255
- }
256
- break;
257
- }
258
- case "remove": {
259
- const u = unit(o.unit, i);
260
- if (u)
261
- next.units = next.units.filter((x) => x !== u);
262
- break;
263
- }
264
- case "merge": {
265
- const names = strings(o.units);
266
- if (!names || names.length < 2 || typeof o.into !== "string") {
267
- errors.push(`ops[${i}]: merge needs \`units\` (two or more) and \`into\``);
268
- break;
269
- }
270
- const members = names.map((n) => unit(n, i));
271
- if (members.some((m) => m === null))
272
- break;
273
- const into = o.into.trim();
274
- const keep = members.find((m) => m.name === into) ?? members[0];
275
- keep.roots = [...new Set(members.flatMap((m) => m.roots))];
276
- keep.name = into;
277
- keep.note = keep.note
278
- ? `${keep.note}; merged from ${names.join(", ")}`
279
- : `merged from ${names.join(", ")}`;
280
- next.units = next.units.filter((x) => x === keep || !members.includes(x));
281
- break;
282
- }
283
- case "move": {
284
- const roots = rootsOf(o.root, i);
285
- const u = unit(o.to, i);
286
- if (roots && u) {
287
- release(roots);
288
- u.roots.push(...roots);
289
- }
290
- break;
291
- }
292
- case "set_roots": {
293
- const u = unit(o.unit, i);
294
- const roots = rootsOf(o.roots, i);
295
- if (u && roots) {
296
- release(roots);
297
- u.roots = roots;
298
- }
299
- break;
300
- }
301
- case "ignore": {
302
- const roots = rootsOf(o.roots, i);
303
- if (roots) {
304
- release(roots);
305
- next.ignored.push(...roots);
306
- }
307
- break;
308
- }
309
- case "unignore": {
310
- const roots = rootsOf(o.roots, i);
311
- if (roots) {
312
- const missing = roots.filter((r) => !next.ignored.includes(r));
313
- if (missing.length > 0)
314
- errors.push(`ops[${i}]: not ignored: ${missing.join(", ")}`);
315
- next.ignored = next.ignored.filter((r) => !roots.includes(r));
316
- }
317
- break;
318
- }
319
- case "note": {
320
- const u = unit(o.unit, i);
321
- if (u && typeof o.note === "string")
322
- u.note = o.note.trim();
323
- else if (u)
324
- errors.push(`ops[${i}]: note needs \`note\``);
325
- break;
326
- }
327
- default:
328
- errors.push(`ops[${i}]: unknown op ${JSON.stringify(o.op)} — one of ${ADJUST_OPS.join(", ")}`);
329
- }
330
- }
331
- return errors.length > 0 ? { ok: false, errors } : { ok: true, cut: next };
332
- }
333
- export const DEFAULT_CASE_BUDGET = {
334
- maxFiles: SCAN_DEFAULTS.case_max_files,
335
- maxLines: SCAN_DEFAULTS.case_max_lines,
336
- };
337
- /**
338
- * Split one unit's files into cases that fit `budget`: subdirectory first
339
- * (a directory that fits stays whole), then file chunks in path order; small
340
- * neighbouring pieces are packed back together so a split never produces a
341
- * case per tiny directory. Every case keeps the unit's name. Deterministic:
342
- * the same files, sizes and budget always give the same cases. No files, no
343
- * case.
344
- */
345
- export function splitUnit(unit, files, linesOf, budget = DEFAULT_CASE_BUDGET) {
346
- if (files.length === 0)
347
- return [];
348
- const sorted = [...files].sort();
349
- const size = new Map(sorted.map((f) => [f, Math.max(0, linesOf(f))]));
350
- const fits = (n, lines) => n <= budget.maxFiles && lines <= budget.maxLines;
351
- const total = (fs) => fs.reduce((n, f) => n + size.get(f), 0);
352
- const pieces = (fs, depth) => {
353
- const lines = total(fs);
354
- if (fits(fs.length, lines))
355
- return [{ files: fs, lines }];
356
- // By the next path segment below the common prefix of this depth.
357
- const groups = new Map();
358
- const loose = [];
359
- for (const f of fs) {
360
- const segs = f.split("/");
361
- if (segs.length - 1 > depth) {
362
- const key = segs.slice(0, depth + 1).join("/");
363
- if (!groups.has(key))
364
- groups.set(key, []);
365
- groups.get(key).push(f);
366
- }
367
- else {
368
- loose.push(f);
369
- }
370
- }
371
- // One directory holding everything: descend without spending a split.
372
- if (loose.length === 0 && groups.size === 1)
373
- return pieces(fs, depth + 1);
374
- const out = [];
375
- for (const key of [...groups.keys()].sort())
376
- out.push(...pieces(groups.get(key), depth + 1));
377
- out.push(...chunk(loose));
378
- return out;
379
- };
380
- const chunk = (fs) => {
381
- const out = [];
382
- let cur = { files: [], lines: 0 };
383
- for (const f of fs) {
384
- const n = size.get(f);
385
- if (cur.files.length > 0 && !fits(cur.files.length + 1, cur.lines + n)) {
386
- out.push(cur);
387
- cur = { files: [], lines: 0 };
388
- }
389
- cur.files.push(f);
390
- cur.lines += n;
391
- }
392
- if (cur.files.length > 0)
393
- out.push(cur);
394
- return out;
395
- };
396
- // Pack neighbours: a directory that fit on its own may share a case with
397
- // the next one when both still fit together.
398
- const packed = [];
399
- for (const p of pieces(sorted, commonDepth(sorted))) {
400
- const last = packed[packed.length - 1];
401
- if (last && fits(last.files.length + p.files.length, last.lines + p.lines)) {
402
- last.files.push(...p.files);
403
- last.lines += p.lines;
404
- }
405
- else {
406
- packed.push({ files: [...p.files], lines: p.lines });
407
- }
408
- }
409
- const parts = packed.length;
410
- return packed.map((p, i) => ({
411
- unit,
412
- id: parts === 1 ? unit : `${unit}#${i + 1}`,
413
- part: i + 1,
414
- parts,
415
- files: p.files,
416
- lines: p.lines,
417
- over_budget: !fits(p.files.length, p.lines),
418
- }));
419
- }
420
- /** Depth of the directory every file shares, so a unit rooted at
421
- * `backend/payments` splits by what is BELOW it rather than re-grouping on
422
- * `backend`. */
423
- function commonDepth(files) {
424
- if (files.length === 0)
425
- return 0;
426
- const first = files[0].split("/").slice(0, -1);
427
- let depth = first.length;
428
- for (const f of files) {
429
- const segs = f.split("/").slice(0, -1);
430
- let d = 0;
431
- while (d < depth && d < segs.length && segs[d] === first[d])
432
- d++;
433
- depth = d;
434
- }
435
- return depth;
436
- }
1
+ // Moved to the plugin (packages/pi-plugin/src/scan/unitcut.ts), the scan engine's
2
+ // home: this name stays for the app's importers.
3
+ export * from "@quickchatai/pi-quickreview/src/scan/unitcut.js";
437
4
  //# sourceMappingURL=unitcut.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"unitcut.js","sourceRoot":"","sources":["../../../src/setup/unitcut.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"unitcut.js","sourceRoot":"","sources":["../../../src/setup/unitcut.ts"],"names":[],"mappings":"AAAA,kFAAkF;AAClF,iDAAiD;AACjD,cAAc,iDAAiD,CAAC"}
package/dist/src/state.js CHANGED
@@ -2,7 +2,7 @@
2
2
  // Core tables: pr_state, rounds, bot_comments (registry), deliveries
3
3
  // (webhook dedupe), events (append log).
4
4
  import { findingRowFields } from "@quickchatai/pi-quickreview/src/finding-row.js";
5
- import { roundStageDeadlines } from "@quickchatai/pi-quickreview/src/round-stage-deadlines.js";
5
+ import { discoveryRowsOf, roundStageDeadlines, } from "@quickchatai/pi-quickreview/src/round-stage-deadlines.js";
6
6
  import Database from "better-sqlite3";
7
7
  import { chmodSync, mkdirSync, renameSync, writeFileSync } from "node:fs";
8
8
  import { dirname, join } from "node:path";
@@ -598,7 +598,8 @@ CREATE TABLE IF NOT EXISTS drain_state (
598
598
  -- with parked_at set gets no worker until the operator's rerun-round or
599
599
  -- restart-session clears the row. worker_breaker_global.epoch ticks at every
600
600
  -- worker success (and at the lift of the breaker's drain); a failure counts
601
- -- toward the global drain only while it carries the current epoch.
601
+ -- toward the global drain only while it carries the current epoch. trips
602
+ -- counts the breaker's drains since then: each waits twice as long to lapse.
602
603
  CREATE TABLE IF NOT EXISTS worker_breaker (
603
604
  pr INTEGER PRIMARY KEY,
604
605
  failures INTEGER NOT NULL DEFAULT 0,
@@ -612,6 +613,7 @@ CREATE TABLE IF NOT EXISTS worker_breaker (
612
613
  CREATE TABLE IF NOT EXISTS worker_breaker_global (
613
614
  id INTEGER PRIMARY KEY CHECK (id = 1),
614
615
  epoch INTEGER NOT NULL DEFAULT 0,
616
+ trips INTEGER NOT NULL DEFAULT 0,
615
617
  updated_at TEXT NOT NULL
616
618
  );
617
619
  -- The host lease: who holds a PR's session while the operator works
@@ -1109,6 +1111,7 @@ export class StateStore {
1109
1111
  this.db.exec("CREATE INDEX IF NOT EXISTS bot_comments_id ON bot_comments(comment_id)");
1110
1112
  this.migrateRecheckReceipts();
1111
1113
  this.migrateDrainHold();
1114
+ this.migrateBreakerHeal();
1112
1115
  this.migrateSessionCommandCancelledStatus();
1113
1116
  this.runDataMigrations();
1114
1117
  }
@@ -1208,6 +1211,30 @@ export class StateStore {
1208
1211
  this.logEvent("drain_hold_carried", null, {});
1209
1212
  })();
1210
1213
  }
1214
+ /**
1215
+ * The breaker's drain lapses on its own (src/workerbreaker.ts). One an older
1216
+ * daemon set stands with no expiry, and only `drain off` would lift it: the
1217
+ * boot that adds the `trips` column gives it an expiry of now, so the first
1218
+ * sweep lifts it and the queue retries, with the failures that tripped it
1219
+ * spent. Only the breaker's drain is carried
1220
+ * (its reason, no hold); a hand-set drain stands as it was set.
1221
+ */
1222
+ migrateBreakerHeal() {
1223
+ this.db.transaction(() => {
1224
+ if (!this.addColumn("ALTER TABLE worker_breaker_global ADD COLUMN trips INTEGER NOT NULL DEFAULT 0"))
1225
+ return;
1226
+ const info = this.db
1227
+ .prepare(`UPDATE drain_state SET expires_at = ?
1228
+ WHERE id = 1 AND draining = 1 AND reason = ? AND expires_at IS NULL AND hold = 0`)
1229
+ .run(this.ts(), WORKER_FAILURES_DRAIN_REASON);
1230
+ if (info.changes > 0) {
1231
+ // The failures that tripped it are spent, as at a new trip: a retry
1232
+ // failing once more must not re-drain with them.
1233
+ this.resetWorkerFailureWindow();
1234
+ this.logEvent("breaker_drain_heal_carried", null, {});
1235
+ }
1236
+ })();
1237
+ }
1211
1238
  /** Existing databases carry a CHECK constraint that predates operator
1212
1239
  * cancellation. Rebuild this one table in-place; every column and index is
1213
1240
  * copied, and the transaction makes the old or new shape visible atomically. */
@@ -4840,7 +4867,9 @@ export class StateStore {
4840
4867
  * One more consecutive worker failure for `pr`. The PR is parked when the count
4841
4868
  * reaches `parkAfter`; `parked` says whether it stands parked now and
4842
4869
  * `newlyParked` whether THIS failure parked it (the caller raises the anomaly
4843
- * once).
4870
+ * once). With `drain: false` it does not count toward the global drain: its
4871
+ * epoch is one no window ever carries, so `failedWorkerPrsSince` never lists
4872
+ * it. With `park: false` it counts but does not park the PR by itself.
4844
4873
  */
4845
4874
  recordWorkerFailure(pr, o) {
4846
4875
  return this.db
@@ -4848,7 +4877,11 @@ export class StateStore {
4848
4877
  const row = this.workerBreaker(pr);
4849
4878
  const failures = (row?.failures ?? 0) + 1;
4850
4879
  const wasParked = row?.parked_at != null;
4851
- const parkedAt = wasParked ? row.parked_at : failures >= o.parkAfter ? o.at : null;
4880
+ const parkedAt = wasParked
4881
+ ? row.parked_at
4882
+ : o.park !== false && failures >= o.parkAfter
4883
+ ? o.at
4884
+ : null;
4852
4885
  this.db
4853
4886
  .prepare(`INSERT INTO worker_breaker (pr, failures, parked_at, last_error, last_failure_at, last_command_id, failure_epoch, updated_at)
4854
4887
  VALUES (@pr, @failures, @parked, @error, @at, @command, @epoch, @at)
@@ -4861,7 +4894,7 @@ export class StateStore {
4861
4894
  error: o.error,
4862
4895
  at: o.at,
4863
4896
  command: o.commandId,
4864
- epoch: this.workerEpoch(),
4897
+ epoch: o.drain === false ? -1 : this.workerEpoch(),
4865
4898
  });
4866
4899
  return {
4867
4900
  failures,
@@ -4897,11 +4930,25 @@ export class StateStore {
4897
4930
  const row = this.db.prepare("SELECT epoch FROM worker_breaker_global WHERE id = 1").get();
4898
4931
  return row?.epoch ?? 0;
4899
4932
  }
4900
- /** Start the global failure window afresh: earlier failures stop counting. */
4933
+ /** Start the global failure window afresh: earlier failures stop counting,
4934
+ * and the breaker's next drain waits the shortest time again. */
4901
4935
  resetWorkerFailureWindow() {
4902
4936
  this.db
4903
- .prepare(`INSERT INTO worker_breaker_global (id, epoch, updated_at) VALUES (1, 1, @ts)
4904
- ON CONFLICT(id) DO UPDATE SET epoch = epoch + 1, updated_at = @ts`)
4937
+ .prepare(`INSERT INTO worker_breaker_global (id, epoch, trips, updated_at) VALUES (1, 1, 0, @ts)
4938
+ ON CONFLICT(id) DO UPDATE SET epoch = epoch + 1, trips = 0, updated_at = @ts`)
4939
+ .run({ ts: this.ts() });
4940
+ }
4941
+ /** The breaker's drains since the last worker success or operator lift. */
4942
+ workerBreakerTrips() {
4943
+ const row = this.db.prepare("SELECT trips FROM worker_breaker_global WHERE id = 1").get();
4944
+ return row?.trips ?? 0;
4945
+ }
4946
+ /** The breaker drained once more: the failures that tripped it are spent (a
4947
+ * new epoch), and its next drain waits longer. */
4948
+ recordWorkerBreakerTrip() {
4949
+ this.db
4950
+ .prepare(`INSERT INTO worker_breaker_global (id, epoch, trips, updated_at) VALUES (1, 1, 1, @ts)
4951
+ ON CONFLICT(id) DO UPDATE SET epoch = epoch + 1, trips = trips + 1, updated_at = @ts`)
4905
4952
  .run({ ts: this.ts() });
4906
4953
  }
4907
4954
  /** Charge a session grant to `day`'s budget. `pr` is null for a drain. */
@@ -5084,6 +5131,23 @@ export class StateStore {
5084
5131
  .all(pr);
5085
5132
  return someMarkHasKey(rows, key);
5086
5133
  }
5134
+ /** Has a check after this (pr, key)'s spent nudge already been recorded? */
5135
+ ledgerRepeatSeen(pr, key) {
5136
+ const rows = this.db
5137
+ .prepare("SELECT payload FROM events WHERE kind = 'ledger_stale_repeat' AND pr = ?")
5138
+ .all(pr);
5139
+ return someMarkHasKey(rows, key);
5140
+ }
5141
+ /** The heads a recheck upheld on `pr` at or after `since` (every one when null). */
5142
+ recheckUpheldHeads(pr, since) {
5143
+ const rows = this.db
5144
+ .prepare("SELECT payload FROM events WHERE kind = 'recheck_upheld' AND pr = ? AND ts >= ?")
5145
+ .all(pr, since ?? "");
5146
+ // An unreadable mark names no head.
5147
+ return rows
5148
+ .map((r) => readStringField(r.payload, "head_sha"))
5149
+ .filter((head) => head !== undefined && head !== "");
5150
+ }
5087
5151
  /**
5088
5152
  * What the event log says about the migration-collision COMMENT with this
5089
5153
  * fingerprint on this PR (the collision comment's dedupe). Scans every such event for the
@@ -5762,7 +5826,7 @@ export function buildStatusSnapshot(store, dryRun, digestHeadline, now = () => n
5762
5826
  const started = Date.parse(command.started_at);
5763
5827
  if (!Number.isFinite(started))
5764
5828
  return identity;
5765
- const deadlines = roundStageDeadlines(started, roundStageBounds, round.sweep_owed === 1);
5829
+ const deadlines = roundStageDeadlines(started, roundStageBounds, round.sweep_owed === 1, discoveryRowsOf(round));
5766
5830
  return {
5767
5831
  ...identity,
5768
5832
  stage_deadlines: {
@@ -5831,6 +5895,8 @@ export function buildStatusSnapshot(store, dryRun, digestHeadline, now = () => n
5831
5895
  ...(maxConcurrentRounds !== undefined ? { max_concurrent_rounds: maxConcurrentRounds } : {}),
5832
5896
  total_spend_usd: store.totalSpend(),
5833
5897
  today_spend_usd: roundUsage(store.roundsOn(today)).spend_usd,
5898
+ // roundsOn matches by prefix: the month's `YYYY-MM` is every day of it
5899
+ month_spend_usd: roundUsage(store.roundsOn(today.slice(0, 7))).spend_usd,
5834
5900
  ...(digestHeadline ? { digest_headline: digestHeadline } : {}),
5835
5901
  ...(piAuth
5836
5902
  ? {