@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.
- package/README.md +116 -53
- package/bin/qcr.js +1 -1
- package/config.example.toml +25 -7
- package/deploy/ansible/README.md +20 -12
- package/deploy/ansible/inventory.example.ini +1 -1
- package/deploy/collect-secrets.sh +1 -1
- package/deploy/qcr-update.sh +1 -1
- package/dist/extensions/cli/home.js +14 -0
- package/dist/extensions/cli/home.js.map +1 -0
- package/dist/extensions/cli/selfimprove.js +18 -0
- package/dist/extensions/cli/selfimprove.js.map +1 -0
- package/dist/extensions/home.js +350 -0
- package/dist/extensions/home.js.map +1 -0
- package/dist/extensions/login-route.js +1 -1
- package/dist/extensions/login-route.js.map +1 -1
- package/dist/extensions/onboarding.js +8 -7
- package/dist/extensions/onboarding.js.map +1 -1
- package/dist/extensions/selfimprove.js +49 -0
- package/dist/extensions/selfimprove.js.map +1 -0
- package/dist/extensions/setup-scan.js +23 -592
- package/dist/extensions/setup-scan.js.map +1 -1
- package/dist/extensions/setup.js +2 -47
- package/dist/extensions/setup.js.map +1 -1
- package/dist/extensions/vendor/openviking-pi/index.js +4 -0
- package/dist/extensions/vendor/openviking-pi/index.js.map +1 -1
- package/dist/extensions/vendor/openviking-pi/qcr-hooks.js +3 -1
- package/dist/extensions/vendor/openviking-pi/qcr-hooks.js.map +1 -1
- package/dist/extensions/vendor/openviking-pi/sync.js +81 -0
- package/dist/extensions/vendor/openviking-pi/sync.js.map +1 -1
- package/dist/src/archive/restore.js +18 -15
- package/dist/src/archive/restore.js.map +1 -1
- package/dist/src/botworker.js +13 -2
- package/dist/src/botworker.js.map +1 -1
- package/dist/src/channelfs.js +3 -26
- package/dist/src/channelfs.js.map +1 -1
- package/dist/src/cli.js +14 -50
- package/dist/src/cli.js.map +1 -1
- package/dist/src/config.js +16 -0
- package/dist/src/config.js.map +1 -1
- package/dist/src/daemon.js +33 -4
- package/dist/src/daemon.js.map +1 -1
- package/dist/src/debuground.js +1 -0
- package/dist/src/debuground.js.map +1 -1
- package/dist/src/debugroundpi.js +1 -0
- package/dist/src/debugroundpi.js.map +1 -1
- package/dist/src/engine/caserunner-sdk.js +15 -251
- package/dist/src/engine/caserunner-sdk.js.map +1 -1
- package/dist/src/home/adapters.js +349 -0
- package/dist/src/home/adapters.js.map +1 -0
- package/dist/src/home/advanced.js +318 -0
- package/dist/src/home/advanced.js.map +1 -0
- package/dist/src/home/advancedtools.js +57 -0
- package/dist/src/home/advancedtools.js.map +1 -0
- package/dist/src/home/autoreview.js +325 -0
- package/dist/src/home/autoreview.js.map +1 -0
- package/dist/src/home/daemonstatus.js +32 -0
- package/dist/src/home/daemonstatus.js.map +1 -0
- package/dist/src/home/devicelogin.js +61 -0
- package/dist/src/home/devicelogin.js.map +1 -0
- package/dist/src/home/launch.js +202 -0
- package/dist/src/home/launch.js.map +1 -0
- package/dist/src/home/loginowner.js +205 -0
- package/dist/src/home/loginowner.js.map +1 -0
- package/dist/src/home/memory.js +122 -0
- package/dist/src/home/memory.js.map +1 -0
- package/dist/src/home/model.js +811 -0
- package/dist/src/home/model.js.map +1 -0
- package/dist/src/home/panel.js +520 -0
- package/dist/src/home/panel.js.map +1 -0
- package/dist/src/home/ports.js +19 -0
- package/dist/src/home/ports.js.map +1 -0
- package/dist/src/home/review.js +163 -0
- package/dist/src/home/review.js.map +1 -0
- package/dist/src/home/reviews.js +478 -0
- package/dist/src/home/reviews.js.map +1 -0
- package/dist/src/home/setup.js +507 -0
- package/dist/src/home/setup.js.map +1 -0
- package/dist/src/home/setupadapters.js +214 -0
- package/dist/src/home/setupadapters.js.map +1 -0
- package/dist/src/home/setupcard.js +199 -0
- package/dist/src/home/setupcard.js.map +1 -0
- package/dist/src/home/setuptools.js +45 -0
- package/dist/src/home/setuptools.js.map +1 -0
- package/dist/src/home/spend.js +160 -0
- package/dist/src/home/spend.js.map +1 -0
- package/dist/src/home/switches.js +209 -0
- package/dist/src/home/switches.js.map +1 -0
- package/dist/src/home/switchon.js +439 -0
- package/dist/src/home/switchon.js.map +1 -0
- package/dist/src/home/toolnames.js +41 -0
- package/dist/src/home/toolnames.js.map +1 -0
- package/dist/src/home/tune.js +341 -0
- package/dist/src/home/tune.js.map +1 -0
- package/dist/src/home/words.js +7 -0
- package/dist/src/home/words.js.map +1 -0
- package/dist/src/homes.js +14 -0
- package/dist/src/homes.js.map +1 -1
- package/dist/src/install.js +1 -1
- package/dist/src/install.js.map +1 -1
- package/dist/src/ipc/workerspec.js +8 -0
- package/dist/src/ipc/workerspec.js.map +1 -1
- package/dist/src/ledgercheck.js +39 -10
- package/dist/src/ledgercheck.js.map +1 -1
- package/dist/src/live/publish.js +1 -1
- package/dist/src/live/publish.js.map +1 -1
- package/dist/src/livecli.js +1 -1
- package/dist/src/livecli.js.map +1 -1
- package/dist/src/loginrefresh/handover.js +121 -0
- package/dist/src/loginrefresh/handover.js.map +1 -0
- package/dist/src/loginrefresh/lock.js +54 -3
- package/dist/src/loginrefresh/lock.js.map +1 -1
- package/dist/src/loginrefresh/refresher.js +33 -5
- package/dist/src/loginrefresh/refresher.js.map +1 -1
- package/dist/src/loginrefresh/supervisor.js +18 -1
- package/dist/src/loginrefresh/supervisor.js.map +1 -1
- package/dist/src/npmverbs.js +42 -12
- package/dist/src/npmverbs.js.map +1 -1
- package/dist/src/onboard/choices.js +6 -3
- package/dist/src/onboard/choices.js.map +1 -1
- package/dist/src/onboard/engine.js +33 -292
- package/dist/src/onboard/engine.js.map +1 -1
- package/dist/src/onboard/steps/bringup.js +3 -2
- package/dist/src/onboard/steps/bringup.js.map +1 -1
- package/dist/src/onboard/steps/configfill.js +18 -7
- package/dist/src/onboard/steps/configfill.js.map +1 -1
- package/dist/src/onboard/steps/loginshare.js +213 -0
- package/dist/src/onboard/steps/loginshare.js.map +1 -0
- package/dist/src/onboard/steps/memory.js +14 -2
- package/dist/src/onboard/steps/memory.js.map +1 -1
- package/dist/src/onboard/steps/repoconfig.js +72 -0
- package/dist/src/onboard/steps/repoconfig.js.map +1 -0
- package/dist/src/onboard/toolnames.js +2 -2
- package/dist/src/onboard/toolnames.js.map +1 -1
- package/dist/src/piagentdir.js +13 -0
- package/dist/src/piagentdir.js.map +1 -0
- package/dist/src/pilogin.js +31 -16
- package/dist/src/pilogin.js.map +1 -1
- package/dist/src/qcrbin.js +16 -11
- package/dist/src/qcrbin.js.map +1 -1
- package/dist/src/reviewargs.js +122 -0
- package/dist/src/reviewargs.js.map +1 -0
- package/dist/src/reviewcli.js +7 -161
- package/dist/src/reviewcli.js.map +1 -1
- package/dist/src/reviewdetach.js +76 -11
- package/dist/src/reviewdetach.js.map +1 -1
- package/dist/src/scancli.js +39 -691
- package/dist/src/scancli.js.map +1 -1
- package/dist/src/selfimprove.js +66 -0
- package/dist/src/selfimprove.js.map +1 -0
- package/dist/src/session-command-supervisor.js +38 -9
- package/dist/src/session-command-supervisor.js.map +1 -1
- package/dist/src/setup/adapters.js +5 -223
- package/dist/src/setup/adapters.js.map +1 -1
- package/dist/src/setup/caserunner.js +15 -249
- package/dist/src/setup/caserunner.js.map +1 -1
- package/dist/src/setup/engine.js +32 -341
- package/dist/src/setup/engine.js.map +1 -1
- package/dist/src/setup/facts.js +3 -592
- package/dist/src/setup/facts.js.map +1 -1
- package/dist/src/setup/findings.js +3 -947
- package/dist/src/setup/findings.js.map +1 -1
- package/dist/src/setup/presentation.js +3 -8
- package/dist/src/setup/presentation.js.map +1 -1
- package/dist/src/setup/scan.js +3 -724
- package/dist/src/setup/scan.js.map +1 -1
- package/dist/src/setup/scanbuild.js +9 -92
- package/dist/src/setup/scanbuild.js.map +1 -1
- package/dist/src/setup/scanlease.js +3 -250
- package/dist/src/setup/scanlease.js.map +1 -1
- package/dist/src/setup/scanreport.js +3 -288
- package/dist/src/setup/scanreport.js.map +1 -1
- package/dist/src/setup/scanrun.js +3 -1732
- package/dist/src/setup/scanrun.js.map +1 -1
- package/dist/src/setup/scantree.js +3 -256
- package/dist/src/setup/scantree.js.map +1 -1
- package/dist/src/setup/stage2request.js +3 -258
- package/dist/src/setup/stage2request.js.map +1 -1
- package/dist/src/setup/unitcut.js +3 -436
- package/dist/src/setup/unitcut.js.map +1 -1
- package/dist/src/state.js +75 -9
- package/dist/src/state.js.map +1 -1
- package/dist/src/takeover.js +1 -0
- package/dist/src/takeover.js.map +1 -1
- package/dist/src/takeoverpi.js +2 -1
- package/dist/src/takeoverpi.js.map +1 -1
- package/dist/src/tui/alerts.js +4 -1
- package/dist/src/tui/alerts.js.map +1 -1
- package/dist/src/tui/board.js +2 -2
- package/dist/src/tui/board.js.map +1 -1
- package/dist/src/tui/companion.js +2 -1
- package/dist/src/tui/companion.js.map +1 -1
- package/dist/src/tui/deploytab.js +2 -2
- package/dist/src/tui/deploytab.js.map +1 -1
- package/dist/src/tui/index.js +9 -6
- package/dist/src/tui/index.js.map +1 -1
- package/dist/src/tui/memory.js +3 -16
- package/dist/src/tui/memory.js.map +1 -1
- package/dist/src/tui/memoryurl.js +22 -0
- package/dist/src/tui/memoryurl.js.map +1 -0
- package/dist/src/tui/remote.js +1 -1
- package/dist/src/tui/remote.js.map +1 -1
- package/dist/src/tui/route.js +2 -2
- package/dist/src/tui/route.js.map +1 -1
- package/dist/src/tui/setupstate.js +8 -5
- package/dist/src/tui/setupstate.js.map +1 -1
- package/dist/src/tui/sshtriage.js +1 -1
- package/dist/src/tui/sshtriage.js.map +1 -1
- package/dist/src/verdicts.js +12 -0
- package/dist/src/verdicts.js.map +1 -1
- package/dist/src/wire.js +9 -4
- package/dist/src/wire.js.map +1 -1
- package/dist/src/workerbreaker.js +32 -5
- package/dist/src/workerbreaker.js.map +1 -1
- package/dist/src/workerworld.js +4 -0
- package/dist/src/workerworld.js.map +1 -1
- package/docker/README.md +1 -1
- package/docker/entrypoint.sh +40 -6
- package/docker-compose.yml +16 -1
- package/extensions/cli/home.ts +14 -0
- package/extensions/cli/selfimprove.ts +18 -0
- package/extensions/home.ts +484 -0
- package/extensions/login-route.ts +1 -1
- package/extensions/onboarding.ts +8 -7
- package/extensions/selfimprove.ts +65 -0
- package/extensions/setup-scan.ts +79 -766
- package/extensions/setup.ts +14 -70
- package/extensions/vendor/openviking-pi/NOTICE +5 -0
- package/extensions/vendor/openviking-pi/README.md +39 -5
- package/extensions/vendor/openviking-pi/index.ts +4 -0
- package/extensions/vendor/openviking-pi/patches/0014-one-turn-sync-in-flight.patch +239 -0
- package/extensions/vendor/openviking-pi/qcr-hooks.ts +3 -1
- package/extensions/vendor/openviking-pi/sync.ts +80 -0
- package/extensions/vendor/openviking-pi/upstream.json +2 -1
- package/package.json +6 -4
- package/prompts/onboarding.md +8 -7
- package/schemas/review-result.v1.json +60 -0
- package/scripts/deploy.sh +8 -6
- package/scripts/qcr-host.sh +28 -5
- package/scripts/vm-ghcr-login.sh +7 -4
- package/src/archive/restore.ts +18 -21
- package/src/botworker.ts +13 -2
- package/src/channelfs.ts +3 -26
- package/src/cli.ts +13 -54
- package/src/config.ts +38 -0
- package/src/daemon.ts +34 -4
- package/src/debuground.ts +3 -0
- package/src/debugroundpi.ts +1 -0
- package/src/engine/caserunner-sdk.ts +29 -314
- package/src/home/adapters.ts +418 -0
- package/src/home/advanced.ts +421 -0
- package/src/home/advancedtools.ts +96 -0
- package/src/home/autoreview.ts +385 -0
- package/src/home/daemonstatus.ts +55 -0
- package/src/home/devicelogin.ts +107 -0
- package/src/home/launch.ts +257 -0
- package/src/home/loginowner.ts +248 -0
- package/src/home/memory.ts +176 -0
- package/src/home/model.ts +965 -0
- package/src/home/panel.ts +666 -0
- package/src/home/ports.ts +338 -0
- package/src/home/review.ts +206 -0
- package/src/home/reviews.ts +565 -0
- package/src/home/setup.ts +689 -0
- package/src/home/setupadapters.ts +217 -0
- package/src/home/setupcard.ts +288 -0
- package/src/home/setuptools.ts +57 -0
- package/src/home/spend.ts +194 -0
- package/src/home/switches.ts +293 -0
- package/src/home/switchon.ts +554 -0
- package/src/home/toolnames.ts +46 -0
- package/src/home/tune.ts +405 -0
- package/src/home/words.ts +8 -0
- package/src/homes.ts +17 -0
- package/src/install.ts +1 -1
- package/src/ipc/workerspec.ts +15 -0
- package/src/ledgercheck.ts +45 -9
- package/src/live/publish.ts +4 -1
- package/src/livecli.ts +6 -1
- package/src/loginrefresh/handover.ts +138 -0
- package/src/loginrefresh/lock.ts +72 -3
- package/src/loginrefresh/refresher.ts +34 -5
- package/src/loginrefresh/supervisor.ts +25 -2
- package/src/npmverbs.ts +51 -11
- package/src/onboard/choices.ts +6 -3
- package/src/onboard/engine.ts +64 -434
- package/src/onboard/steps/bringup.ts +3 -2
- package/src/onboard/steps/configfill.ts +23 -7
- package/src/onboard/steps/loginshare.ts +258 -0
- package/src/onboard/steps/memory.ts +18 -2
- package/src/onboard/steps/repoconfig.ts +85 -0
- package/src/onboard/toolnames.ts +2 -2
- package/src/piagentdir.ts +12 -0
- package/src/pilogin.ts +32 -16
- package/src/qcrbin.ts +16 -11
- package/src/reviewargs.ts +119 -0
- package/src/reviewcli.ts +6 -167
- package/src/reviewdetach.ts +107 -13
- package/src/scancli.ts +75 -826
- package/src/selfimprove.ts +90 -0
- package/src/session-command-supervisor.ts +41 -8
- package/src/setup/adapters.ts +13 -221
- package/src/setup/caserunner.ts +34 -273
- package/src/setup/engine.ts +62 -496
- package/src/setup/facts.ts +3 -627
- package/src/setup/findings.ts +3 -1264
- package/src/setup/presentation.ts +3 -18
- package/src/setup/scan.ts +3 -1033
- package/src/setup/scanbuild.ts +20 -114
- package/src/setup/scanlease.ts +3 -297
- package/src/setup/scanreport.ts +3 -373
- package/src/setup/scanrun.ts +3 -2268
- package/src/setup/scantree.ts +3 -259
- package/src/setup/stage2request.ts +3 -347
- package/src/setup/unitcut.ts +3 -523
- package/src/state.ts +126 -11
- package/src/takeover.ts +5 -0
- package/src/takeoverpi.ts +3 -2
- package/src/tui/alerts.ts +9 -2
- package/src/tui/board.ts +2 -2
- package/src/tui/companion.ts +2 -1
- package/src/tui/deploytab.ts +2 -2
- package/src/tui/index.ts +9 -6
- package/src/tui/memory.ts +3 -15
- package/src/tui/memoryurl.ts +22 -0
- package/src/tui/remote.ts +1 -1
- package/src/tui/route.ts +2 -2
- package/src/tui/setupstate.ts +20 -16
- package/src/tui/sshtriage.ts +1 -1
- package/src/verdicts.ts +13 -0
- package/src/wire.ts +9 -4
- package/src/workerbreaker.ts +41 -5
- package/src/workerworld.ts +9 -1
- package/dist/src/live/projection.js +0 -450
- package/dist/src/live/projection.js.map +0 -1
- package/dist/src/live/read.js +0 -60
- package/dist/src/live/read.js.map +0 -1
- package/dist/src/onboard/launch.js +0 -75
- package/dist/src/onboard/launch.js.map +0 -1
- package/dist/src/setup/ledger.js +0 -207
- package/dist/src/setup/ledger.js.map +0 -1
- package/dist/src/setup/stage2.js +0 -617
- package/dist/src/setup/stage2.js.map +0 -1
- package/dist/src/setup/textsniff.js +0 -89
- package/dist/src/setup/textsniff.js.map +0 -1
- package/src/live/projection.ts +0 -491
- package/src/live/read.ts +0 -86
- package/src/onboard/launch.ts +0 -103
- package/src/setup/ledger.ts +0 -324
- package/src/setup/stage2.ts +0 -926
- package/src/setup/textsniff.ts +0 -95
|
@@ -1,437 +1,4 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
|
|
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
|
|
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
|
? {
|