@tokenfactory/acc-runner 0.43.0 → 0.44.1
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 +75 -3
- package/dist/companion-run.d.ts.map +1 -1
- package/dist/companion-run.js +20 -0
- package/dist/companion-run.js.map +1 -1
- package/dist/config.d.ts +72 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +117 -3
- package/dist/config.js.map +1 -1
- package/dist/cost-pricing.d.ts +45 -0
- package/dist/cost-pricing.d.ts.map +1 -1
- package/dist/cost-pricing.js +57 -0
- package/dist/cost-pricing.js.map +1 -1
- package/dist/doctor.d.ts +4 -0
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +32 -3
- package/dist/doctor.js.map +1 -1
- package/dist/engines/registry.d.ts +17 -6
- package/dist/engines/registry.d.ts.map +1 -1
- package/dist/engines/registry.js +38 -13
- package/dist/engines/registry.js.map +1 -1
- package/dist/git-auth/github-token-source.d.ts +54 -0
- package/dist/git-auth/github-token-source.d.ts.map +1 -0
- package/dist/git-auth/github-token-source.js +44 -0
- package/dist/git-auth/github-token-source.js.map +1 -0
- package/dist/git-auth/index.d.ts +4 -0
- package/dist/git-auth/index.d.ts.map +1 -1
- package/dist/git-auth/index.js +4 -0
- package/dist/git-auth/index.js.map +1 -1
- package/dist/git-auth/installation-token.d.ts +95 -0
- package/dist/git-auth/installation-token.d.ts.map +1 -0
- package/dist/git-auth/installation-token.js +192 -0
- package/dist/git-auth/installation-token.js.map +1 -0
- 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 +4 -8
- package/dist/keychain.d.ts.map +1 -1
- package/dist/keychain.js +10 -4
- package/dist/keychain.js.map +1 -1
- package/dist/local-repo/command.d.ts +28 -0
- package/dist/local-repo/command.d.ts.map +1 -0
- package/dist/local-repo/command.js +178 -0
- package/dist/local-repo/command.js.map +1 -0
- package/dist/local-repo/index.d.ts +119 -0
- package/dist/local-repo/index.d.ts.map +1 -0
- package/dist/local-repo/index.js +141 -0
- package/dist/local-repo/index.js.map +1 -0
- package/dist/login.d.ts +4 -0
- package/dist/login.d.ts.map +1 -1
- package/dist/login.js +6 -1
- package/dist/login.js.map +1 -1
- package/dist/mcp-spawn.d.ts +19 -0
- package/dist/mcp-spawn.d.ts.map +1 -1
- package/dist/mcp-spawn.js +52 -26
- 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/program.d.ts.map +1 -1
- package/dist/program.js +34 -0
- package/dist/program.js.map +1 -1
- package/dist/provider-auth.d.ts +70 -1
- package/dist/provider-auth.d.ts.map +1 -1
- package/dist/provider-auth.js +106 -2
- 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/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 +75 -0
- package/dist/runtime/reviewer.d.ts.map +1 -1
- package/dist/runtime/reviewer.js +235 -81
- package/dist/runtime/reviewer.js.map +1 -1
- 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/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 +19 -2
- package/dist/task-runner.d.ts.map +1 -1
- package/dist/task-runner.js +110 -28
- package/dist/task-runner.js.map +1 -1
- package/dist/watch.d.ts +13 -0
- package/dist/watch.d.ts.map +1 -1
- package/dist/watch.js +128 -20
- package/dist/watch.js.map +1 -1
- package/package.json +3 -2
|
@@ -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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"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* S15-FU/REVIEW-1 (AC2) — reviewer FAULT ⇒ explicit abstain, never a verdict.
|
|
3
|
+
*
|
|
4
|
+
* THE RULE. A reviewer fault is a statement about the HARNESS, not about the
|
|
5
|
+
* code. When the reviewer cannot render a faithful prompt, cannot parse its own
|
|
6
|
+
* output, or cites content it never actually read, the only sound outcome is
|
|
7
|
+
* "I did not review this — a human must": an explicit ABSTAIN that routes the PR
|
|
8
|
+
* to needs-review. It must NEVER be a confident reject, and it must never carry
|
|
9
|
+
* a defect reason, because on a fault path there is no evidence for one.
|
|
10
|
+
*
|
|
11
|
+
* WHY THIS MODULE EXISTS. The #1933 episode produced 0.75–0.85-confidence
|
|
12
|
+
* rejects naming a "prose spliced into the SQL literal" corruption that did not
|
|
13
|
+
* exist — the reviewer was reporting, in good faith, the harness's own prompt
|
|
14
|
+
* text that a `replace()` bug had spliced into the diff (see
|
|
15
|
+
* `./prompt-substitute.ts`). Two of those sessions even self-logged the fault and
|
|
16
|
+
* still emitted a reject. Fixing the splice (AC1) removes THAT trigger; this
|
|
17
|
+
* module removes the whole CLASS, by making every fault path structurally
|
|
18
|
+
* incapable of producing a defect claim.
|
|
19
|
+
*
|
|
20
|
+
* MECHANISM, and why the decision string is unchanged. `decision` stays
|
|
21
|
+
* `reviewer_error`, which downstream ALREADY treats as "never merges, retry or
|
|
22
|
+
* hold" — introducing a new decision value would silently fall through every
|
|
23
|
+
* classifier that switches on it. What is new is that the outcome is explicitly
|
|
24
|
+
* MARKED as an abstain, its confidence is pinned to 0, and any model-authored
|
|
25
|
+
* text is QUARANTINED behind a prefix that the finding classifiers skip, so it
|
|
26
|
+
* can be read as context by an operator and never scored as a defect.
|
|
27
|
+
*
|
|
28
|
+
* Pure. No I/O, no DB, no throw.
|
|
29
|
+
* Mirrored from `api/_lib/reviewer/abstain.ts`; the runner ships as a published
|
|
30
|
+
* npm package and cannot import from `api/_lib`.
|
|
31
|
+
* // keep in sync with api/_lib/reviewer/abstain.ts
|
|
32
|
+
*/
|
|
33
|
+
/** What faulted. Recorded verbatim on the outcome so the trail is diagnosable. */
|
|
34
|
+
export type ReviewerFaultKind =
|
|
35
|
+
/** The rendered prompt did not carry the PR content verbatim (#1933 class). */
|
|
36
|
+
"prompt_render"
|
|
37
|
+
/** The reviewer's own JSON verdict could not be parsed, even after repair. */
|
|
38
|
+
| "verdict_parse"
|
|
39
|
+
/** The CLI/API envelope around the verdict could not be parsed. */
|
|
40
|
+
| "envelope_parse"
|
|
41
|
+
/** The verdict cited a file/diff the verification tooling never read (RR-3). */
|
|
42
|
+
| "tooling_read";
|
|
43
|
+
/**
|
|
44
|
+
* Stable marker for cheap row detection — mirrors TOOLING_FAILURE_MARKER /
|
|
45
|
+
* ACCOUNT_CAP_MARKER in `api/_lib/review-retry.ts`, so ONLY rows we classified
|
|
46
|
+
* are ever treated as abstentions.
|
|
47
|
+
*/
|
|
48
|
+
export declare const REVIEWER_ABSTAIN_MARKER = "reviewer abstained:";
|
|
49
|
+
/** The headline stamped on every abstain outcome. Explicit by design: an
|
|
50
|
+
* operator reading the row must not have to infer that nothing was judged. */
|
|
51
|
+
export declare const REVIEWER_ABSTAIN_REASON: string;
|
|
52
|
+
/**
|
|
53
|
+
* Prefix applied to every model-authored / fault-detail line carried alongside an
|
|
54
|
+
* abstain. Deliberately leads with "advisory" so the RR-3 severity classifiers
|
|
55
|
+
* and the calibration reason-matchers read it as non-blocking context.
|
|
56
|
+
*/
|
|
57
|
+
export declare const ABSTAIN_DETAIL_PREFIX = "abstain_context (advisory, not a finding):";
|
|
58
|
+
/** A one-line, machine-greppable statement of which fault kind fired. */
|
|
59
|
+
export declare function abstainFaultLine(kind: ReviewerFaultKind): string;
|
|
60
|
+
/**
|
|
61
|
+
* Quarantine fault detail behind ABSTAIN_DETAIL_PREFIX. Empty/blank lines are
|
|
62
|
+
* dropped, each line is capped, and the list is bounded so a runaway model
|
|
63
|
+
* response cannot bloat the stored row.
|
|
64
|
+
*/
|
|
65
|
+
export declare function quarantineAbstainDetail(lines: ReadonlyArray<string | null | undefined>, opts?: {
|
|
66
|
+
maxLines?: number;
|
|
67
|
+
maxChars?: number;
|
|
68
|
+
}): string[];
|
|
69
|
+
/**
|
|
70
|
+
* The full `reasons` array for an abstain outcome: the explicit headline, the
|
|
71
|
+
* fault kind, then quarantined detail. Callers pin `confidence: 0` and keep
|
|
72
|
+
* `decision: 'reviewer_error'`.
|
|
73
|
+
*/
|
|
74
|
+
export declare function abstainReasons(kind: ReviewerFaultKind, detail?: ReadonlyArray<string | null | undefined>): string[];
|
|
75
|
+
/**
|
|
76
|
+
* A short operator-facing stderr line for an abstain. Names the fault kind and
|
|
77
|
+
* the abstain routing, never the model's text (which may be corrupted content).
|
|
78
|
+
*/
|
|
79
|
+
export declare function describeAbstainForLog(kind: ReviewerFaultKind, detail?: string): string;
|
|
80
|
+
/**
|
|
81
|
+
* True when a completed review outcome/row is an ABSTAIN. Marker-based and
|
|
82
|
+
* defensive: a non-`reviewer_error` decision or absent reasons ⇒ false, so a
|
|
83
|
+
* legacy row that merely mentions abstaining is never reclassified.
|
|
84
|
+
*/
|
|
85
|
+
export declare function isAbstainReviewerOutcome(decision: string | null | undefined, reasons: string[] | null | undefined): boolean;
|
|
86
|
+
/**
|
|
87
|
+
* Reason prefixes that are STRUCTURAL — emitted by the harness, describing the
|
|
88
|
+
* review itself rather than the code. Anything not in this set is a finding.
|
|
89
|
+
*
|
|
90
|
+
* `"review diff coverage:"` is REVIEW_DIFF_COVERAGE_MARKER from
|
|
91
|
+
* `./absence-claims.ts`; it is inlined (and pinned by a test) so this module
|
|
92
|
+
* stays dependency-free and trivially mirrorable into the runner package.
|
|
93
|
+
*/
|
|
94
|
+
export declare const ABSTAIN_STRUCTURAL_PREFIXES: readonly string[];
|
|
95
|
+
/**
|
|
96
|
+
* True when `reasons` carries NO unquarantined, model-authored finding — the
|
|
97
|
+
* invariant an abstain must satisfy. Used by the AC2 test to prove that a
|
|
98
|
+
* simulated fault cannot smuggle a fabricated defect reason through.
|
|
99
|
+
*/
|
|
100
|
+
export declare function abstainCarriesNoFinding(reasons: string[] | null | undefined): boolean;
|
|
101
|
+
//# sourceMappingURL=reviewer-abstain.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reviewer-abstain.d.ts","sourceRoot":"","sources":["../../src/runtime/reviewer-abstain.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,kFAAkF;AAClF,MAAM,MAAM,iBAAiB;AAC3B,+EAA+E;AAC7E,eAAe;AACjB,8EAA8E;GAC5E,eAAe;AACjB,mEAAmE;GACjE,gBAAgB;AAClB,gFAAgF;GAC9E,cAAc,CAAC;AAEnB;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,wBAAwB,CAAC;AAE7D;+EAC+E;AAC/E,eAAO,MAAM,uBAAuB,QAI2C,CAAC;AAEhF;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,+CAA+C,CAAC;AAElF,yEAAyE;AACzE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,iBAAiB,GAAG,MAAM,CAEhE;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,aAAa,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAC/C,IAAI,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAClD,MAAM,EAAE,CAeV;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,iBAAiB,EACvB,MAAM,GAAE,aAAa,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAM,GACpD,MAAM,EAAE,CAMV;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,iBAAiB,EACvB,MAAM,GAAE,MAAW,GAClB,MAAM,CAMR;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACnC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,GAAG,SAAS,GACnC,OAAO,CAKT;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,2BAA2B,EAAE,SAAS,MAAM,EAOxD,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAMrF"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* S15-FU/REVIEW-1 (AC2) — reviewer FAULT ⇒ explicit abstain, never a verdict.
|
|
3
|
+
*
|
|
4
|
+
* THE RULE. A reviewer fault is a statement about the HARNESS, not about the
|
|
5
|
+
* code. When the reviewer cannot render a faithful prompt, cannot parse its own
|
|
6
|
+
* output, or cites content it never actually read, the only sound outcome is
|
|
7
|
+
* "I did not review this — a human must": an explicit ABSTAIN that routes the PR
|
|
8
|
+
* to needs-review. It must NEVER be a confident reject, and it must never carry
|
|
9
|
+
* a defect reason, because on a fault path there is no evidence for one.
|
|
10
|
+
*
|
|
11
|
+
* WHY THIS MODULE EXISTS. The #1933 episode produced 0.75–0.85-confidence
|
|
12
|
+
* rejects naming a "prose spliced into the SQL literal" corruption that did not
|
|
13
|
+
* exist — the reviewer was reporting, in good faith, the harness's own prompt
|
|
14
|
+
* text that a `replace()` bug had spliced into the diff (see
|
|
15
|
+
* `./prompt-substitute.ts`). Two of those sessions even self-logged the fault and
|
|
16
|
+
* still emitted a reject. Fixing the splice (AC1) removes THAT trigger; this
|
|
17
|
+
* module removes the whole CLASS, by making every fault path structurally
|
|
18
|
+
* incapable of producing a defect claim.
|
|
19
|
+
*
|
|
20
|
+
* MECHANISM, and why the decision string is unchanged. `decision` stays
|
|
21
|
+
* `reviewer_error`, which downstream ALREADY treats as "never merges, retry or
|
|
22
|
+
* hold" — introducing a new decision value would silently fall through every
|
|
23
|
+
* classifier that switches on it. What is new is that the outcome is explicitly
|
|
24
|
+
* MARKED as an abstain, its confidence is pinned to 0, and any model-authored
|
|
25
|
+
* text is QUARANTINED behind a prefix that the finding classifiers skip, so it
|
|
26
|
+
* can be read as context by an operator and never scored as a defect.
|
|
27
|
+
*
|
|
28
|
+
* Pure. No I/O, no DB, no throw.
|
|
29
|
+
* Mirrored from `api/_lib/reviewer/abstain.ts`; the runner ships as a published
|
|
30
|
+
* npm package and cannot import from `api/_lib`.
|
|
31
|
+
* // keep in sync with api/_lib/reviewer/abstain.ts
|
|
32
|
+
*/
|
|
33
|
+
/**
|
|
34
|
+
* Stable marker for cheap row detection — mirrors TOOLING_FAILURE_MARKER /
|
|
35
|
+
* ACCOUNT_CAP_MARKER in `api/_lib/review-retry.ts`, so ONLY rows we classified
|
|
36
|
+
* are ever treated as abstentions.
|
|
37
|
+
*/
|
|
38
|
+
export const REVIEWER_ABSTAIN_MARKER = "reviewer abstained:";
|
|
39
|
+
/** The headline stamped on every abstain outcome. Explicit by design: an
|
|
40
|
+
* operator reading the row must not have to infer that nothing was judged. */
|
|
41
|
+
export const REVIEWER_ABSTAIN_REASON = `${REVIEWER_ABSTAIN_MARKER} the reviewer harness faulted, so NO code judgement ` +
|
|
42
|
+
"was formed — this is NOT a defect finding and NOT a reject. The PR needs " +
|
|
43
|
+
"HUMAN VERIFICATION: route to needs-review and have an operator read the diff. " +
|
|
44
|
+
"Any reviewer text below is quarantined diagnostic context, never a finding.";
|
|
45
|
+
/**
|
|
46
|
+
* Prefix applied to every model-authored / fault-detail line carried alongside an
|
|
47
|
+
* abstain. Deliberately leads with "advisory" so the RR-3 severity classifiers
|
|
48
|
+
* and the calibration reason-matchers read it as non-blocking context.
|
|
49
|
+
*/
|
|
50
|
+
export const ABSTAIN_DETAIL_PREFIX = "abstain_context (advisory, not a finding):";
|
|
51
|
+
/** A one-line, machine-greppable statement of which fault kind fired. */
|
|
52
|
+
export function abstainFaultLine(kind) {
|
|
53
|
+
return `abstain_fault_kind: ${kind}`;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Quarantine fault detail behind ABSTAIN_DETAIL_PREFIX. Empty/blank lines are
|
|
57
|
+
* dropped, each line is capped, and the list is bounded so a runaway model
|
|
58
|
+
* response cannot bloat the stored row.
|
|
59
|
+
*/
|
|
60
|
+
export function quarantineAbstainDetail(lines, opts = {}) {
|
|
61
|
+
const maxLines = opts.maxLines ?? 6;
|
|
62
|
+
const maxChars = opts.maxChars ?? 300;
|
|
63
|
+
const out = [];
|
|
64
|
+
for (const raw of lines ?? []) {
|
|
65
|
+
const text = (raw ?? "").trim();
|
|
66
|
+
if (!text)
|
|
67
|
+
continue;
|
|
68
|
+
// Already quarantined (or already the headline) → pass through untouched.
|
|
69
|
+
const prefixed = text.startsWith(ABSTAIN_DETAIL_PREFIX) || text.startsWith(REVIEWER_ABSTAIN_MARKER)
|
|
70
|
+
? text
|
|
71
|
+
: `${ABSTAIN_DETAIL_PREFIX} ${text}`;
|
|
72
|
+
out.push(prefixed.slice(0, maxChars));
|
|
73
|
+
if (out.length >= maxLines)
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* The full `reasons` array for an abstain outcome: the explicit headline, the
|
|
80
|
+
* fault kind, then quarantined detail. Callers pin `confidence: 0` and keep
|
|
81
|
+
* `decision: 'reviewer_error'`.
|
|
82
|
+
*/
|
|
83
|
+
export function abstainReasons(kind, detail = []) {
|
|
84
|
+
return [
|
|
85
|
+
REVIEWER_ABSTAIN_REASON,
|
|
86
|
+
abstainFaultLine(kind),
|
|
87
|
+
...quarantineAbstainDetail(detail),
|
|
88
|
+
];
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* A short operator-facing stderr line for an abstain. Names the fault kind and
|
|
92
|
+
* the abstain routing, never the model's text (which may be corrupted content).
|
|
93
|
+
*/
|
|
94
|
+
export function describeAbstainForLog(kind, detail = "") {
|
|
95
|
+
const tail = detail.trim() ? ` — ${detail.trim().slice(0, 200)}` : "";
|
|
96
|
+
return (`[reviewer] ABSTAIN (${kind}): harness fault, no code judgement formed; ` +
|
|
97
|
+
`routing to needs-review for human verification${tail}\n`);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* True when a completed review outcome/row is an ABSTAIN. Marker-based and
|
|
101
|
+
* defensive: a non-`reviewer_error` decision or absent reasons ⇒ false, so a
|
|
102
|
+
* legacy row that merely mentions abstaining is never reclassified.
|
|
103
|
+
*/
|
|
104
|
+
export function isAbstainReviewerOutcome(decision, reasons) {
|
|
105
|
+
if (decision !== "reviewer_error")
|
|
106
|
+
return false;
|
|
107
|
+
return (reasons ?? []).some((r) => typeof r === "string" && r.includes(REVIEWER_ABSTAIN_MARKER));
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Reason prefixes that are STRUCTURAL — emitted by the harness, describing the
|
|
111
|
+
* review itself rather than the code. Anything not in this set is a finding.
|
|
112
|
+
*
|
|
113
|
+
* `"review diff coverage:"` is REVIEW_DIFF_COVERAGE_MARKER from
|
|
114
|
+
* `./absence-claims.ts`; it is inlined (and pinned by a test) so this module
|
|
115
|
+
* stays dependency-free and trivially mirrorable into the runner package.
|
|
116
|
+
*/
|
|
117
|
+
export const ABSTAIN_STRUCTURAL_PREFIXES = [
|
|
118
|
+
REVIEWER_ABSTAIN_MARKER,
|
|
119
|
+
ABSTAIN_DETAIL_PREFIX,
|
|
120
|
+
"abstain_fault_kind:",
|
|
121
|
+
// Read-coverage telemetry stamped on every outcome: it states how MUCH was
|
|
122
|
+
// read, never what is wrong. // keep in sync with REVIEW_DIFF_COVERAGE_MARKER
|
|
123
|
+
"review diff coverage:",
|
|
124
|
+
];
|
|
125
|
+
/**
|
|
126
|
+
* True when `reasons` carries NO unquarantined, model-authored finding — the
|
|
127
|
+
* invariant an abstain must satisfy. Used by the AC2 test to prove that a
|
|
128
|
+
* simulated fault cannot smuggle a fabricated defect reason through.
|
|
129
|
+
*/
|
|
130
|
+
export function abstainCarriesNoFinding(reasons) {
|
|
131
|
+
return (reasons ?? []).every((r) => {
|
|
132
|
+
const text = (r ?? "").trim();
|
|
133
|
+
if (!text)
|
|
134
|
+
return true;
|
|
135
|
+
return ABSTAIN_STRUCTURAL_PREFIXES.some((p) => text.startsWith(p));
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=reviewer-abstain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reviewer-abstain.js","sourceRoot":"","sources":["../../src/runtime/reviewer-abstain.ts"],"names":[],"mappings":"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"}
|