peeksafe 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +325 -0
- package/LICENSE +190 -0
- package/NOTICE +78 -0
- package/README.md +490 -2
- package/RESEARCH-NOTES.md +274 -0
- package/dist/baseline.d.ts +45 -0
- package/dist/baseline.d.ts.map +1 -0
- package/dist/baseline.js +49 -0
- package/dist/baseline.js.map +1 -0
- package/dist/check.d.ts +64 -0
- package/dist/check.d.ts.map +1 -0
- package/dist/check.js +172 -0
- package/dist/check.js.map +1 -0
- package/dist/cluster.d.ts +328 -0
- package/dist/cluster.d.ts.map +1 -0
- package/dist/cluster.js +606 -0
- package/dist/cluster.js.map +1 -0
- package/dist/errors.d.ts +108 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +192 -0
- package/dist/errors.js.map +1 -0
- package/dist/evidence.d.ts +15 -0
- package/dist/evidence.d.ts.map +1 -0
- package/dist/evidence.js +30 -0
- package/dist/evidence.js.map +1 -0
- package/dist/frontier.d.ts +271 -0
- package/dist/frontier.d.ts.map +1 -0
- package/dist/frontier.js +505 -0
- package/dist/frontier.js.map +1 -0
- package/dist/gate.d.ts +175 -0
- package/dist/gate.d.ts.map +1 -0
- package/dist/gate.js +200 -0
- package/dist/gate.js.map +1 -0
- package/dist/index.d.ts +60 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +79 -0
- package/dist/index.js.map +1 -0
- package/dist/plan.d.ts +280 -0
- package/dist/plan.d.ts.map +1 -0
- package/dist/plan.js +626 -0
- package/dist/plan.js.map +1 -0
- package/dist/rand.d.ts +17 -0
- package/dist/rand.d.ts.map +1 -0
- package/dist/rand.js +104 -0
- package/dist/rand.js.map +1 -0
- package/dist/stats.d.ts +401 -0
- package/dist/stats.d.ts.map +1 -0
- package/dist/stats.js +1304 -0
- package/dist/stats.js.map +1 -0
- package/dist/stop.d.ts +164 -0
- package/dist/stop.d.ts.map +1 -0
- package/dist/stop.js +270 -0
- package/dist/stop.js.map +1 -0
- package/package.json +62 -3
- package/src/baseline.ts +72 -0
- package/src/check.ts +238 -0
- package/src/cluster.ts +842 -0
- package/src/errors.ts +275 -0
- package/src/evidence.ts +62 -0
- package/src/frontier.ts +837 -0
- package/src/gate.ts +363 -0
- package/src/index.ts +206 -0
- package/src/plan.ts +968 -0
- package/src/rand.ts +137 -0
- package/src/stats.ts +1663 -0
- package/src/stop.ts +464 -0
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* errors.ts: one error class, stable codes, structured detail.
|
|
3
|
+
*
|
|
4
|
+
* Nothing in peeksafe throws a bare string. Every code below is thrown
|
|
5
|
+
* somewhere in `src/` and is part of the public contract: a caller may branch
|
|
6
|
+
* on `err.code` and we will not repurpose one.
|
|
7
|
+
*/
|
|
8
|
+
export type PeeksafeErrorCode = "PEEKSAFE_E_CONFIG" | "PEEKSAFE_E_CASE_DUPLICATE" | "PEEKSAFE_E_SUITE_EMPTY" | "PEEKSAFE_E_BASELINE_MISSING" | "PEEKSAFE_E_STAT_DOMAIN";
|
|
9
|
+
export interface PeeksafeErrorOptions {
|
|
10
|
+
detail?: Record<string, unknown>;
|
|
11
|
+
/** what the operator should actually do about it */
|
|
12
|
+
hint?: string;
|
|
13
|
+
cause?: unknown;
|
|
14
|
+
}
|
|
15
|
+
export declare class PeeksafeError extends Error {
|
|
16
|
+
readonly code: PeeksafeErrorCode;
|
|
17
|
+
readonly detail: Record<string, unknown>;
|
|
18
|
+
readonly hint?: string;
|
|
19
|
+
constructor(code: PeeksafeErrorCode, message: string, opts?: PeeksafeErrorOptions);
|
|
20
|
+
/** Stable JSON shape, for logs and CI artifacts. */
|
|
21
|
+
toJSON(): {
|
|
22
|
+
name: string;
|
|
23
|
+
code: string;
|
|
24
|
+
message: string;
|
|
25
|
+
hint?: string;
|
|
26
|
+
detail: Record<string, unknown>;
|
|
27
|
+
};
|
|
28
|
+
static is(e: unknown): e is PeeksafeError;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Domain guard for the statistics. Every function in `stats.ts` that can be
|
|
32
|
+
* handed nonsense (successes > trials, a negative count, an MDE larger than the
|
|
33
|
+
* baseline rate) routes through here rather than returning NaN.
|
|
34
|
+
*/
|
|
35
|
+
export declare function requireCounts(successes: number, trials: number, where: string): void;
|
|
36
|
+
/**
|
|
37
|
+
* A probability that must be strictly inside (0,1), a rate, an effect size, an
|
|
38
|
+
* error budget. `requireProbability` allows the endpoints, which is right for a
|
|
39
|
+
* *rate* and wrong for an *effect*: an MDE of 0 asks for a zero-point drop to be
|
|
40
|
+
* detected, which every design answers "no" to for reasons that look like a
|
|
41
|
+
* ceiling problem and are not.
|
|
42
|
+
*/
|
|
43
|
+
export declare function requireOpenProbability(p: number, name: string, where: string): void;
|
|
44
|
+
/**
|
|
45
|
+
* A configuration number that must be finite and strictly positive.
|
|
46
|
+
*
|
|
47
|
+
* Shared by `gate` and `shouldStop` so the two entry points cannot drift on
|
|
48
|
+
* what they accept. They did drift: `gate` rejected a non-positive
|
|
49
|
+
* `altConcentration` and `shouldStop` passed it through to a prior floor, so
|
|
50
|
+
* the same options object was a refusal through one door and a verdict through
|
|
51
|
+
* the other.
|
|
52
|
+
*/
|
|
53
|
+
export declare function requirePositiveConfig(v: number, name: string, where: string): void;
|
|
54
|
+
/**
|
|
55
|
+
* The largest `altConcentration` the default statistic accepts at a verdict.
|
|
56
|
+
*
|
|
57
|
+
* Its error control is computed rather than proven, and a concentrated
|
|
58
|
+
* alternative is the one known way it exceeds alpha at a rate inside the
|
|
59
|
+
* checked range: 1.04 times alpha at 32 (`test/error-control.test.ts`). At 16
|
|
60
|
+
* the worst checked cell is 0.91 of alpha.
|
|
61
|
+
*/
|
|
62
|
+
export declare const MAX_ALT_CONCENTRATION = 16;
|
|
63
|
+
/**
|
|
64
|
+
* `altConcentration` for a verdict: finite and positive, and at most
|
|
65
|
+
* `MAX_ALT_CONCENTRATION` under the default `bayes` statistic. `universal` is
|
|
66
|
+
* valid at every rate by construction, so it takes any positive value.
|
|
67
|
+
*/
|
|
68
|
+
export declare function requireAltConcentration(v: number, evidence: string, where: string): void;
|
|
69
|
+
/**
|
|
70
|
+
* The error rates of a Wald SPRT, run by `sprtDecision` or priced by a
|
|
71
|
+
* planner: each strictly between 0 and 1, and summing to less than 1.
|
|
72
|
+
*
|
|
73
|
+
* At `alpha + beta >= 1` Wald's walls meet or cross, so the test decides
|
|
74
|
+
* before it has seen a run. `sprtExpectedN` answered a small positive number
|
|
75
|
+
* there, and a plan at `alpha: 0.6, beta: 0.5` priced every case at
|
|
76
|
+
* `minTrials` with nothing to say the design had no error control left.
|
|
77
|
+
*/
|
|
78
|
+
export declare function requireErrorRates(alpha: number, beta: number, where: string): void;
|
|
79
|
+
/**
|
|
80
|
+
* `runsPerCaseCeiling`: finite and at least one run. The planners search up to
|
|
81
|
+
* eight times it, and at NaN or 0 that search never ran, so every case came
|
|
82
|
+
* back undecidable with a reason that blamed the evidence bar.
|
|
83
|
+
*/
|
|
84
|
+
export declare function requireRunCeiling(v: number, where: string): void;
|
|
85
|
+
/**
|
|
86
|
+
* The batch grid a plan prices the gate on: `minTrials` and `batchSize`
|
|
87
|
+
* positive integers, `maxTrials` an integer at least `minTrials`. A NaN bound
|
|
88
|
+
* or a zero batch made the typical bill NaN, and a cap under the minimum
|
|
89
|
+
* priced every case at the cap.
|
|
90
|
+
*/
|
|
91
|
+
export declare function requireTrialGrid(cfg: {
|
|
92
|
+
minTrials: number;
|
|
93
|
+
maxTrials: number;
|
|
94
|
+
batchSize: number;
|
|
95
|
+
}, where: string): void;
|
|
96
|
+
/** The four cells of a pair table, each a non-negative integer. */
|
|
97
|
+
export declare function requirePairedCounts(p: {
|
|
98
|
+
bothPass: number;
|
|
99
|
+
worse: number;
|
|
100
|
+
better: number;
|
|
101
|
+
bothFail: number;
|
|
102
|
+
}, where: string): void;
|
|
103
|
+
export declare function requireProbability(p: number, name: string, where: string): void;
|
|
104
|
+
/** Case ids, each at most once: a repeated id is planned twice and inflates the suite size the bar is set from. */
|
|
105
|
+
export declare function requireUniqueCaseIds(cases: readonly {
|
|
106
|
+
id: string;
|
|
107
|
+
}[], where: string): void;
|
|
108
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,iBAAiB,GAEzB,mBAAmB,GACnB,2BAA2B,GAC3B,wBAAwB,GAExB,6BAA6B,GAE7B,wBAAwB,CAAC;AAE7B,MAAM,WAAW,oBAAoB;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,oDAAoD;IACpD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,qBAAa,aAAc,SAAQ,KAAK;IACtC,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAEvB,YAAY,IAAI,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,oBAAyB,EAMpF;IAED,oDAAoD;IACpD,MAAM,IAAI;QACR,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACjC,CAQA;IAED,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,aAAa,CAExC;CACF;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAkBpF;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAUnF;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAUlF;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAExC;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAYxF;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAalF;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAUhE;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,EAChE,KAAK,EAAE,MAAM,GACZ,IAAI,CAyBN;AAED,mEAAmE;AACnE,wBAAgB,mBAAmB,CACjC,CAAC,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,EACxE,KAAK,EAAE,MAAM,GACZ,IAAI,CAaN;AAED,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAU/E;AAED,mHAAmH;AACnH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,SAAS;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAW1F"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* errors.ts: one error class, stable codes, structured detail.
|
|
3
|
+
*
|
|
4
|
+
* Nothing in peeksafe throws a bare string. Every code below is thrown
|
|
5
|
+
* somewhere in `src/` and is part of the public contract: a caller may branch
|
|
6
|
+
* on `err.code` and we will not repurpose one.
|
|
7
|
+
*/
|
|
8
|
+
export class PeeksafeError extends Error {
|
|
9
|
+
code;
|
|
10
|
+
detail;
|
|
11
|
+
hint;
|
|
12
|
+
constructor(code, message, opts = {}) {
|
|
13
|
+
super(message, opts.cause !== undefined ? { cause: opts.cause } : undefined);
|
|
14
|
+
this.name = "PeeksafeError";
|
|
15
|
+
this.code = code;
|
|
16
|
+
this.detail = opts.detail ?? {};
|
|
17
|
+
this.hint = opts.hint;
|
|
18
|
+
}
|
|
19
|
+
/** Stable JSON shape, for logs and CI artifacts. */
|
|
20
|
+
toJSON() {
|
|
21
|
+
return {
|
|
22
|
+
name: this.name,
|
|
23
|
+
code: this.code,
|
|
24
|
+
message: this.message,
|
|
25
|
+
...(this.hint ? { hint: this.hint } : {}),
|
|
26
|
+
detail: this.detail,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
static is(e) {
|
|
30
|
+
return e instanceof PeeksafeError;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Domain guard for the statistics. Every function in `stats.ts` that can be
|
|
35
|
+
* handed nonsense (successes > trials, a negative count, an MDE larger than the
|
|
36
|
+
* baseline rate) routes through here rather than returning NaN.
|
|
37
|
+
*/
|
|
38
|
+
export function requireCounts(successes, trials, where) {
|
|
39
|
+
if (!Number.isFinite(successes) || !Number.isFinite(trials)) {
|
|
40
|
+
throw new PeeksafeError("PEEKSAFE_E_STAT_DOMAIN", `${where}: counts must be finite`, {
|
|
41
|
+
detail: { successes, trials, where },
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
if (!Number.isInteger(successes) || !Number.isInteger(trials)) {
|
|
45
|
+
throw new PeeksafeError("PEEKSAFE_E_STAT_DOMAIN", `${where}: counts must be integers`, {
|
|
46
|
+
detail: { successes, trials, where },
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
if (trials < 0 || successes < 0 || successes > trials) {
|
|
50
|
+
throw new PeeksafeError("PEEKSAFE_E_STAT_DOMAIN", `${where}: need 0 ≤ successes ≤ trials, got ${successes}/${trials}`, { detail: { successes, trials, where } });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* A probability that must be strictly inside (0,1), a rate, an effect size, an
|
|
55
|
+
* error budget. `requireProbability` allows the endpoints, which is right for a
|
|
56
|
+
* *rate* and wrong for an *effect*: an MDE of 0 asks for a zero-point drop to be
|
|
57
|
+
* detected, which every design answers "no" to for reasons that look like a
|
|
58
|
+
* ceiling problem and are not.
|
|
59
|
+
*/
|
|
60
|
+
export function requireOpenProbability(p, name, where) {
|
|
61
|
+
if (!Number.isFinite(p) || p <= 0 || p >= 1) {
|
|
62
|
+
throw new PeeksafeError("PEEKSAFE_E_STAT_DOMAIN", `${where}: ${name} must be strictly between 0 and 1, got ${p}`, {
|
|
63
|
+
detail: { [name]: p, where },
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* A configuration number that must be finite and strictly positive.
|
|
69
|
+
*
|
|
70
|
+
* Shared by `gate` and `shouldStop` so the two entry points cannot drift on
|
|
71
|
+
* what they accept. They did drift: `gate` rejected a non-positive
|
|
72
|
+
* `altConcentration` and `shouldStop` passed it through to a prior floor, so
|
|
73
|
+
* the same options object was a refusal through one door and a verdict through
|
|
74
|
+
* the other.
|
|
75
|
+
*/
|
|
76
|
+
export function requirePositiveConfig(v, name, where) {
|
|
77
|
+
if (!Number.isFinite(v) || v <= 0) {
|
|
78
|
+
throw new PeeksafeError("PEEKSAFE_E_CONFIG", `${where}: ${name} must be finite and positive, got ${v}`, {
|
|
79
|
+
detail: { [name]: v, where },
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* The largest `altConcentration` the default statistic accepts at a verdict.
|
|
85
|
+
*
|
|
86
|
+
* Its error control is computed rather than proven, and a concentrated
|
|
87
|
+
* alternative is the one known way it exceeds alpha at a rate inside the
|
|
88
|
+
* checked range: 1.04 times alpha at 32 (`test/error-control.test.ts`). At 16
|
|
89
|
+
* the worst checked cell is 0.91 of alpha.
|
|
90
|
+
*/
|
|
91
|
+
export const MAX_ALT_CONCENTRATION = 16;
|
|
92
|
+
/**
|
|
93
|
+
* `altConcentration` for a verdict: finite and positive, and at most
|
|
94
|
+
* `MAX_ALT_CONCENTRATION` under the default `bayes` statistic. `universal` is
|
|
95
|
+
* valid at every rate by construction, so it takes any positive value.
|
|
96
|
+
*/
|
|
97
|
+
export function requireAltConcentration(v, evidence, where) {
|
|
98
|
+
requirePositiveConfig(v, "altConcentration", where);
|
|
99
|
+
if (evidence === "bayes" && v > MAX_ALT_CONCENTRATION) {
|
|
100
|
+
throw new PeeksafeError("PEEKSAFE_E_CONFIG", `${where}: altConcentration must be at most ${MAX_ALT_CONCENTRATION} with the default statistic, got ${v}`, {
|
|
101
|
+
detail: { altConcentration: v, max: MAX_ALT_CONCENTRATION, evidence, where },
|
|
102
|
+
hint: "Use evidence: 'universal' for a more concentrated alternative, or check the rates with typeOneError.",
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* The error rates of a Wald SPRT, run by `sprtDecision` or priced by a
|
|
108
|
+
* planner: each strictly between 0 and 1, and summing to less than 1.
|
|
109
|
+
*
|
|
110
|
+
* At `alpha + beta >= 1` Wald's walls meet or cross, so the test decides
|
|
111
|
+
* before it has seen a run. `sprtExpectedN` answered a small positive number
|
|
112
|
+
* there, and a plan at `alpha: 0.6, beta: 0.5` priced every case at
|
|
113
|
+
* `minTrials` with nothing to say the design had no error control left.
|
|
114
|
+
*/
|
|
115
|
+
export function requireErrorRates(alpha, beta, where) {
|
|
116
|
+
requireOpenProbability(alpha, "alpha", where);
|
|
117
|
+
requireOpenProbability(beta, "beta", where);
|
|
118
|
+
if (alpha + beta >= 1) {
|
|
119
|
+
throw new PeeksafeError("PEEKSAFE_E_CONFIG", `${where}: alpha + beta must be under 1, got ${alpha} + ${beta}`, {
|
|
120
|
+
detail: { alpha, beta, where },
|
|
121
|
+
hint: "a test with alpha + beta >= 1 does no better than ignoring the data",
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* `runsPerCaseCeiling`: finite and at least one run. The planners search up to
|
|
127
|
+
* eight times it, and at NaN or 0 that search never ran, so every case came
|
|
128
|
+
* back undecidable with a reason that blamed the evidence bar.
|
|
129
|
+
*/
|
|
130
|
+
export function requireRunCeiling(v, where) {
|
|
131
|
+
if (!(v >= 1) || !Number.isFinite(v)) {
|
|
132
|
+
throw new PeeksafeError("PEEKSAFE_E_CONFIG", `${where}: runsPerCaseCeiling must be finite and at least 1, got ${v}`, {
|
|
133
|
+
detail: { runsPerCaseCeiling: v, where },
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* The batch grid a plan prices the gate on: `minTrials` and `batchSize`
|
|
139
|
+
* positive integers, `maxTrials` an integer at least `minTrials`. A NaN bound
|
|
140
|
+
* or a zero batch made the typical bill NaN, and a cap under the minimum
|
|
141
|
+
* priced every case at the cap.
|
|
142
|
+
*/
|
|
143
|
+
export function requireTrialGrid(cfg, where) {
|
|
144
|
+
const { minTrials, maxTrials, batchSize } = cfg;
|
|
145
|
+
const detail = { minTrials, maxTrials, batchSize, where };
|
|
146
|
+
for (const [name, v] of [
|
|
147
|
+
["minTrials", minTrials],
|
|
148
|
+
["batchSize", batchSize],
|
|
149
|
+
]) {
|
|
150
|
+
if (!Number.isInteger(v) || v < 1) {
|
|
151
|
+
throw new PeeksafeError("PEEKSAFE_E_CONFIG", `${where}: ${name} must be a positive integer, got ${v}`, { detail });
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (!Number.isInteger(maxTrials) || maxTrials < minTrials) {
|
|
155
|
+
throw new PeeksafeError("PEEKSAFE_E_CONFIG", `${where}: maxTrials must be an integer ≥ minTrials, got ${maxTrials} vs ${minTrials}`, {
|
|
156
|
+
detail,
|
|
157
|
+
hint: "the typical bill is clamped to [minTrials, maxTrials]; a cap under the minimum prices a gate that stops before it starts",
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/** The four cells of a pair table, each a non-negative integer. */
|
|
162
|
+
export function requirePairedCounts(p, where) {
|
|
163
|
+
for (const name of ["bothPass", "worse", "better", "bothFail"]) {
|
|
164
|
+
const v = p[name];
|
|
165
|
+
if (!Number.isInteger(v) || v < 0) {
|
|
166
|
+
throw new PeeksafeError("PEEKSAFE_E_STAT_DOMAIN", `${where}: ${name} must be a non-negative integer, got ${v}`, {
|
|
167
|
+
detail: { [name]: v, where },
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
export function requireProbability(p, name, where) {
|
|
173
|
+
if (!Number.isFinite(p) || p < 0 || p > 1) {
|
|
174
|
+
throw new PeeksafeError("PEEKSAFE_E_STAT_DOMAIN", `${where}: ${name} must be in [0,1], got ${p}`, {
|
|
175
|
+
detail: { [name]: p, where },
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/** Case ids, each at most once: a repeated id is planned twice and inflates the suite size the bar is set from. */
|
|
180
|
+
export function requireUniqueCaseIds(cases, where) {
|
|
181
|
+
const seen = new Set();
|
|
182
|
+
for (const c of cases) {
|
|
183
|
+
if (seen.has(c.id)) {
|
|
184
|
+
throw new PeeksafeError("PEEKSAFE_E_CASE_DUPLICATE", `${where}: duplicate case id ${c.id}`, {
|
|
185
|
+
detail: { id: c.id, where },
|
|
186
|
+
hint: "a case listed twice is priced twice and counted twice in the suite size; dedupe the case list before calling",
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
seen.add(c.id);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* evidence.ts: which two-sample e-value a decision uses, and its ceiling.
|
|
3
|
+
*
|
|
4
|
+
* `bayes` is `twoSampleLogE`, the default: more evidence per run, with error
|
|
5
|
+
* control checked numerically rather than proven. `universal` is
|
|
6
|
+
* `universalTwoSampleLogE`: valid at every rate by construction, with less
|
|
7
|
+
* evidence per run. README, "What that guarantee rests on", has the numbers.
|
|
8
|
+
*/
|
|
9
|
+
export type Evidence = "bayes" | "universal";
|
|
10
|
+
export declare function requireEvidence(evidence: unknown, where: string): asserts evidence is Evidence;
|
|
11
|
+
export declare const logEvidence: (evidence: Evidence, cs: number, cn: number, bs: number, bn: number, mde: number, altConcentration: number) => number;
|
|
12
|
+
export declare const toEvalue: (logE: number) => number;
|
|
13
|
+
/** The most evidence a candidate at rate `p` could ever reach against this baseline. */
|
|
14
|
+
export declare const ceilingLogEvidence: (evidence: Evidence, p: number, bs: number, bn: number, mde: number, altConcentration: number) => number;
|
|
15
|
+
//# sourceMappingURL=evidence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evidence.d.ts","sourceRoot":"","sources":["../src/evidence.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAUH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,WAAW,CAAC;AAE7C,wBAAgB,eAAe,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,IAAI,QAAQ,CAU9F;AAED,eAAO,MAAM,WAAW,aACZ,QAAQ,MACd,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,OACL,MAAM,oBACO,MAAM,KACvB,MAGuD,CAAC;AAG3D,eAAO,MAAM,QAAQ,SAAU,MAAM,KAAG,MAGvC,CAAC;AAEF,wFAAwF;AACxF,eAAO,MAAM,kBAAkB,aACnB,QAAQ,KACf,MAAM,MACL,MAAM,MACN,MAAM,OACL,MAAM,oBACO,MAAM,KACvB,MAGwD,CAAC"}
|
package/dist/evidence.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* evidence.ts: which two-sample e-value a decision uses, and its ceiling.
|
|
3
|
+
*
|
|
4
|
+
* `bayes` is `twoSampleLogE`, the default: more evidence per run, with error
|
|
5
|
+
* control checked numerically rather than proven. `universal` is
|
|
6
|
+
* `universalTwoSampleLogE`: valid at every rate by construction, with less
|
|
7
|
+
* evidence per run. README, "What that guarantee rests on", has the numbers.
|
|
8
|
+
*/
|
|
9
|
+
import { PeeksafeError } from "./errors.js";
|
|
10
|
+
import { evidenceCeilingLogE, twoSampleLogE, universalCeilingLogE, universalTwoSampleLogE, } from "./stats.js";
|
|
11
|
+
export function requireEvidence(evidence, where) {
|
|
12
|
+
if (evidence !== "bayes" && evidence !== "universal") {
|
|
13
|
+
throw new PeeksafeError("PEEKSAFE_E_CONFIG", `${where}: evidence must be 'bayes' or 'universal', got ${String(evidence)}`, {
|
|
14
|
+
detail: { evidence },
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export const logEvidence = (evidence, cs, cn, bs, bn, mde, altConcentration) => evidence === "universal"
|
|
19
|
+
? universalTwoSampleLogE(cs, cn, bs, bn, mde, altConcentration)
|
|
20
|
+
: twoSampleLogE(cs, cn, bs, bn, mde, altConcentration);
|
|
21
|
+
// exp() of a large logE overflows, and e-BH cannot rank an infinite entry; the largest double keeps the order.
|
|
22
|
+
export const toEvalue = (logE) => {
|
|
23
|
+
const e = Math.exp(logE);
|
|
24
|
+
return Number.isFinite(e) ? e : Number.MAX_VALUE;
|
|
25
|
+
};
|
|
26
|
+
/** The most evidence a candidate at rate `p` could ever reach against this baseline. */
|
|
27
|
+
export const ceilingLogEvidence = (evidence, p, bs, bn, mde, altConcentration) => evidence === "universal"
|
|
28
|
+
? universalCeilingLogE(p, bs, bn)
|
|
29
|
+
: evidenceCeilingLogE(p, bs, bn, mde, altConcentration);
|
|
30
|
+
//# sourceMappingURL=evidence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evidence.js","sourceRoot":"","sources":["../src/evidence.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EACL,mBAAmB,EACnB,aAAa,EACb,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,YAAY,CAAC;AAIpB,MAAM,UAAU,eAAe,CAAC,QAAiB,EAAE,KAAa;IAC9D,IAAI,QAAQ,KAAK,OAAO,IAAI,QAAQ,KAAK,WAAW,EAAE,CAAC;QACrD,MAAM,IAAI,aAAa,CACrB,mBAAmB,EACnB,GAAG,KAAK,kDAAkD,MAAM,CAAC,QAAQ,CAAC,EAAE,EAC5E;YACE,MAAM,EAAE,EAAE,QAAQ,EAAE;SACrB,CACF,CAAC;IACJ,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,WAAW,GAAG,CACzB,QAAkB,EAClB,EAAU,EACV,EAAU,EACV,EAAU,EACV,EAAU,EACV,GAAW,EACX,gBAAwB,EAChB,EAAE,CACV,QAAQ,KAAK,WAAW;IACtB,CAAC,CAAC,sBAAsB,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,gBAAgB,CAAC;IAC/D,CAAC,CAAC,aAAa,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,gBAAgB,CAAC,CAAC;AAE3D,+GAA+G;AAC/G,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAU,EAAE;IAC/C,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACzB,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC;AACnD,CAAC,CAAC;AAEF,wFAAwF;AACxF,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAChC,QAAkB,EAClB,CAAS,EACT,EAAU,EACV,EAAU,EACV,GAAW,EACX,gBAAwB,EAChB,EAAE,CACV,QAAQ,KAAK,WAAW;IACtB,CAAC,CAAC,oBAAoB,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC;IACjC,CAAC,CAAC,mBAAmB,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,gBAAgB,CAAC,CAAC"}
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* frontier.ts: what *is* certifiable at coding-agent prices, and how.
|
|
3
|
+
*
|
|
4
|
+
* ## Why this module exists
|
|
5
|
+
*
|
|
6
|
+
* At $1 a run a 200-case suite at a 15-point MDE is $1,320 a pull request, so
|
|
7
|
+
* peeksafe is not a per-PR gate for a coding-agent benchmark at that size. That
|
|
8
|
+
* is true and it is half an answer. The other half, the one a
|
|
9
|
+
* buyer actually needs, is **which configurations *are* affordable**, and
|
|
10
|
+
* nobody had drawn it.
|
|
11
|
+
*
|
|
12
|
+
* A configuration is a point in five dimensions:
|
|
13
|
+
*
|
|
14
|
+
* suite size m × MDE δ × design × screening × baseline runs n_b
|
|
15
|
+
*
|
|
16
|
+
* and it is usable only if it is *both* affordable and **certifiable**. Those
|
|
17
|
+
* are different constraints, and a plan that conflates them quotes the wrong
|
|
18
|
+
* numbers. Cheap is easy: run fewer cases. Cheap *and able to decide anything* is
|
|
19
|
+
* the hard part, because `evidenceCeilingLogE` says a case whose unpaired
|
|
20
|
+
* ceiling sits under the e-BH bar cannot be certified at any budget, and the
|
|
21
|
+
* bar `m/q` moves with the suite size, so shrinking the suite raises the
|
|
22
|
+
* ceiling's odds *and* lowers the bar at the same time. That interaction is not
|
|
23
|
+
* something you can eyeball.
|
|
24
|
+
*
|
|
25
|
+
* ## The cost model, and the term the plan was missing
|
|
26
|
+
*
|
|
27
|
+
* `plan.ts` prices an observation at one number, `costPerRunUsd`. That is wrong
|
|
28
|
+
* in two ways at coding-agent prices, and both matter:
|
|
29
|
+
*
|
|
30
|
+
* 1. **Grading is not free.** An LLM judge is a model call; a coding-agent
|
|
31
|
+
* grader is a test suite in a container. So
|
|
32
|
+
*
|
|
33
|
+
* cost(observation) = costPerRunUsd + costPerGradeUsd
|
|
34
|
+
*
|
|
35
|
+
* and the second term is the *only* one a cheap proxy grader can reduce.
|
|
36
|
+
* When `costPerGradeUsd` is 0, which is the default,
|
|
37
|
+
* matching this package's own free graders, a proxy screen saves exactly
|
|
38
|
+
* nothing, and this module says so instead of offering it as an economy.
|
|
39
|
+
*
|
|
40
|
+
* 2. **The baseline is not free either, and it is not amortised over
|
|
41
|
+
* infinity.** The README calls the baseline "amortised, you pay it once on
|
|
42
|
+
* main". At $0.002 a run that is a rounding error. At $1 a run, a 200-case
|
|
43
|
+
* suite with a 240-run baseline is 48,000 runs, **$48,000**, and spread
|
|
44
|
+
* over a hundred pull requests it is still $480 each, which is larger than
|
|
45
|
+
* everything else in the bill put together. The unpaired design's evidence
|
|
46
|
+
* ceiling is bought with baseline runs, so *the amortised baseline is the
|
|
47
|
+
* dominant cost of the unpaired design at coding-agent prices*, and the
|
|
48
|
+
* paired design's headline advantage is not variance reduction. It is that
|
|
49
|
+
* it does not need to buy a ceiling.
|
|
50
|
+
*
|
|
51
|
+
* ## What this module will not do
|
|
52
|
+
*
|
|
53
|
+
* It will not recommend a screened configuration whose **recall cost has not
|
|
54
|
+
* been measured**. Screening is the largest cost lever available and it is paid
|
|
55
|
+
* for in missed regressions, which the caller supplies as `screenRecall`
|
|
56
|
+
* against planted ground truth, and until someone has, the honest price of a
|
|
57
|
+
* screened plan is unknown rather than low.
|
|
58
|
+
*/
|
|
59
|
+
export type FrontierDesign = "unpaired" | "paired";
|
|
60
|
+
/**
|
|
61
|
+
* What the screening pass grades with.
|
|
62
|
+
* - `none` every case goes straight to the sequential test
|
|
63
|
+
* - `expensive` the screen pays the real grader for its k runs per case
|
|
64
|
+
* - `proxy` the screen ranks on a cheap deterministic grader, so its runs
|
|
65
|
+
* cost `costPerRunUsd` and no grade. Sound, a screen chooses
|
|
66
|
+
* where to spend and cannot falsely certify, but it ranks on an
|
|
67
|
+
* effect attenuated by Youden's J, so it costs
|
|
68
|
+
* power, and the power is what has to be measured.
|
|
69
|
+
*/
|
|
70
|
+
export type FrontierScreen = "none" | "expensive" | "proxy";
|
|
71
|
+
/**
|
|
72
|
+
* Which bill the budget is checked against. Three, because they are three
|
|
73
|
+
* genuinely different questions, and answering one while quoting another
|
|
74
|
+
* misprices the plan.
|
|
75
|
+
*
|
|
76
|
+
* `'typical'`, a pull request that changed nothing. Almost every pull request
|
|
77
|
+
* is this one. Budget from it and a genuine regression overruns and comes
|
|
78
|
+
* back uncertified, which is a survivable failure.
|
|
79
|
+
*
|
|
80
|
+
* `'certify-all'`, every case regressed by exactly the MDE and every one had
|
|
81
|
+
* to be certified. It was once called "the ceiling", and
|
|
82
|
+
* it is **not** an upper bound: at a large MDE against a sharp baseline,
|
|
83
|
+
* certifying a real regression is *cheaper* than clearing a case that did not
|
|
84
|
+
* move, so this bill can come in under the typical one. The arithmetic was
|
|
85
|
+
* never wrong; the word "ceiling" was.
|
|
86
|
+
*
|
|
87
|
+
* `'ceiling'`, every case the screen kept runs to the per-case cap
|
|
88
|
+
* (`maxTrials` observations). The gate cannot spend more than that by
|
|
89
|
+
* construction, so nothing can overrun it. This is the basis to use when the
|
|
90
|
+
* gate must return a verdict rather than a deferral, and it changes which
|
|
91
|
+
* *design* wins: paired costs two runs an observation and loses the typical
|
|
92
|
+
* basis by construction, while on the ceiling basis, and whenever the
|
|
93
|
+
* baseline cannot be amortised, it wins outright.
|
|
94
|
+
*/
|
|
95
|
+
export type FrontierBasis = "typical" | "certify-all" | "ceiling";
|
|
96
|
+
export interface FrontierAxes {
|
|
97
|
+
cases: number[];
|
|
98
|
+
/** in probability units, e.g. 0.15 for 15 points */
|
|
99
|
+
mdes: number[];
|
|
100
|
+
baselineRuns: number[];
|
|
101
|
+
designs: FrontierDesign[];
|
|
102
|
+
screens: FrontierScreen[];
|
|
103
|
+
/** screening runs per case; only consulted for screens other than `none` */
|
|
104
|
+
screenRuns: number[];
|
|
105
|
+
}
|
|
106
|
+
export declare const DEFAULT_AXES: FrontierAxes;
|
|
107
|
+
export interface FrontierConfig {
|
|
108
|
+
/** the budget one pull request may spend */
|
|
109
|
+
budgetUsd: number;
|
|
110
|
+
/** which bill the budget is checked against */
|
|
111
|
+
basis: FrontierBasis;
|
|
112
|
+
/** USD to execute the agent once on one case */
|
|
113
|
+
costPerRunUsd: number;
|
|
114
|
+
/**
|
|
115
|
+
* USD to *grade* one output with the real grader. Zero for this package's own
|
|
116
|
+
* graders; a model call or a container run for anything real. This is the
|
|
117
|
+
* only term a proxy grader can reduce.
|
|
118
|
+
*/
|
|
119
|
+
costPerGradeUsd: number;
|
|
120
|
+
/** the pass rate a typical case sits at, used for every power calculation */
|
|
121
|
+
baselineRate: number;
|
|
122
|
+
/** suite-level false discovery rate */
|
|
123
|
+
fdr: number;
|
|
124
|
+
/** measured pair coupling in [0,1] */
|
|
125
|
+
pairCoupling: number;
|
|
126
|
+
/**
|
|
127
|
+
* How many pull requests one recorded baseline is spread over before it is
|
|
128
|
+
* re-recorded. The README's "amortised" claim is exactly this number, and it
|
|
129
|
+
* had never been written down.
|
|
130
|
+
*/
|
|
131
|
+
amortisePrs: number;
|
|
132
|
+
/**
|
|
133
|
+
* Baseline runs per case the *paired* design needs. The paired e-value's null
|
|
134
|
+
* is a point mass, so the stored baseline is not part of its validity and
|
|
135
|
+
* does not set a ceiling; here it is only the baseline bill, since a paired
|
|
136
|
+
* typical bill is priced in pairs to settle. A short one is enough, and that
|
|
137
|
+
* asymmetry is most of why the paired design wins at these prices.
|
|
138
|
+
*/
|
|
139
|
+
pairedBaselineRuns: number;
|
|
140
|
+
/** fraction of cases a screen carries through to the expensive test */
|
|
141
|
+
screenKeepFraction: number;
|
|
142
|
+
/** past this many runs for one case, call it unaffordable regardless of budget */
|
|
143
|
+
runsPerCaseCeiling: number;
|
|
144
|
+
/**
|
|
145
|
+
* Measured recall of the screen, per screen kind. `null` means nobody has
|
|
146
|
+
* measured it, and a configuration using that screen will be reported but
|
|
147
|
+
* never *recommended*.
|
|
148
|
+
*/
|
|
149
|
+
screenRecall: {
|
|
150
|
+
expensive: number | null;
|
|
151
|
+
proxy: number | null;
|
|
152
|
+
};
|
|
153
|
+
/** free text saying where `screenRecall` came from; printed next to it */
|
|
154
|
+
screenRecallSource: string;
|
|
155
|
+
/** the sequential design being priced; match the stopping rule you actually run */
|
|
156
|
+
alpha: number;
|
|
157
|
+
beta: number;
|
|
158
|
+
minTrials: number;
|
|
159
|
+
maxTrials: number;
|
|
160
|
+
batchSize: number;
|
|
161
|
+
nullQuantile: number;
|
|
162
|
+
/** the `futilityConfidence` `shouldStopPaired` runs at; unset takes its default */
|
|
163
|
+
futilityConfidence?: number;
|
|
164
|
+
}
|
|
165
|
+
export declare const DEFAULT_FRONTIER: FrontierConfig;
|
|
166
|
+
/** A cost broken into the three things a pull request actually pays for. */
|
|
167
|
+
export interface FrontierCost {
|
|
168
|
+
screenRuns: number;
|
|
169
|
+
screenUsd: number;
|
|
170
|
+
testRuns: number;
|
|
171
|
+
testUsd: number;
|
|
172
|
+
/** baseline runs charged to *this* pull request after amortisation */
|
|
173
|
+
baselineRunsAmortised: number;
|
|
174
|
+
baselineUsd: number;
|
|
175
|
+
totalUsd: number;
|
|
176
|
+
}
|
|
177
|
+
export interface FrontierPoint {
|
|
178
|
+
cases: number;
|
|
179
|
+
mde: number;
|
|
180
|
+
baselineRuns: number;
|
|
181
|
+
design: FrontierDesign;
|
|
182
|
+
screen: FrontierScreen;
|
|
183
|
+
/** 0 when `screen` is `none` */
|
|
184
|
+
screenRunsPerCase: number;
|
|
185
|
+
/** how many cases the screen carries through */
|
|
186
|
+
keep: number;
|
|
187
|
+
/** e-BH solo bar, in logs */
|
|
188
|
+
barLogE: number;
|
|
189
|
+
/** unpaired evidence ceiling at this baseline size, in logs. −∞ is not used;
|
|
190
|
+
* the paired design has no ceiling and this field is `null` for it. */
|
|
191
|
+
ceilingLogE: number | null;
|
|
192
|
+
feasible: boolean;
|
|
193
|
+
/** why not, when not. Never empty when `feasible` is false. */
|
|
194
|
+
infeasibleBecause: string | null;
|
|
195
|
+
/** observations per case to certify a case that really regressed */
|
|
196
|
+
certifyObservations: number;
|
|
197
|
+
/** observations per case on a pull request that changed nothing */
|
|
198
|
+
typicalObservations: number;
|
|
199
|
+
/**
|
|
200
|
+
* The per-case run cap this configuration needs. A `shouldStop` `maxTrials`
|
|
201
|
+
* below it stops cases short with reason `budget`, however much money is left.
|
|
202
|
+
*/
|
|
203
|
+
requiresMaxRuns: number;
|
|
204
|
+
/** true when `requiresMaxRuns` exceeds the gate's configured cap */
|
|
205
|
+
exceedsGateCap: boolean;
|
|
206
|
+
/** a pull request that changed nothing, the number to budget from */
|
|
207
|
+
typical: FrontierCost;
|
|
208
|
+
/** every case regressed by exactly the MDE and had to be certified. A scenario, not a bound. */
|
|
209
|
+
certifyAll: FrontierCost;
|
|
210
|
+
/** every kept case ran to the per-case cap, the bill nothing can exceed */
|
|
211
|
+
ceiling: FrontierCost;
|
|
212
|
+
/** whichever of the three the configured `basis` selects, what the budget is checked against */
|
|
213
|
+
basisCostUsd: number;
|
|
214
|
+
withinBudget: boolean;
|
|
215
|
+
/** measured recall of this configuration's screen; null when unmeasured */
|
|
216
|
+
screenRecall: number | null;
|
|
217
|
+
/** true when this configuration may be *recommended*, not merely reported */
|
|
218
|
+
recommendable: boolean;
|
|
219
|
+
/** why it is reported but not recommendable */
|
|
220
|
+
caveat: string | null;
|
|
221
|
+
}
|
|
222
|
+
/** $1.00, but $0.0021 rather than $0.00, a per-run price rounds to nothing. */
|
|
223
|
+
export declare const fmtRate: (x: number) => string;
|
|
224
|
+
/**
|
|
225
|
+
* Observations per case on a pull request where nothing moved, clamped onto the
|
|
226
|
+
* gate's own batch grid.
|
|
227
|
+
*/
|
|
228
|
+
export declare function typicalObservationsPerCase(rate: number, mde: number, cfg: FrontierConfig, baselineRuns: number): number;
|
|
229
|
+
export declare function evaluatePoint(cases: number, mde: number, baselineRuns: number, design: FrontierDesign, screen: FrontierScreen, screenRunsPerCase: number, cfg: FrontierConfig): FrontierPoint;
|
|
230
|
+
/** Every point in the swept space, feasible or not. */
|
|
231
|
+
export declare function enumerateFrontier(cfg: FrontierConfig, axes?: FrontierAxes): FrontierPoint[];
|
|
232
|
+
export interface FrontierCell {
|
|
233
|
+
cases: number;
|
|
234
|
+
mde: number;
|
|
235
|
+
/** the cheapest recommendable configuration that certifies this cell */
|
|
236
|
+
best: FrontierPoint;
|
|
237
|
+
/** the cheapest configuration ignoring the recommendable rule, when it differs */
|
|
238
|
+
cheapestReported: FrontierPoint | null;
|
|
239
|
+
}
|
|
240
|
+
export interface FrontierResult {
|
|
241
|
+
config: FrontierConfig;
|
|
242
|
+
axes: FrontierAxes;
|
|
243
|
+
/** everything swept */
|
|
244
|
+
points: FrontierPoint[];
|
|
245
|
+
/** feasible, within budget, and recommendable */
|
|
246
|
+
affordable: FrontierPoint[];
|
|
247
|
+
/**
|
|
248
|
+
* The frontier proper: the (cases, mde) cells that are achievable within
|
|
249
|
+
* budget and not dominated by another achievable cell, i.e. no other cell
|
|
250
|
+
* has at least as many cases *and* an MDE at least as tight.
|
|
251
|
+
*/
|
|
252
|
+
frontier: FrontierCell[];
|
|
253
|
+
/** cheapest feasible configuration overall, whether or not it fits */
|
|
254
|
+
cheapestFeasible: FrontierPoint | null;
|
|
255
|
+
/** the smallest budget at which anything at all becomes recommendable */
|
|
256
|
+
minimumViableBudgetUsd: number | null;
|
|
257
|
+
headline: string;
|
|
258
|
+
}
|
|
259
|
+
export declare function computeFrontier(cfg: FrontierConfig, axes?: FrontierAxes): FrontierResult;
|
|
260
|
+
/**
|
|
261
|
+
* Where the money goes, for one configuration. Printed under the frontier
|
|
262
|
+
* because the shares are the actual finding: at coding-agent prices the
|
|
263
|
+
* unpaired design's amortised baseline is usually the largest line, and it is
|
|
264
|
+
* the one the README used to call free.
|
|
265
|
+
*/
|
|
266
|
+
export declare function costShares(p: FrontierPoint, basis?: FrontierBasis): {
|
|
267
|
+
screen: number;
|
|
268
|
+
test: number;
|
|
269
|
+
baseline: number;
|
|
270
|
+
};
|
|
271
|
+
//# sourceMappingURL=frontier.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontier.d.ts","sourceRoot":"","sources":["../src/frontier.ts"],"names":[],"mappings":"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"}
|