@tokenfactory/acc-runner 0.44.0 → 0.44.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 +43 -1
- package/dist/bin-resolve.d.ts +39 -3
- package/dist/bin-resolve.d.ts.map +1 -1
- package/dist/bin-resolve.js +176 -12
- package/dist/bin-resolve.js.map +1 -1
- package/dist/build-fingerprint.d.ts +66 -0
- package/dist/build-fingerprint.d.ts.map +1 -0
- package/dist/build-fingerprint.js +70 -0
- package/dist/build-fingerprint.js.map +1 -0
- package/dist/build-info.generated.d.ts +3 -0
- package/dist/build-info.generated.d.ts.map +1 -0
- package/dist/build-info.generated.js +3 -0
- package/dist/build-info.generated.js.map +1 -0
- package/dist/capacity/resume-controller.d.ts +5 -3
- package/dist/capacity/resume-controller.d.ts.map +1 -1
- package/dist/capacity/resume-controller.js +9 -0
- package/dist/capacity/resume-controller.js.map +1 -1
- package/dist/chat-delegation-guard.d.ts +23 -0
- package/dist/chat-delegation-guard.d.ts.map +1 -0
- package/dist/chat-delegation-guard.js +62 -0
- package/dist/chat-delegation-guard.js.map +1 -0
- package/dist/chat-lane.d.ts +17 -6
- package/dist/chat-lane.d.ts.map +1 -1
- package/dist/chat-lane.js +29 -8
- package/dist/chat-lane.js.map +1 -1
- package/dist/companion-identity.d.ts +37 -0
- package/dist/companion-identity.d.ts.map +1 -1
- package/dist/companion-identity.js +98 -29
- package/dist/companion-identity.js.map +1 -1
- package/dist/companion-run.d.ts +1 -50
- package/dist/companion-run.d.ts.map +1 -1
- package/dist/companion-run.js +48 -105
- package/dist/companion-run.js.map +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +14 -3
- package/dist/config.js.map +1 -1
- package/dist/conformance.d.ts +49 -0
- package/dist/conformance.d.ts.map +1 -0
- package/dist/conformance.js +121 -0
- package/dist/conformance.js.map +1 -0
- package/dist/cost-pricing.d.ts +91 -0
- package/dist/cost-pricing.d.ts.map +1 -1
- package/dist/cost-pricing.js +115 -1
- package/dist/cost-pricing.js.map +1 -1
- package/dist/credential-identity.d.ts +70 -0
- package/dist/credential-identity.d.ts.map +1 -0
- package/dist/credential-identity.js +128 -0
- package/dist/credential-identity.js.map +1 -0
- package/dist/doctor.d.ts +37 -1
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +300 -41
- package/dist/doctor.js.map +1 -1
- package/dist/engines/chat-stream.d.ts +71 -1
- package/dist/engines/chat-stream.d.ts.map +1 -1
- package/dist/engines/chat-stream.js +126 -8
- package/dist/engines/chat-stream.js.map +1 -1
- package/dist/engines/claude-code-chat.d.ts +11 -1
- package/dist/engines/claude-code-chat.d.ts.map +1 -1
- package/dist/engines/claude-code-chat.js +69 -5
- package/dist/engines/claude-code-chat.js.map +1 -1
- package/dist/engines/claude-code.d.ts.map +1 -1
- package/dist/engines/claude-code.js +30 -6
- package/dist/engines/claude-code.js.map +1 -1
- package/dist/engines/codex.d.ts.map +1 -1
- package/dist/engines/codex.js +18 -5
- package/dist/engines/codex.js.map +1 -1
- package/dist/engines/registry.d.ts +26 -0
- package/dist/engines/registry.d.ts.map +1 -1
- package/dist/engines/registry.js +37 -0
- package/dist/engines/registry.js.map +1 -1
- package/dist/engines/types.d.ts +5 -0
- package/dist/engines/types.d.ts.map +1 -1
- package/dist/failure-classifier.d.ts +1 -1
- package/dist/failure-classifier.d.ts.map +1 -1
- package/dist/failure-classifier.js +65 -12
- package/dist/failure-classifier.js.map +1 -1
- package/dist/gh.d.ts +15 -0
- package/dist/gh.d.ts.map +1 -1
- package/dist/gh.js +18 -0
- package/dist/gh.js.map +1 -1
- package/dist/git.d.ts +6 -0
- package/dist/git.d.ts.map +1 -1
- package/dist/git.js +27 -1
- package/dist/git.js.map +1 -1
- package/dist/instance.d.ts +30 -0
- package/dist/instance.d.ts.map +1 -0
- package/dist/instance.js +51 -0
- package/dist/instance.js.map +1 -0
- package/dist/keychain.d.ts +20 -7
- package/dist/keychain.d.ts.map +1 -1
- package/dist/keychain.js +40 -4
- package/dist/keychain.js.map +1 -1
- package/dist/loc-advisory.d.ts +70 -0
- package/dist/loc-advisory.d.ts.map +1 -0
- package/dist/loc-advisory.js +196 -0
- package/dist/loc-advisory.js.map +1 -0
- package/dist/locks/claim-lock-policy.d.ts +67 -0
- package/dist/locks/claim-lock-policy.d.ts.map +1 -0
- package/dist/locks/claim-lock-policy.js +132 -0
- package/dist/locks/claim-lock-policy.js.map +1 -0
- package/dist/login.d.ts +63 -2
- package/dist/login.d.ts.map +1 -1
- package/dist/login.js +93 -8
- package/dist/login.js.map +1 -1
- package/dist/mcp-spawn.d.ts +54 -0
- package/dist/mcp-spawn.d.ts.map +1 -1
- package/dist/mcp-spawn.js +88 -4
- package/dist/mcp-spawn.js.map +1 -1
- package/dist/memory-context.d.ts +106 -0
- package/dist/memory-context.d.ts.map +1 -0
- package/dist/memory-context.js +220 -0
- package/dist/memory-context.js.map +1 -0
- package/dist/memory-curation.d.ts +172 -0
- package/dist/memory-curation.d.ts.map +1 -0
- package/dist/memory-curation.js +277 -0
- package/dist/memory-curation.js.map +1 -0
- package/dist/migration-seq-guard.d.ts +28 -1
- package/dist/migration-seq-guard.d.ts.map +1 -1
- package/dist/migration-seq-guard.js +111 -6
- package/dist/migration-seq-guard.js.map +1 -1
- package/dist/output-diagnostics.d.ts +15 -0
- package/dist/output-diagnostics.d.ts.map +1 -0
- package/dist/output-diagnostics.js +95 -0
- package/dist/output-diagnostics.js.map +1 -0
- package/dist/pr-open-guard.d.ts +67 -0
- package/dist/pr-open-guard.d.ts.map +1 -0
- package/dist/pr-open-guard.js +79 -0
- package/dist/pr-open-guard.js.map +1 -0
- package/dist/prepr-gate.d.ts +229 -1
- package/dist/prepr-gate.d.ts.map +1 -1
- package/dist/prepr-gate.js +406 -3
- package/dist/prepr-gate.js.map +1 -1
- package/dist/program.d.ts.map +1 -1
- package/dist/program.js +43 -5
- package/dist/program.js.map +1 -1
- package/dist/provider-auth.d.ts +27 -0
- package/dist/provider-auth.d.ts.map +1 -1
- package/dist/provider-auth.js +33 -0
- package/dist/provider-auth.js.map +1 -1
- package/dist/runtime/cache-dir.d.ts.map +1 -1
- package/dist/runtime/cache-dir.js +8 -3
- package/dist/runtime/cache-dir.js.map +1 -1
- package/dist/runtime/decision-provenance.d.ts +63 -0
- package/dist/runtime/decision-provenance.d.ts.map +1 -0
- package/dist/runtime/decision-provenance.js +107 -0
- package/dist/runtime/decision-provenance.js.map +1 -0
- package/dist/runtime/prompt-substitute.d.ts +100 -0
- package/dist/runtime/prompt-substitute.d.ts.map +1 -0
- package/dist/runtime/prompt-substitute.js +124 -0
- package/dist/runtime/prompt-substitute.js.map +1 -0
- package/dist/runtime/quarantine.d.ts +11 -0
- package/dist/runtime/quarantine.d.ts.map +1 -1
- package/dist/runtime/quarantine.js +16 -1
- package/dist/runtime/quarantine.js.map +1 -1
- package/dist/runtime/review-cost-cap.d.ts +167 -0
- package/dist/runtime/review-cost-cap.d.ts.map +1 -0
- package/dist/runtime/review-cost-cap.js +182 -0
- package/dist/runtime/review-cost-cap.js.map +1 -0
- package/dist/runtime/reviewer-abstain.d.ts +101 -0
- package/dist/runtime/reviewer-abstain.d.ts.map +1 -0
- package/dist/runtime/reviewer-abstain.js +138 -0
- package/dist/runtime/reviewer-abstain.js.map +1 -0
- package/dist/runtime/reviewer.d.ts +165 -2
- package/dist/runtime/reviewer.d.ts.map +1 -1
- package/dist/runtime/reviewer.js +752 -111
- package/dist/runtime/reviewer.js.map +1 -1
- package/dist/runtime/running-build.d.ts +24 -0
- package/dist/runtime/running-build.d.ts.map +1 -0
- package/dist/runtime/running-build.js +62 -0
- package/dist/runtime/running-build.js.map +1 -0
- package/dist/runtime/singleton.d.ts.map +1 -1
- package/dist/runtime/singleton.js +9 -1
- package/dist/runtime/singleton.js.map +1 -1
- package/dist/runtime/worktree.d.ts +45 -0
- package/dist/runtime/worktree.d.ts.map +1 -1
- package/dist/runtime/worktree.js +174 -5
- package/dist/runtime/worktree.js.map +1 -1
- package/dist/serving-auth/index.d.ts +79 -0
- package/dist/serving-auth/index.d.ts.map +1 -0
- package/dist/serving-auth/index.js +169 -0
- package/dist/serving-auth/index.js.map +1 -0
- package/dist/task-runner.d.ts +134 -5
- package/dist/task-runner.d.ts.map +1 -1
- package/dist/task-runner.js +1255 -129
- package/dist/task-runner.js.map +1 -1
- package/dist/types.d.ts +8 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/watch-chat/wire.d.ts +80 -0
- package/dist/watch-chat/wire.d.ts.map +1 -1
- package/dist/watch-chat/wire.js +203 -43
- package/dist/watch-chat/wire.js.map +1 -1
- package/dist/watch.d.ts +70 -16
- package/dist/watch.d.ts.map +1 -1
- package/dist/watch.js +548 -108
- package/dist/watch.js.map +1 -1
- package/package.json +4 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt-substitute.d.ts","sourceRoot":"","sources":["../../src/runtime/prompt-substitute.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH;;;;;;;;;GASG;AACH,eAAO,MAAM,+BAA+B,mCAAoC,CAAC;AAEjF;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAGjF;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC/B,MAAM,CAGR;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,aAAa,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC,GACjE,MAAM,CAMR;AAED,4DAA4D;AAC5D,MAAM,WAAW,iBAAiB;IAChC,sEAAsE;IACtE,WAAW,EAAE,MAAM,CAAC;IACpB,iFAAiF;IACjF,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,aAAa,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC,GACjE,iBAAiB,EAAE,CAarB;AAED,sFAAsF;AACtF,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,iBAAiB,EAAE,GAAG,MAAM,CAI7E"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* S15-FU/REVIEW-1 (AC1) — literal-safe reviewer-prompt placeholder substitution.
|
|
3
|
+
*
|
|
4
|
+
* THE FAULT THIS CLOSES (the LEDGER-1 #1933 episode)
|
|
5
|
+
* ----------------------------------------------------------------------------
|
|
6
|
+
* `renderReviewerPrompt` interpolated the PR diff into the reviewer prompt with
|
|
7
|
+
*
|
|
8
|
+
* template.replace("{{pr_diff}}", diff)
|
|
9
|
+
*
|
|
10
|
+
* `String.prototype.replace` treats a STRING replacement as a pattern: `$$`,
|
|
11
|
+
* `$&`, `` $` ``, `$'` and `$1`…`$9` are expanded, not copied. So a diff whose
|
|
12
|
+
* text contains `$'` — which every one of these clean SQL literals does —
|
|
13
|
+
*
|
|
14
|
+
* select seq, slug, sha from acc.applied_migrations where seq ~ '^[0-9]+$' order by 1
|
|
15
|
+
* ^^ $'
|
|
16
|
+
*
|
|
17
|
+
* made `replace` splice in "the portion of the template AFTER the match": the
|
|
18
|
+
* reviewer's OWN instruction text, starting at
|
|
19
|
+
* "Now answer in this exact JSON shape, with no surrounding prose:". The
|
|
20
|
+
* reviewer then read that prose INSIDE the SQL literal and — correctly, given
|
|
21
|
+
* what it was shown — reported "prose spliced into the literal" as a corruption
|
|
22
|
+
* defect, at 0.75–0.85 confidence, across several independent sessions. The
|
|
23
|
+
* diff on disk was clean the whole time; the harness corrupted it on the way in.
|
|
24
|
+
*
|
|
25
|
+
* (Same root cause as the 3× `gatherAppliedLedger` corruption in
|
|
26
|
+
* `scripts/ci/migration-lint.mjs`: `$'` expansion in a string replacement, not
|
|
27
|
+
* context bleed.)
|
|
28
|
+
*
|
|
29
|
+
* THE RULE: NEVER pass untrusted text as `replace`'s string replacement. A
|
|
30
|
+
* FUNCTION replacement is copied verbatim — `$` has no meaning in its return
|
|
31
|
+
* value — so `substitutePlaceholder` below is the only sanctioned way to put a
|
|
32
|
+
* diff, PR body, title, description, or acceptance criterion into a prompt.
|
|
33
|
+
*
|
|
34
|
+
* Mirrored, byte-for-byte in behaviour, from
|
|
35
|
+
* `api/_lib/reviewer/prompt-substitute.ts` (the server path). The runner ships as
|
|
36
|
+
* a published npm package and cannot import from `api/_lib`, so the module is
|
|
37
|
+
* duplicated and pinned by a cross-implementation parity test.
|
|
38
|
+
* // keep in sync with api/_lib/reviewer/prompt-substitute.ts
|
|
39
|
+
*/
|
|
40
|
+
/**
|
|
41
|
+
* The `$`-sequences `String.prototype.replace` expands inside a STRING
|
|
42
|
+
* replacement. Documented here because the whole module exists to defeat them.
|
|
43
|
+
*
|
|
44
|
+
* `$$` → a literal `$`
|
|
45
|
+
* `$&` → the matched substring
|
|
46
|
+
* ``$` `` → everything BEFORE the match
|
|
47
|
+
* `$'` → everything AFTER the match ← the #1933 corruption
|
|
48
|
+
* `$n` → capture group n
|
|
49
|
+
*/
|
|
50
|
+
export const REPLACEMENT_EXPANSION_SEQUENCES = ["$$", "$&", "$`", "$'"];
|
|
51
|
+
/**
|
|
52
|
+
* True when `value` contains a sequence that `String.prototype.replace` would
|
|
53
|
+
* expand if it were used as a string replacement. Diagnostic only — the
|
|
54
|
+
* substitution below is safe either way — but it lets the render seam log that a
|
|
55
|
+
* value was in the dangerous class without ever emitting the value itself.
|
|
56
|
+
*/
|
|
57
|
+
export function hasReplacementExpansion(value) {
|
|
58
|
+
const text = value ?? "";
|
|
59
|
+
return /\$[$&`'\d]/.test(text);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Substitute the FIRST occurrence of `placeholder` with `value`, VERBATIM.
|
|
63
|
+
*
|
|
64
|
+
* The replacement is a function, so no `$`-sequence in `value` is expanded and
|
|
65
|
+
* no template text can bleed into it. An absent placeholder is a no-op (the
|
|
66
|
+
* template is returned unchanged) — same as the string form it replaces.
|
|
67
|
+
*/
|
|
68
|
+
export function substitutePlaceholder(template, placeholder, value) {
|
|
69
|
+
const text = value ?? "";
|
|
70
|
+
return (template ?? "").replace(placeholder, () => text);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Apply an ORDERED list of `[placeholder, value]` pairs. Order matters only in
|
|
74
|
+
* that each pass sees the output of the previous one; because values are
|
|
75
|
+
* inserted verbatim, a value that happens to contain a later placeholder's
|
|
76
|
+
* literal text (`"{{pr_diff}}"` inside a PR body, say) is NOT re-substituted
|
|
77
|
+
* away — the later pass still matches the TEMPLATE's own placeholder first,
|
|
78
|
+
* since it appears earlier in the string only if the template put it there.
|
|
79
|
+
*/
|
|
80
|
+
export function substitutePlaceholders(template, pairs) {
|
|
81
|
+
let out = template ?? "";
|
|
82
|
+
for (const [placeholder, value] of pairs) {
|
|
83
|
+
out = substitutePlaceholder(out, placeholder, value);
|
|
84
|
+
}
|
|
85
|
+
return out;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* INTEGRITY CHECK: every value substituted for a placeholder THE TEMPLATE
|
|
89
|
+
* ACTUALLY CONTAINS must appear VERBATIM in the rendered output. Anything else
|
|
90
|
+
* means the render mangled file content — the #1933 class — and the review must
|
|
91
|
+
* NOT proceed on that prompt (the reviewer would be judging text the repo does
|
|
92
|
+
* not contain).
|
|
93
|
+
*
|
|
94
|
+
* `template` is required precisely so a placeholder the template OMITS is not
|
|
95
|
+
* reported: a prompt that never asks for the PR body was not corrupted by the
|
|
96
|
+
* body's absence, it simply does not carry it. Only a value the template asked
|
|
97
|
+
* for and did not receive verbatim is a bleed.
|
|
98
|
+
*
|
|
99
|
+
* Pure and allocation-cheap (`includes`, no regex). Values are never echoed into
|
|
100
|
+
* the result, so a bleed report is safe to log.
|
|
101
|
+
*/
|
|
102
|
+
export function findPromptRenderBleed(template, rendered, pairs) {
|
|
103
|
+
const out = [];
|
|
104
|
+
const tpl = template ?? "";
|
|
105
|
+
const text = rendered ?? "";
|
|
106
|
+
for (const [placeholder, value] of pairs) {
|
|
107
|
+
const expected = value ?? "";
|
|
108
|
+
if (expected.length === 0)
|
|
109
|
+
continue;
|
|
110
|
+
if (!tpl.includes(placeholder))
|
|
111
|
+
continue;
|
|
112
|
+
if (!text.includes(expected)) {
|
|
113
|
+
out.push({ placeholder, expected_chars: expected.length });
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return out;
|
|
117
|
+
}
|
|
118
|
+
/** Operator-facing one-liner naming the corrupted placeholders (never the values). */
|
|
119
|
+
export function describePromptRenderBleed(bleeds) {
|
|
120
|
+
return bleeds
|
|
121
|
+
.map((b) => `${b.placeholder} (${b.expected_chars} chars did not survive verbatim)`)
|
|
122
|
+
.join("; ");
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=prompt-substitute.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt-substitute.js","sourceRoot":"","sources":["../../src/runtime/prompt-substitute.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAU,CAAC;AAEjF;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CAAC,KAAgC;IACtE,MAAM,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;IACzB,OAAO,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACjC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CACnC,QAAgB,EAChB,WAAmB,EACnB,KAAgC;IAEhC,MAAM,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;IACzB,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;AAC3D,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,sBAAsB,CACpC,QAAgB,EAChB,KAAkE;IAElE,IAAI,GAAG,GAAG,QAAQ,IAAI,EAAE,CAAC;IACzB,KAAK,MAAM,CAAC,WAAW,EAAE,KAAK,CAAC,IAAI,KAAK,EAAE,CAAC;QACzC,GAAG,GAAG,qBAAqB,CAAC,GAAG,EAAE,WAAW,EAAE,KAAK,CAAC,CAAC;IACvD,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAUD;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,qBAAqB,CACnC,QAAgB,EAChB,QAAgB,EAChB,KAAkE;IAElE,MAAM,GAAG,GAAwB,EAAE,CAAC;IACpC,MAAM,GAAG,GAAG,QAAQ,IAAI,EAAE,CAAC;IAC3B,MAAM,IAAI,GAAG,QAAQ,IAAI,EAAE,CAAC;IAC5B,KAAK,MAAM,CAAC,WAAW,EAAE,KAAK,CAAC,IAAI,KAAK,EAAE,CAAC;QACzC,MAAM,QAAQ,GAAG,KAAK,IAAI,EAAE,CAAC;QAC7B,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QACpC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC;YAAE,SAAS;QACzC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7B,GAAG,CAAC,IAAI,CAAC,EAAE,WAAW,EAAE,cAAc,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,yBAAyB,CAAC,MAA2B;IACnE,OAAO,MAAM;SACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,WAAW,KAAK,CAAC,CAAC,cAAc,kCAAkC,CAAC;SACnF,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC"}
|
|
@@ -4,6 +4,10 @@ export interface QuarantineState {
|
|
|
4
4
|
classifiedAt: string;
|
|
5
5
|
taskId: string;
|
|
6
6
|
detail: string;
|
|
7
|
+
engineScope?: string;
|
|
8
|
+
engine_scope?: string;
|
|
9
|
+
resumeAt?: string | null;
|
|
10
|
+
resume_at?: string | null;
|
|
7
11
|
/**
|
|
8
12
|
* v0.53 T-53-4: how many consecutive instant-empty (heuristic) claude
|
|
9
13
|
* exits triggered the quarantine. Cache-schema additive — older readers
|
|
@@ -15,6 +19,13 @@ export interface QuarantineState {
|
|
|
15
19
|
*/
|
|
16
20
|
consecutive?: number;
|
|
17
21
|
}
|
|
22
|
+
export interface PublishedQuarantineState {
|
|
23
|
+
entered_at: string;
|
|
24
|
+
reason: ClaudeExitClass;
|
|
25
|
+
engine_scope: string;
|
|
26
|
+
resume_at: string | null;
|
|
27
|
+
}
|
|
28
|
+
export declare function publishedQuarantineState(state: QuarantineState | null, nowMs?: number): PublishedQuarantineState | null;
|
|
18
29
|
/**
|
|
19
30
|
* Path to the quarantine sentinel file. Resolves through the shared
|
|
20
31
|
* `cacheDir()` (honours ACC_RUNNER_CACHE_DIR), so a per-runner cache dir
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"quarantine.d.ts","sourceRoot":"","sources":["../../src/runtime/quarantine.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"quarantine.d.ts","sourceRoot":"","sources":["../../src/runtime/quarantine.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAEhE,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,eAAe,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B;;;;;;;;OAQG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,wBAAwB;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,eAAe,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,eAAe,GAAG,IAAI,EAC7B,KAAK,GAAE,MAAmB,GACzB,wBAAwB,GAAG,IAAI,CAUjC;AAED;;;;GAIG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,0EAA0E;AAC1E,wBAAsB,aAAa,IAAI,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAQrE;AAED,oEAAoE;AACpE,wBAAsB,aAAa,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAIzE;AAED,yEAAyE;AACzE,wBAAsB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAMrD"}
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
* ~/.cache/acc-runner/quarantine.json by the watch loop when the failure
|
|
4
4
|
* classifier flags a machine-level failure (usage_limit | auth_expired |
|
|
5
5
|
* env_broken). The presence of this file blocks pump() from claiming
|
|
6
|
-
* new tasks so no further work is burned.
|
|
6
|
+
* new tasks so no further work is burned. The live watcher periodically
|
|
7
|
+
* re-reads this sentinel, so removing it resumes that named instance without a
|
|
8
|
+
* process restart.
|
|
7
9
|
*
|
|
8
10
|
* Clearing the quarantine (after the operator resolves the underlying issue):
|
|
9
11
|
* - Delete the file manually, OR
|
|
@@ -14,6 +16,19 @@
|
|
|
14
16
|
import fs from "node:fs/promises";
|
|
15
17
|
import path from "node:path";
|
|
16
18
|
import { cacheDir } from "./cache-dir.js";
|
|
19
|
+
export function publishedQuarantineState(state, nowMs = Date.now()) {
|
|
20
|
+
if (!state)
|
|
21
|
+
return null;
|
|
22
|
+
const resumeAt = state.resume_at ?? state.resumeAt ?? null;
|
|
23
|
+
if (resumeAt !== null && Date.parse(resumeAt) <= nowMs)
|
|
24
|
+
return null;
|
|
25
|
+
return {
|
|
26
|
+
entered_at: state.classifiedAt,
|
|
27
|
+
reason: state.cause,
|
|
28
|
+
engine_scope: state.engine_scope ?? state.engineScope ?? "all",
|
|
29
|
+
resume_at: resumeAt,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
17
32
|
/**
|
|
18
33
|
* Path to the quarantine sentinel file. Resolves through the shared
|
|
19
34
|
* `cacheDir()` (honours ACC_RUNNER_CACHE_DIR), so a per-runner cache dir
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"quarantine.js","sourceRoot":"","sources":["../../src/runtime/quarantine.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"quarantine.js","sourceRoot":"","sources":["../../src/runtime/quarantine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AA+B1C,MAAM,UAAU,wBAAwB,CACtC,KAA6B,EAC7B,QAAgB,IAAI,CAAC,GAAG,EAAE;IAE1B,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,MAAM,QAAQ,GAAG,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,QAAQ,IAAI,IAAI,CAAC;IAC3D,IAAI,QAAQ,KAAK,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,KAAK;QAAE,OAAO,IAAI,CAAC;IACpE,OAAO;QACL,UAAU,EAAE,KAAK,CAAC,YAAY;QAC9B,MAAM,EAAE,KAAK,CAAC,KAAK;QACnB,YAAY,EAAE,KAAK,CAAC,YAAY,IAAI,KAAK,CAAC,WAAW,IAAI,KAAK;QAC9D,SAAS,EAAE,QAAQ;KACpB,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc;IAC5B,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,iBAAiB,CAAC,CAAC;AAClD,CAAC;AAED,0EAA0E;AAC1E,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,cAAc,EAAE,EAAE,MAAM,CAAC,CAAC;QACxD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAoB,CAAC;IAC5C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QAClE,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC;AAED,oEAAoE;AACpE,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAAsB;IACxD,MAAM,IAAI,GAAG,cAAc,EAAE,CAAC;IAC9B,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACxD,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;AACnE,CAAC;AAED,yEAAyE;AACzE,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC;IACpC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;YAAE,MAAM,GAAG,CAAC;IAClE,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* REV-COST — runner-side per-review cost cap (size-scaled).
|
|
3
|
+
*
|
|
4
|
+
* THE EPISODE (PR #1937). The runner reviewer applied the org's FLAT
|
|
5
|
+
* `max_cost_usd_per_review` and, the instant the spend crossed it, discarded the
|
|
6
|
+
* review with `cost_cap_exceeded`. #1937 is a ~1800-line PR: it cost ~$18 against
|
|
7
|
+
* an $8 flat cap and produced NO verdict at all — the money was spent, the
|
|
8
|
+
* analysis was done, and the harness threw both away, then re-charged the same
|
|
9
|
+
* review on the next tick. That is the measured 239-decisions-over-9-tasks loop
|
|
10
|
+
* AU-5 fixed on the SERVER path (api/_lib/cost-cap.ts) and left unfixed here,
|
|
11
|
+
* which is why a large PR could still be stranded whenever a runner picked the
|
|
12
|
+
* broadcast up (the normal case — the server path is the emergency fallback).
|
|
13
|
+
*
|
|
14
|
+
* This module is the runner's copy of AU-5's PURE cost-cap core:
|
|
15
|
+
*
|
|
16
|
+
* • `effectiveReviewCostCap` — the cap for THIS review, scaled by assembled
|
|
17
|
+
* diff size, FLOORED at the org's flat cap (so a median PR is judged against
|
|
18
|
+
* exactly today's number — never inflated) and CLAMPED at
|
|
19
|
+
* REVIEW_CAP_MAX_MULTIPLE.
|
|
20
|
+
* • `foldReviewCostLedger` — the chargeable-vs-infra split. Only PRODUCTIVE
|
|
21
|
+
* attempts (a spawn that returned a readable reviewer envelope) are charged
|
|
22
|
+
* to the cap; capacity/auth/tooling faults are reported but never charged.
|
|
23
|
+
* • `classifyReviewCost` — within / overrun / runaway. `overrun` keeps the
|
|
24
|
+
* verdict (the money is already spent; discarding it only strands the PR);
|
|
25
|
+
* `runaway` is the hard backstop that still refuses.
|
|
26
|
+
*
|
|
27
|
+
* The runner is a standalone published package and cannot import from api/_lib
|
|
28
|
+
* (the same convention as the duplicated PRICING / MODEL_ALIASES maps and
|
|
29
|
+
* review-diff-guard.ts). KEEP IN SYNC with the AU-5 block at the bottom of
|
|
30
|
+
* api/_lib/cost-cap.ts — the constants and all three functions are pinned
|
|
31
|
+
* byte-for-behaviour by tests/unit/revcost1-runner-cap-parity.test.ts, which
|
|
32
|
+
* fails the build the moment the two drift.
|
|
33
|
+
*/
|
|
34
|
+
/**
|
|
35
|
+
* Assembled-diff size the flat cap is treated as calibrated for: the measured
|
|
36
|
+
* p50 production churn (750 changed lines ≈ 30k chars of unified diff). At or
|
|
37
|
+
* below this the effective cap is EXACTLY `max_cost_usd_per_review`.
|
|
38
|
+
* Mirrors api/_lib/cost-cap.ts. // keep in sync
|
|
39
|
+
*/
|
|
40
|
+
export declare const REVIEW_CAP_REFERENCE_DIFF_CHARS = 30000;
|
|
41
|
+
/** Fraction of the base cap added per reference-diff-worth of EXCESS size. */
|
|
42
|
+
export declare const REVIEW_CAP_SIZE_SLOPE = 0.5;
|
|
43
|
+
/**
|
|
44
|
+
* Ceiling on the scaled cap as a multiple of the base cap. Deliberately tight
|
|
45
|
+
* (1.5× → $3.00 at a $2 base) because the measured size↔cost correlation is
|
|
46
|
+
* weak (Pearson r = 0.100): a bigger ramp would be unjustified by the data.
|
|
47
|
+
*/
|
|
48
|
+
export declare const REVIEW_CAP_MAX_MULTIPLE = 1.5;
|
|
49
|
+
/**
|
|
50
|
+
* Multiple of the effective cap above which an already-PAID review is still
|
|
51
|
+
* discarded as a runaway — the hard backstop that survives this change. Between
|
|
52
|
+
* 1× and this, the verdict is KEPT and flagged (`cost_overrun`) instead of
|
|
53
|
+
* stranding the PR.
|
|
54
|
+
*/
|
|
55
|
+
export declare const REVIEW_COST_OVERRUN_TOLERANCE = 3;
|
|
56
|
+
export interface ReviewCostCapDecision {
|
|
57
|
+
/** The cap to enforce for THIS review. */
|
|
58
|
+
cap_usd: number;
|
|
59
|
+
/** The org's flat `max_cost_usd_per_review` — the floor. */
|
|
60
|
+
base_cap_usd: number;
|
|
61
|
+
/** false when the base cap is 0/negative/non-finite (cap disabled). */
|
|
62
|
+
enabled: boolean;
|
|
63
|
+
/** Assembled-diff chars the scale was computed from. */
|
|
64
|
+
diff_chars: number;
|
|
65
|
+
/** cap_usd / base_cap_usd — 1 when the flat floor applied. */
|
|
66
|
+
scale: number;
|
|
67
|
+
/** True when the size ramp hit REVIEW_CAP_MAX_MULTIPLE. */
|
|
68
|
+
ceiling_applied: boolean;
|
|
69
|
+
/** Spend above which an already-paid verdict is discarded as a runaway. */
|
|
70
|
+
runaway_usd: number;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* The size-scaled per-review cost cap. Monotone non-decreasing in `diffChars`,
|
|
74
|
+
* floored at `baseCapUsd` (so it can only ever LOOSEN relative to the flat cap —
|
|
75
|
+
* a small PR's effective cap is unchanged) and clamped at
|
|
76
|
+
* `REVIEW_CAP_MAX_MULTIPLE × baseCapUsd`. Pure.
|
|
77
|
+
*
|
|
78
|
+
* A base cap of 0 (or a non-finite/negative one) means "cap disabled" — the
|
|
79
|
+
* existing `max_cost_usd_per_review > 0` semantics — and is returned verbatim.
|
|
80
|
+
*
|
|
81
|
+
* Mirrors api/_lib/cost-cap.ts `effectiveReviewCostCap`. // keep in sync
|
|
82
|
+
*/
|
|
83
|
+
export declare function effectiveReviewCostCap(baseCapUsd: number, diffChars: number): ReviewCostCapDecision;
|
|
84
|
+
/**
|
|
85
|
+
* How one reviewer attempt is classified for BUDGET purposes. Only `productive`
|
|
86
|
+
* attempts (a spawn that returned a reviewer envelope we can read a verdict out
|
|
87
|
+
* of) are charged to the per-review cap. Every other class is an INFRA fault: it
|
|
88
|
+
* is still reported in the review's total spend so the accounting stays honest,
|
|
89
|
+
* but it must never consume the budget set aside for reviewing this PR.
|
|
90
|
+
*
|
|
91
|
+
* Mirrors api/_lib/cost-cap.ts `ReviewAttemptClass`. // keep in sync
|
|
92
|
+
*/
|
|
93
|
+
export type ReviewAttemptClass = "productive"
|
|
94
|
+
/** Parse-repair re-prompt — a harness cost, not review work. */
|
|
95
|
+
| "parse_repair"
|
|
96
|
+
/** 429 / overloaded / rate-limit brownout (budget-exempt). */
|
|
97
|
+
| "infra_capacity"
|
|
98
|
+
/** Hard billing/quota wall. */
|
|
99
|
+
| "infra_billing"
|
|
100
|
+
/** Account-level spend cap. */
|
|
101
|
+
| "infra_billing_capped"
|
|
102
|
+
/** Missing/invalid credential — env broken. */
|
|
103
|
+
| "infra_auth"
|
|
104
|
+
/** Verification-tooling failure (unread file / truncated diff). */
|
|
105
|
+
| "infra_tooling";
|
|
106
|
+
export interface ReviewCostAttempt {
|
|
107
|
+
/** 1-based attempt ordinal within this review. */
|
|
108
|
+
attempt: number;
|
|
109
|
+
model_id: string;
|
|
110
|
+
attempt_class: ReviewAttemptClass;
|
|
111
|
+
/** Spend this attempt actually incurred (0 for a throw before any tokens). */
|
|
112
|
+
cost_usd: number;
|
|
113
|
+
}
|
|
114
|
+
export interface ReviewCostLedger {
|
|
115
|
+
attempts: ReviewCostAttempt[];
|
|
116
|
+
/** Sum of PRODUCTIVE attempt cost — the only figure the cap is checked against. */
|
|
117
|
+
chargeable_usd: number;
|
|
118
|
+
/** Sum of every non-productive attempt cost (reported, never charged). */
|
|
119
|
+
infra_usd: number;
|
|
120
|
+
/** chargeable + infra — what the review truly cost (stored on the queue row). */
|
|
121
|
+
total_usd: number;
|
|
122
|
+
productive_attempts: number;
|
|
123
|
+
infra_attempts: number;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Fold an attempt list into the chargeable / infra split. Pure.
|
|
127
|
+
* Mirrors api/_lib/cost-cap.ts `foldReviewCostLedger`. // keep in sync
|
|
128
|
+
*/
|
|
129
|
+
export declare function foldReviewCostLedger(attempts: ReviewCostAttempt[]): ReviewCostLedger;
|
|
130
|
+
/**
|
|
131
|
+
* Verdict on this review's chargeable spend:
|
|
132
|
+
* • `within` — at or under the effective cap. Nothing changes.
|
|
133
|
+
* • `overrun` — over the cap but under the runaway threshold. The money is
|
|
134
|
+
* ALREADY SPENT, so discarding the verdict buys nothing and strands the PR
|
|
135
|
+
* (#1937). Keep the verdict and flag it.
|
|
136
|
+
* • `runaway` — past the tolerance band. Refuse the verdict; something is
|
|
137
|
+
* pathological and the operator must see it.
|
|
138
|
+
*
|
|
139
|
+
* Mirrors api/_lib/cost-cap.ts `classifyReviewCost`. // keep in sync
|
|
140
|
+
*/
|
|
141
|
+
export type ReviewCostVerdict = "within" | "overrun" | "runaway";
|
|
142
|
+
export declare function classifyReviewCost(chargeableUsd: number, cap: ReviewCostCapDecision): ReviewCostVerdict;
|
|
143
|
+
/** Mutable cost state for ONE review, shared by every pass it runs. */
|
|
144
|
+
export interface ReviewCostContext {
|
|
145
|
+
cap: ReviewCostCapDecision;
|
|
146
|
+
attempts: ReviewCostAttempt[];
|
|
147
|
+
}
|
|
148
|
+
/** Open a cost context for a review over `diffChars` of assembled diff. */
|
|
149
|
+
export declare function openReviewCostContext(baseCapUsd: number, diffChars: number): ReviewCostContext;
|
|
150
|
+
/** Record one attempt and return the folded ledger. */
|
|
151
|
+
export declare function chargeReviewAttempt(ctx: ReviewCostContext, modelId: string, attemptClass: ReviewAttemptClass, costUsd: number): ReviewCostLedger;
|
|
152
|
+
/** The current folded ledger without recording anything. */
|
|
153
|
+
export declare function reviewCostLedger(ctx: ReviewCostContext): ReviewCostLedger;
|
|
154
|
+
/**
|
|
155
|
+
* Advisory reason for an ACCEPTED overrun. Deliberately phrased to match none of
|
|
156
|
+
* the absence-claim patterns in review-chunking.ts (asserted by this wave's
|
|
157
|
+
* test), so an audit line can never be re-read as a read-coverage claim.
|
|
158
|
+
*/
|
|
159
|
+
export declare function reviewCostOverrunReason(cap: ReviewCostCapDecision, ledger: ReviewCostLedger): string;
|
|
160
|
+
/**
|
|
161
|
+
* Refusal reason for a RUNAWAY — the backstop above the scaled cap. Carries the
|
|
162
|
+
* PRODUCTIVE attempt count, which is what distinguishes a single-pass runaway
|
|
163
|
+
* from a chunked review that accumulated past the tolerance over several part
|
|
164
|
+
* passes (the old chunked-only reason string), without needing two call sites.
|
|
165
|
+
*/
|
|
166
|
+
export declare function reviewCostRunawayReason(cap: ReviewCostCapDecision, ledger: ReviewCostLedger): string;
|
|
167
|
+
//# sourceMappingURL=review-cost-cap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-cost-cap.d.ts","sourceRoot":"","sources":["../../src/runtime/review-cost-cap.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* REV-COST — runner-side per-review cost cap (size-scaled).
|
|
3
|
+
*
|
|
4
|
+
* THE EPISODE (PR #1937). The runner reviewer applied the org's FLAT
|
|
5
|
+
* `max_cost_usd_per_review` and, the instant the spend crossed it, discarded the
|
|
6
|
+
* review with `cost_cap_exceeded`. #1937 is a ~1800-line PR: it cost ~$18 against
|
|
7
|
+
* an $8 flat cap and produced NO verdict at all — the money was spent, the
|
|
8
|
+
* analysis was done, and the harness threw both away, then re-charged the same
|
|
9
|
+
* review on the next tick. That is the measured 239-decisions-over-9-tasks loop
|
|
10
|
+
* AU-5 fixed on the SERVER path (api/_lib/cost-cap.ts) and left unfixed here,
|
|
11
|
+
* which is why a large PR could still be stranded whenever a runner picked the
|
|
12
|
+
* broadcast up (the normal case — the server path is the emergency fallback).
|
|
13
|
+
*
|
|
14
|
+
* This module is the runner's copy of AU-5's PURE cost-cap core:
|
|
15
|
+
*
|
|
16
|
+
* • `effectiveReviewCostCap` — the cap for THIS review, scaled by assembled
|
|
17
|
+
* diff size, FLOORED at the org's flat cap (so a median PR is judged against
|
|
18
|
+
* exactly today's number — never inflated) and CLAMPED at
|
|
19
|
+
* REVIEW_CAP_MAX_MULTIPLE.
|
|
20
|
+
* • `foldReviewCostLedger` — the chargeable-vs-infra split. Only PRODUCTIVE
|
|
21
|
+
* attempts (a spawn that returned a readable reviewer envelope) are charged
|
|
22
|
+
* to the cap; capacity/auth/tooling faults are reported but never charged.
|
|
23
|
+
* • `classifyReviewCost` — within / overrun / runaway. `overrun` keeps the
|
|
24
|
+
* verdict (the money is already spent; discarding it only strands the PR);
|
|
25
|
+
* `runaway` is the hard backstop that still refuses.
|
|
26
|
+
*
|
|
27
|
+
* The runner is a standalone published package and cannot import from api/_lib
|
|
28
|
+
* (the same convention as the duplicated PRICING / MODEL_ALIASES maps and
|
|
29
|
+
* review-diff-guard.ts). KEEP IN SYNC with the AU-5 block at the bottom of
|
|
30
|
+
* api/_lib/cost-cap.ts — the constants and all three functions are pinned
|
|
31
|
+
* byte-for-behaviour by tests/unit/revcost1-runner-cap-parity.test.ts, which
|
|
32
|
+
* fails the build the moment the two drift.
|
|
33
|
+
*/
|
|
34
|
+
/**
|
|
35
|
+
* Assembled-diff size the flat cap is treated as calibrated for: the measured
|
|
36
|
+
* p50 production churn (750 changed lines ≈ 30k chars of unified diff). At or
|
|
37
|
+
* below this the effective cap is EXACTLY `max_cost_usd_per_review`.
|
|
38
|
+
* Mirrors api/_lib/cost-cap.ts. // keep in sync
|
|
39
|
+
*/
|
|
40
|
+
export const REVIEW_CAP_REFERENCE_DIFF_CHARS = 30_000;
|
|
41
|
+
/** Fraction of the base cap added per reference-diff-worth of EXCESS size. */
|
|
42
|
+
export const REVIEW_CAP_SIZE_SLOPE = 0.5;
|
|
43
|
+
/**
|
|
44
|
+
* Ceiling on the scaled cap as a multiple of the base cap. Deliberately tight
|
|
45
|
+
* (1.5× → $3.00 at a $2 base) because the measured size↔cost correlation is
|
|
46
|
+
* weak (Pearson r = 0.100): a bigger ramp would be unjustified by the data.
|
|
47
|
+
*/
|
|
48
|
+
export const REVIEW_CAP_MAX_MULTIPLE = 1.5;
|
|
49
|
+
/**
|
|
50
|
+
* Multiple of the effective cap above which an already-PAID review is still
|
|
51
|
+
* discarded as a runaway — the hard backstop that survives this change. Between
|
|
52
|
+
* 1× and this, the verdict is KEPT and flagged (`cost_overrun`) instead of
|
|
53
|
+
* stranding the PR.
|
|
54
|
+
*/
|
|
55
|
+
export const REVIEW_COST_OVERRUN_TOLERANCE = 3;
|
|
56
|
+
/**
|
|
57
|
+
* The size-scaled per-review cost cap. Monotone non-decreasing in `diffChars`,
|
|
58
|
+
* floored at `baseCapUsd` (so it can only ever LOOSEN relative to the flat cap —
|
|
59
|
+
* a small PR's effective cap is unchanged) and clamped at
|
|
60
|
+
* `REVIEW_CAP_MAX_MULTIPLE × baseCapUsd`. Pure.
|
|
61
|
+
*
|
|
62
|
+
* A base cap of 0 (or a non-finite/negative one) means "cap disabled" — the
|
|
63
|
+
* existing `max_cost_usd_per_review > 0` semantics — and is returned verbatim.
|
|
64
|
+
*
|
|
65
|
+
* Mirrors api/_lib/cost-cap.ts `effectiveReviewCostCap`. // keep in sync
|
|
66
|
+
*/
|
|
67
|
+
export function effectiveReviewCostCap(baseCapUsd, diffChars) {
|
|
68
|
+
const base = typeof baseCapUsd === "number" && Number.isFinite(baseCapUsd) && baseCapUsd > 0
|
|
69
|
+
? baseCapUsd
|
|
70
|
+
: 0;
|
|
71
|
+
const chars = typeof diffChars === "number" && Number.isFinite(diffChars) && diffChars > 0
|
|
72
|
+
? diffChars
|
|
73
|
+
: 0;
|
|
74
|
+
if (base === 0) {
|
|
75
|
+
return {
|
|
76
|
+
cap_usd: 0,
|
|
77
|
+
base_cap_usd: 0,
|
|
78
|
+
enabled: false,
|
|
79
|
+
diff_chars: chars,
|
|
80
|
+
scale: 1,
|
|
81
|
+
ceiling_applied: false,
|
|
82
|
+
runaway_usd: 0,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
const excess = Math.max(0, chars - REVIEW_CAP_REFERENCE_DIFF_CHARS);
|
|
86
|
+
const raw = 1 + (REVIEW_CAP_SIZE_SLOPE * excess) / REVIEW_CAP_REFERENCE_DIFF_CHARS;
|
|
87
|
+
const scale = Math.min(raw, REVIEW_CAP_MAX_MULTIPLE);
|
|
88
|
+
const cap = base * scale;
|
|
89
|
+
return {
|
|
90
|
+
cap_usd: cap,
|
|
91
|
+
base_cap_usd: base,
|
|
92
|
+
enabled: true,
|
|
93
|
+
diff_chars: chars,
|
|
94
|
+
scale,
|
|
95
|
+
ceiling_applied: raw > REVIEW_CAP_MAX_MULTIPLE,
|
|
96
|
+
runaway_usd: cap * REVIEW_COST_OVERRUN_TOLERANCE,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function usableUsd(value) {
|
|
100
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Fold an attempt list into the chargeable / infra split. Pure.
|
|
104
|
+
* Mirrors api/_lib/cost-cap.ts `foldReviewCostLedger`. // keep in sync
|
|
105
|
+
*/
|
|
106
|
+
export function foldReviewCostLedger(attempts) {
|
|
107
|
+
let chargeable = 0;
|
|
108
|
+
let infra = 0;
|
|
109
|
+
let productiveN = 0;
|
|
110
|
+
let infraN = 0;
|
|
111
|
+
for (const a of attempts) {
|
|
112
|
+
const cost = usableUsd(a.cost_usd);
|
|
113
|
+
if (a.attempt_class === "productive") {
|
|
114
|
+
chargeable += cost;
|
|
115
|
+
productiveN += 1;
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
infra += cost;
|
|
119
|
+
infraN += 1;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
attempts,
|
|
124
|
+
chargeable_usd: chargeable,
|
|
125
|
+
infra_usd: infra,
|
|
126
|
+
total_usd: chargeable + infra,
|
|
127
|
+
productive_attempts: productiveN,
|
|
128
|
+
infra_attempts: infraN,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
export function classifyReviewCost(chargeableUsd, cap) {
|
|
132
|
+
if (!cap.enabled)
|
|
133
|
+
return "within";
|
|
134
|
+
const spent = usableUsd(chargeableUsd);
|
|
135
|
+
if (spent <= cap.cap_usd)
|
|
136
|
+
return "within";
|
|
137
|
+
return spent > cap.runaway_usd ? "runaway" : "overrun";
|
|
138
|
+
}
|
|
139
|
+
/** Open a cost context for a review over `diffChars` of assembled diff. */
|
|
140
|
+
export function openReviewCostContext(baseCapUsd, diffChars) {
|
|
141
|
+
return { cap: effectiveReviewCostCap(baseCapUsd, diffChars), attempts: [] };
|
|
142
|
+
}
|
|
143
|
+
/** Record one attempt and return the folded ledger. */
|
|
144
|
+
export function chargeReviewAttempt(ctx, modelId, attemptClass, costUsd) {
|
|
145
|
+
ctx.attempts.push({
|
|
146
|
+
attempt: ctx.attempts.length + 1,
|
|
147
|
+
model_id: modelId,
|
|
148
|
+
attempt_class: attemptClass,
|
|
149
|
+
cost_usd: costUsd,
|
|
150
|
+
});
|
|
151
|
+
return foldReviewCostLedger(ctx.attempts);
|
|
152
|
+
}
|
|
153
|
+
/** The current folded ledger without recording anything. */
|
|
154
|
+
export function reviewCostLedger(ctx) {
|
|
155
|
+
return foldReviewCostLedger(ctx.attempts);
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Advisory reason for an ACCEPTED overrun. Deliberately phrased to match none of
|
|
159
|
+
* the absence-claim patterns in review-chunking.ts (asserted by this wave's
|
|
160
|
+
* test), so an audit line can never be re-read as a read-coverage claim.
|
|
161
|
+
*/
|
|
162
|
+
export function reviewCostOverrunReason(cap, ledger) {
|
|
163
|
+
return (`cost_overrun: review cost $${ledger.chargeable_usd.toFixed(4)} exceeded the ` +
|
|
164
|
+
`effective cap $${cap.cap_usd.toFixed(2)} (base $${cap.base_cap_usd.toFixed(2)}, ` +
|
|
165
|
+
`scale ${cap.scale.toFixed(2)}×, ${cap.diff_chars} chars of assembled changes) — ` +
|
|
166
|
+
`verdict KEPT (already paid) and escalated for cap ratification`);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Refusal reason for a RUNAWAY — the backstop above the scaled cap. Carries the
|
|
170
|
+
* PRODUCTIVE attempt count, which is what distinguishes a single-pass runaway
|
|
171
|
+
* from a chunked review that accumulated past the tolerance over several part
|
|
172
|
+
* passes (the old chunked-only reason string), without needing two call sites.
|
|
173
|
+
*/
|
|
174
|
+
export function reviewCostRunawayReason(cap, ledger) {
|
|
175
|
+
const n = ledger.productive_attempts;
|
|
176
|
+
return (`reviewer cost $${ledger.chargeable_usd.toFixed(4)} across ${n} productive ` +
|
|
177
|
+
`${n === 1 ? "attempt" : "attempts"} exceeded cap $${cap.cap_usd.toFixed(2)} ` +
|
|
178
|
+
`(base $${cap.base_cap_usd.toFixed(2)}, scale ${cap.scale.toFixed(2)}×) by more ` +
|
|
179
|
+
`than the ${REVIEW_COST_OVERRUN_TOLERANCE}× runaway tolerance ` +
|
|
180
|
+
`($${cap.runaway_usd.toFixed(2)}) — verdict refused`);
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=review-cost-cap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-cost-cap.js","sourceRoot":"","sources":["../../src/runtime/review-cost-cap.ts"],"names":[],"mappings":"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"}
|