@tangle-network/agent-eval 0.120.0 → 0.120.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/CHANGELOG.md +12 -0
- package/package.json +1 -1
- package/dist/analyst/index.d.ts +0 -3111
- package/dist/analyst/index.js +0 -403
- package/dist/analyst/index.js.map +0 -1
- package/dist/authenticity/index.d.ts +0 -161
- package/dist/authenticity/index.js +0 -215
- package/dist/authenticity/index.js.map +0 -1
- package/dist/belief-state/index.d.ts +0 -1301
- package/dist/belief-state/index.js +0 -2152
- package/dist/belief-state/index.js.map +0 -1
- package/dist/benchmarks/index.d.ts +0 -974
- package/dist/benchmarks/index.js +0 -60
- package/dist/benchmarks/index.js.map +0 -1
- package/dist/builder-eval/index.d.ts +0 -695
- package/dist/builder-eval/index.js +0 -366
- package/dist/builder-eval/index.js.map +0 -1
- package/dist/campaign/index.d.ts +0 -7454
- package/dist/campaign/index.js +0 -272
- package/dist/campaign/index.js.map +0 -1
- package/dist/chunk-32BZXMSO.js +0 -3878
- package/dist/chunk-32BZXMSO.js.map +0 -1
- package/dist/chunk-3A246TSA.js +0 -998
- package/dist/chunk-3A246TSA.js.map +0 -1
- package/dist/chunk-3RF76KTD.js +0 -84
- package/dist/chunk-3RF76KTD.js.map +0 -1
- package/dist/chunk-3YYRZDON.js +0 -45
- package/dist/chunk-3YYRZDON.js.map +0 -1
- package/dist/chunk-4I2E3LLO.js +0 -1030
- package/dist/chunk-4I2E3LLO.js.map +0 -1
- package/dist/chunk-ARU2PZFM.js +0 -312
- package/dist/chunk-ARU2PZFM.js.map +0 -1
- package/dist/chunk-BOD4O7OF.js +0 -40
- package/dist/chunk-BOD4O7OF.js.map +0 -1
- package/dist/chunk-DPZAEKA6.js +0 -880
- package/dist/chunk-DPZAEKA6.js.map +0 -1
- package/dist/chunk-DTJ6QUQB.js +0 -131
- package/dist/chunk-DTJ6QUQB.js.map +0 -1
- package/dist/chunk-GGE4NNQT.js +0 -65
- package/dist/chunk-GGE4NNQT.js.map +0 -1
- package/dist/chunk-H5UD2323.js +0 -286
- package/dist/chunk-H5UD2323.js.map +0 -1
- package/dist/chunk-HHWE3POT.js +0 -94
- package/dist/chunk-HHWE3POT.js.map +0 -1
- package/dist/chunk-HKUCJ437.js +0 -787
- package/dist/chunk-HKUCJ437.js.map +0 -1
- package/dist/chunk-JHCHEVET.js +0 -274
- package/dist/chunk-JHCHEVET.js.map +0 -1
- package/dist/chunk-JHOJHHU7.js +0 -867
- package/dist/chunk-JHOJHHU7.js.map +0 -1
- package/dist/chunk-JM2SKQMS.js +0 -750
- package/dist/chunk-JM2SKQMS.js.map +0 -1
- package/dist/chunk-JN2FCO5W.js +0 -7958
- package/dist/chunk-JN2FCO5W.js.map +0 -1
- package/dist/chunk-K4DBDHLK.js +0 -158
- package/dist/chunk-K4DBDHLK.js.map +0 -1
- package/dist/chunk-K6N6XJJX.js +0 -306
- package/dist/chunk-K6N6XJJX.js.map +0 -1
- package/dist/chunk-MA6HLL3S.js +0 -65
- package/dist/chunk-MA6HLL3S.js.map +0 -1
- package/dist/chunk-MAZ26DC7.js +0 -99
- package/dist/chunk-MAZ26DC7.js.map +0 -1
- package/dist/chunk-MOXWMGPC.js +0 -577
- package/dist/chunk-MOXWMGPC.js.map +0 -1
- package/dist/chunk-NJC7U437.js +0 -626
- package/dist/chunk-NJC7U437.js.map +0 -1
- package/dist/chunk-NPCTHQIO.js +0 -91
- package/dist/chunk-NPCTHQIO.js.map +0 -1
- package/dist/chunk-ONWEPEDO.js +0 -57
- package/dist/chunk-ONWEPEDO.js.map +0 -1
- package/dist/chunk-OYZAPX5G.js +0 -1526
- package/dist/chunk-OYZAPX5G.js.map +0 -1
- package/dist/chunk-PC4UYEBM.js +0 -166
- package/dist/chunk-PC4UYEBM.js.map +0 -1
- package/dist/chunk-PICTDURQ.js +0 -766
- package/dist/chunk-PICTDURQ.js.map +0 -1
- package/dist/chunk-PJQFMIOX.js +0 -1182
- package/dist/chunk-PJQFMIOX.js.map +0 -1
- package/dist/chunk-PXD6ZFNY.js +0 -1107
- package/dist/chunk-PXD6ZFNY.js.map +0 -1
- package/dist/chunk-PXE2VKMX.js +0 -140
- package/dist/chunk-PXE2VKMX.js.map +0 -1
- package/dist/chunk-PZ5AY32C.js +0 -10
- package/dist/chunk-PZ5AY32C.js.map +0 -1
- package/dist/chunk-QBRSJK47.js +0 -622
- package/dist/chunk-QBRSJK47.js.map +0 -1
- package/dist/chunk-QWMPPZ3X.js +0 -550
- package/dist/chunk-QWMPPZ3X.js.map +0 -1
- package/dist/chunk-S3UZOQ5Y.js +0 -328
- package/dist/chunk-S3UZOQ5Y.js.map +0 -1
- package/dist/chunk-S5TT5R3L.js +0 -2668
- package/dist/chunk-S5TT5R3L.js.map +0 -1
- package/dist/chunk-T4SQEITX.js +0 -95
- package/dist/chunk-T4SQEITX.js.map +0 -1
- package/dist/chunk-TT4KNT67.js +0 -124
- package/dist/chunk-TT4KNT67.js.map +0 -1
- package/dist/chunk-U5CHZ5M3.js +0 -357
- package/dist/chunk-U5CHZ5M3.js.map +0 -1
- package/dist/chunk-ULOKLHIQ.js +0 -1937
- package/dist/chunk-ULOKLHIQ.js.map +0 -1
- package/dist/chunk-VI2UW6B6.js +0 -162
- package/dist/chunk-VI2UW6B6.js.map +0 -1
- package/dist/chunk-VQMK5FMP.js +0 -247
- package/dist/chunk-VQMK5FMP.js.map +0 -1
- package/dist/chunk-VSMTAMNK.js +0 -53
- package/dist/chunk-VSMTAMNK.js.map +0 -1
- package/dist/chunk-VZSRQ272.js +0 -149
- package/dist/chunk-VZSRQ272.js.map +0 -1
- package/dist/chunk-WW2A73HW.js +0 -159
- package/dist/chunk-WW2A73HW.js.map +0 -1
- package/dist/chunk-X4UCIOTZ.js +0 -136
- package/dist/chunk-X4UCIOTZ.js.map +0 -1
- package/dist/chunk-XDIRG3TO.js +0 -1266
- package/dist/chunk-XDIRG3TO.js.map +0 -1
- package/dist/chunk-XJYR7XFV.js +0 -317
- package/dist/chunk-XJYR7XFV.js.map +0 -1
- package/dist/chunk-ZET2UAYW.js +0 -89
- package/dist/chunk-ZET2UAYW.js.map +0 -1
- package/dist/chunk-ZZUXHH3R.js +0 -99
- package/dist/chunk-ZZUXHH3R.js.map +0 -1
- package/dist/cli.d.ts +0 -1
- package/dist/cli.js +0 -112
- package/dist/cli.js.map +0 -1
- package/dist/contract/index.d.ts +0 -4969
- package/dist/contract/index.js +0 -1653
- package/dist/contract/index.js.map +0 -1
- package/dist/control.d.ts +0 -1013
- package/dist/control.js +0 -34
- package/dist/control.js.map +0 -1
- package/dist/fuzz.d.ts +0 -759
- package/dist/fuzz.js +0 -714
- package/dist/fuzz.js.map +0 -1
- package/dist/hosted/index.d.ts +0 -730
- package/dist/hosted/index.js +0 -14
- package/dist/hosted/index.js.map +0 -1
- package/dist/index.d.ts +0 -16780
- package/dist/index.js +0 -12168
- package/dist/index.js.map +0 -1
- package/dist/matrix/index.d.ts +0 -155
- package/dist/matrix/index.js +0 -8
- package/dist/matrix/index.js.map +0 -1
- package/dist/meta-eval/index.d.ts +0 -1030
- package/dist/meta-eval/index.js +0 -417
- package/dist/meta-eval/index.js.map +0 -1
- package/dist/multishot/index.d.ts +0 -579
- package/dist/multishot/index.js +0 -589
- package/dist/multishot/index.js.map +0 -1
- package/dist/openapi.json +0 -992
- package/dist/pipelines/index.d.ts +0 -567
- package/dist/pipelines/index.js +0 -515
- package/dist/pipelines/index.js.map +0 -1
- package/dist/reporting.d.ts +0 -1277
- package/dist/reporting.js +0 -48
- package/dist/reporting.js.map +0 -1
- package/dist/rl.d.ts +0 -4092
- package/dist/rl.js +0 -1724
- package/dist/rl.js.map +0 -1
- package/dist/run-campaign-HNFPJET4.js +0 -14
- package/dist/run-campaign-HNFPJET4.js.map +0 -1
- package/dist/storyboard/index.d.ts +0 -279
- package/dist/storyboard/index.js +0 -767
- package/dist/storyboard/index.js.map +0 -1
- package/dist/trace-attributes.d.ts +0 -52
- package/dist/trace-attributes.js +0 -62
- package/dist/trace-attributes.js.map +0 -1
- package/dist/traces.d.ts +0 -2343
- package/dist/traces.js +0 -249
- package/dist/traces.js.map +0 -1
- package/dist/wire/index.d.ts +0 -1252
- package/dist/wire/index.js +0 -81
- package/dist/wire/index.js.map +0 -1
package/dist/reporting.d.ts
DELETED
|
@@ -1,1277 +0,0 @@
|
|
|
1
|
-
type AgentProfileCellSchemaVersion = 'agent-profile-cell/v1';
|
|
2
|
-
type AgentProfileDimensionValue = string | number | boolean | null;
|
|
3
|
-
interface AgentProfileSource {
|
|
4
|
-
/** Runtime/profile contract being fingerprinted, e.g. `agent-interface-profile`. */
|
|
5
|
-
kind: string;
|
|
6
|
-
/** sha256 over the canonical source profile object. */
|
|
7
|
-
hash: string;
|
|
8
|
-
}
|
|
9
|
-
interface AgentProfileHarness {
|
|
10
|
-
id: string;
|
|
11
|
-
version?: string;
|
|
12
|
-
hash?: string;
|
|
13
|
-
}
|
|
14
|
-
interface AgentProfileCell {
|
|
15
|
-
schemaVersion: AgentProfileCellSchemaVersion;
|
|
16
|
-
cellId: string;
|
|
17
|
-
profileId: string;
|
|
18
|
-
sourceProfile: AgentProfileSource;
|
|
19
|
-
harness?: AgentProfileHarness;
|
|
20
|
-
model?: string;
|
|
21
|
-
promptHash?: string;
|
|
22
|
-
dimensions?: Record<string, AgentProfileDimensionValue>;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
type FailureClass = 'success' | 'reasoning_error' | 'tool_selection_error' | 'tool_argument_error' | 'tool_recovery_failure' | 'hallucination' | 'instruction_following' | 'safety_refusal_miss' | 'policy_violation' | 'budget_exceeded' | 'format_drift' | 'permission_escalation' | 'pii_leak' | 'cost_overrun' | 'timeout' | 'sandbox_failure' | 'missing_user_data' | 'missing_domain_data' | 'missing_codebase_context' | 'missing_runtime_context' | 'missing_credentials' | 'missing_integration_connection' | 'missing_integration_scope' | 'integration_approval_required' | 'integration_auth_expired' | 'integration_provider_failure' | 'bad_integration_manifest' | 'unsafe_integration_write_denied' | 'stale_external_data' | 'bad_retrieval' | 'insufficient_evidence' | 'contradictory_evidence' | 'ambiguous_user_intent' | 'knowledge_readiness_blocked' | 'unknown';
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Paper-grade RunRecord schema + runtime validator.
|
|
29
|
-
*
|
|
30
|
-
* Every run that participates in a promotion gate, paper table, or
|
|
31
|
-
* researcher loop SHOULD be recorded as a `RunRecord`. The mandatory
|
|
32
|
-
* fields are exactly those the paper "Two Loops, Three Roles" requires
|
|
33
|
-
* for reproducibility: who/what/when/cost/seed/hash, plus the search vs
|
|
34
|
-
* holdout split tag and either a `searchScore` or a `holdoutScore`.
|
|
35
|
-
*
|
|
36
|
-
* This is intentionally NOT a replacement for the rich `Run` /
|
|
37
|
-
* `ProposeReviewReport` / `ScenarioResult` types already in the
|
|
38
|
-
* package. Those are runtime structures with full provenance. A
|
|
39
|
-
* `RunRecord` is the analysis-time projection — the JSON-friendly
|
|
40
|
-
* row you'd put in a parquet file or paste into a notebook.
|
|
41
|
-
*
|
|
42
|
-
* Validate at the boundary:
|
|
43
|
-
*
|
|
44
|
-
* const rec = validateRunRecord(rawJson) // throws on missing
|
|
45
|
-
* const ok = isRunRecord(rawJson) // boolean check
|
|
46
|
-
* const rec = parseRunRecordSafe(rawJson) // { ok, value | error }
|
|
47
|
-
*
|
|
48
|
-
* The validator runs in pure TS — zod is intentionally NOT a
|
|
49
|
-
* dependency. Round-trip tested in `tests/run-record.test.ts`.
|
|
50
|
-
*/
|
|
51
|
-
|
|
52
|
-
/** Search/dev/holdout split tag. 'search' is the paper-grade alias for the
|
|
53
|
-
* combined train+test pool that the optimizer is allowed to read. */
|
|
54
|
-
type RunSplitTag = 'search' | 'dev' | 'holdout';
|
|
55
|
-
interface RunTokenUsage {
|
|
56
|
-
input: number;
|
|
57
|
-
/** All generated tokens charged as output, including reasoning tokens. */
|
|
58
|
-
output: number;
|
|
59
|
-
/** Reasoning-token subset of `output`, when the provider reports it. */
|
|
60
|
-
reasoning?: number;
|
|
61
|
-
/** Prompt tokens served from a provider cache. */
|
|
62
|
-
cached?: number;
|
|
63
|
-
/** Prompt tokens written into a provider cache. */
|
|
64
|
-
cacheWrite?: number;
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* How a run's USD amount was obtained.
|
|
68
|
-
*
|
|
69
|
-
* `costUsd` remains mandatory for wire compatibility. New producers should
|
|
70
|
-
* always populate this discriminated union so a missing bill is never
|
|
71
|
-
* mistaken for an observed zero-dollar run. For `uncaptured`, `costUsd` uses
|
|
72
|
-
* the legacy `0` sentinel while this field carries the truthful null.
|
|
73
|
-
*/
|
|
74
|
-
type RunCostProvenance = {
|
|
75
|
-
kind: 'observed';
|
|
76
|
-
usd: number;
|
|
77
|
-
} | {
|
|
78
|
-
kind: 'estimated';
|
|
79
|
-
usd: number;
|
|
80
|
-
} | {
|
|
81
|
-
kind: 'uncaptured';
|
|
82
|
-
usd: null;
|
|
83
|
-
};
|
|
84
|
-
interface RunJudgeMetadata {
|
|
85
|
-
model: string;
|
|
86
|
-
promptVersion: string;
|
|
87
|
-
/** [0,1] confidence the judge declared. Constant judge confidence
|
|
88
|
-
* across many runs is a fallback signal (see `canary.ts`). */
|
|
89
|
-
confidence: number;
|
|
90
|
-
/** True if the judge degraded to a fallback path (rules-only,
|
|
91
|
-
* prior-call cache, etc.). The canary uses this to alert. */
|
|
92
|
-
fallback: boolean;
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Per-judge / per-dimension breakdown for runs scored by an ensemble of
|
|
96
|
-
* judges over a multi-dimensional rubric.
|
|
97
|
-
*
|
|
98
|
-
* The collapsed `outcome.searchScore` / `holdoutScore` carries the
|
|
99
|
-
* composite the gate uses. The full breakdown belongs here so consumers
|
|
100
|
-
* can answer "which judge disagreed?", "which dimension dragged the
|
|
101
|
-
* composite down?", and "did half the panel fail?" without re-running.
|
|
102
|
-
*
|
|
103
|
-
* `perJudge[judgeId][dim]` is the canonical source; `perDimMean` and
|
|
104
|
-
* `composite` are convenience projections — derivable but precomputed so
|
|
105
|
-
* downstream IRR primitives (`interRaterReliability`,
|
|
106
|
-
* `corpusInterRaterAgreement`) and reporters don't pay the same
|
|
107
|
-
* aggregation twice.
|
|
108
|
-
*
|
|
109
|
-
* Fail-loud discipline: judges that errored out land in `failedJudges`
|
|
110
|
-
* by id. A missing key in `perJudge` is ambiguous (silent zero vs not
|
|
111
|
-
* run); the explicit list makes a partial-failure recorded as such.
|
|
112
|
-
*/
|
|
113
|
-
interface JudgeScoresRecord {
|
|
114
|
-
/** Per-judge per-dimension scores. `{ "kimi-k2.6": { helpfulness: 0.8, clarity: 0.7 }, ... }`. */
|
|
115
|
-
perJudge: Record<string, Record<string, number>>;
|
|
116
|
-
/** Per-dim mean across judges. Convenience — derivable from `perJudge`. */
|
|
117
|
-
perDimMean: Record<string, number>;
|
|
118
|
-
/** Composite mean across all dims and judges. Mirrors the score
|
|
119
|
-
* the gate sees on `outcome.searchScore` / `holdoutScore`. */
|
|
120
|
-
composite: number;
|
|
121
|
-
/** Judges that errored or returned an unparseable verdict. Recorded
|
|
122
|
-
* by id (e.g. `['glm-5.1']`) so a partial-failure case is explicit,
|
|
123
|
-
* not inferred from missing keys in `perJudge`. */
|
|
124
|
-
failedJudges?: string[];
|
|
125
|
-
/** Free-form notes the judges emitted (joined across judges or
|
|
126
|
-
* first-judge only — consumer's choice). */
|
|
127
|
-
notes?: string;
|
|
128
|
-
}
|
|
129
|
-
interface RunOutcome {
|
|
130
|
-
/** Score on the search/optimization split. Optional because a
|
|
131
|
-
* holdout-only evaluation only fills `holdoutScore`. */
|
|
132
|
-
searchScore?: number;
|
|
133
|
-
/** Score on the held-out split. Optional because a search-only run
|
|
134
|
-
* only fills `searchScore`. At least one must be present. */
|
|
135
|
-
holdoutScore?: number;
|
|
136
|
-
/** Bag of any other metric the run produced — judge dimensions,
|
|
137
|
-
* pass/fail counters, latency stats, etc. Numeric only — keeps
|
|
138
|
-
* reporters honest. */
|
|
139
|
-
raw: Record<string, number>;
|
|
140
|
-
/** Per-judge / per-dim breakdown. Consumers writing ensemble
|
|
141
|
-
* judgements populate this; substrate primitives like
|
|
142
|
-
* `interRaterReliability` and `corpusInterRaterAgreement` accept
|
|
143
|
-
* these records as input. Optional — single-judge or scalar-only
|
|
144
|
-
* runs leave it unset. */
|
|
145
|
-
judgeScores?: JudgeScoresRecord;
|
|
146
|
-
/** Authenticity / realness verdict — did the run build the REAL thing on the
|
|
147
|
-
* intended infra, or fake it (see `./authenticity`)? Optional: only domains
|
|
148
|
-
* with an authenticity config populate it. Carried in the corpus so the
|
|
149
|
-
* flywheel / off-policy learning can optimize for real completion, not gamed
|
|
150
|
-
* pass-rate. `score` is 0-1; `gated` is the anti-Goodhart flag — a gated run
|
|
151
|
-
* must not count as a real success regardless of `score`. */
|
|
152
|
-
realness?: {
|
|
153
|
-
score: number;
|
|
154
|
-
gated: boolean;
|
|
155
|
-
reason?: string;
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
/**
|
|
159
|
-
* Mandatory paper-grade fields for a single evaluation run. Optional
|
|
160
|
-
* fields are extension points; mandatory fields throw if missing.
|
|
161
|
-
*
|
|
162
|
-
* Hash discipline:
|
|
163
|
-
* - `promptHash` is the sha256 of the EFFECTIVE prompt sent to the
|
|
164
|
-
* model (after any steering bundle merge).
|
|
165
|
-
* - `configHash` is the sha256 of the effective run config (model,
|
|
166
|
-
* temperature, tools, judges, splits). The pair (promptHash,
|
|
167
|
-
* configHash) uniquely identifies an experiment cell.
|
|
168
|
-
*
|
|
169
|
-
* Model snapshot discipline:
|
|
170
|
-
* - `model` MUST encode a snapshot version. Bare aliases like
|
|
171
|
-
* `claude-sonnet-4` or `gpt-4o` are banned — they remap silently.
|
|
172
|
-
* Use `claude-sonnet-4-6@2025-04-15` or `gpt-4o-2024-11-20`.
|
|
173
|
-
*/
|
|
174
|
-
interface RunRecord {
|
|
175
|
-
/** UUID for the run. */
|
|
176
|
-
runId: string;
|
|
177
|
-
/** Logical experiment grouping (a treatment vs a baseline within
|
|
178
|
-
* the same sweep should share `experimentId`). */
|
|
179
|
-
experimentId: string;
|
|
180
|
-
/** Stable identifier for the candidate (variant) being run. The
|
|
181
|
-
* promotion gate compares two `candidateId`s on matched items. */
|
|
182
|
-
candidateId: string;
|
|
183
|
-
/** RNG seed for the run. Always recorded — silent re-seeding is
|
|
184
|
-
* the most common cause of non-reproducible numbers. */
|
|
185
|
-
seed: number;
|
|
186
|
-
/** Model identifier WITH snapshot version. */
|
|
187
|
-
model: string;
|
|
188
|
-
/** sha256 of the effective prompt (post-steering). */
|
|
189
|
-
promptHash: string;
|
|
190
|
-
/** sha256 of the effective config. */
|
|
191
|
-
configHash: string;
|
|
192
|
-
/** Git SHA the harness was run from. */
|
|
193
|
-
commitSha: string;
|
|
194
|
-
/** End-to-end wall-clock duration in milliseconds. */
|
|
195
|
-
wallMs: number;
|
|
196
|
-
/** Time spent queued before execution started, if known. */
|
|
197
|
-
queueMs?: number;
|
|
198
|
-
/** Total USD cost. Mandatory — runs without a cost number are
|
|
199
|
-
* unbounded by definition and must not be admitted into the gate.
|
|
200
|
-
* `0` is retained as the compatibility sentinel for an uncaptured amount;
|
|
201
|
-
* inspect `costProvenance` before treating it as observed. */
|
|
202
|
-
costUsd: number;
|
|
203
|
-
/** Observed, model-priced estimate, or genuinely uncaptured USD amount.
|
|
204
|
-
* Optional only so existing serialized RunRecords remain valid. */
|
|
205
|
-
costProvenance?: RunCostProvenance;
|
|
206
|
-
/** Token usage breakdown. */
|
|
207
|
-
tokenUsage: RunTokenUsage;
|
|
208
|
-
/** Judge-side metadata, if a judge was used. */
|
|
209
|
-
judgeMetadata?: RunJudgeMetadata;
|
|
210
|
-
/** Per-split scores + raw bag. */
|
|
211
|
-
outcome: RunOutcome;
|
|
212
|
-
/** Canonical, cross-agent failure class drawn from the shared
|
|
213
|
-
* `FAILURE_CLASSES` taxonomy. This is the aggregation key that makes
|
|
214
|
-
* "which failure dominates across the whole fleet" answerable in ONE
|
|
215
|
-
* vocabulary — every agent classifies against the same enum. Producers
|
|
216
|
-
* set it via the substrate classifier; leave unset only when the failure
|
|
217
|
-
* genuinely can't be classified. */
|
|
218
|
-
failureClass?: FailureClass;
|
|
219
|
-
/** Free-form domain-specific failure detail, scoped UNDER `failureClass`
|
|
220
|
-
* (e.g. failureClass='tool_recovery_failure', failureMode='forge_build_unsatisfied').
|
|
221
|
-
* The within-agent drill-down; `failureClass` is the cross-agent key. */
|
|
222
|
-
failureMode?: string;
|
|
223
|
-
/** Which split this run was drawn from. */
|
|
224
|
-
splitTag: RunSplitTag;
|
|
225
|
-
/**
|
|
226
|
-
* Stable scenario identifier the run was scored against. Optional for
|
|
227
|
-
* backwards compatibility, but **strongly recommended**: every primitive
|
|
228
|
-
* that pairs runs by scenario (preferences, paired stats, BT tournament)
|
|
229
|
-
* keys on this. The campaign artifact populates it canonically; legacy
|
|
230
|
-
* runs without it fall back to inference from `outcome.raw.scenario_id`
|
|
231
|
-
* or `experimentId`.
|
|
232
|
-
*/
|
|
233
|
-
scenarioId?: string;
|
|
234
|
-
/**
|
|
235
|
-
* Canonical identity for the agent profile cell that produced this row:
|
|
236
|
-
* profile artifact hash plus optional harness/model/prompt/reporting
|
|
237
|
-
* dimensions. Use `agentProfile.cellId` to group persona sweeps and
|
|
238
|
-
* longitudinal reports by the complete source profile, not by a loose
|
|
239
|
-
* candidate label or opaque config hash.
|
|
240
|
-
*/
|
|
241
|
-
agentProfile?: AgentProfileCell;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* OutcomeStore — deployment outcomes attached to Run IDs.
|
|
246
|
-
*
|
|
247
|
-
* Outcomes arrive asynchronously from production telemetry after the
|
|
248
|
-
* eval run completed: user ratings, retention flags, conversion events,
|
|
249
|
-
* revenue, support-ticket rate, anything a product team can measure.
|
|
250
|
-
* The store is a peer to TraceStore — separate lifecycle, same runId
|
|
251
|
-
* foreign key.
|
|
252
|
-
*
|
|
253
|
-
* The whole point of this module is to make the meta-eval correlation
|
|
254
|
-
* question computable: `correlate(evalMetric, outcomeMetric) → r, ρ, n, CI`.
|
|
255
|
-
*/
|
|
256
|
-
interface DeploymentOutcome {
|
|
257
|
-
runId: string;
|
|
258
|
-
capturedAt: number;
|
|
259
|
-
/** Numeric outcomes keyed by name — retention_7d, csat, revenue_usd, etc. */
|
|
260
|
-
metrics: Record<string, number>;
|
|
261
|
-
/** Dimensions for stratified analysis — cohort, region, user_segment. */
|
|
262
|
-
labels?: Record<string, string>;
|
|
263
|
-
/** Free-form provenance (source system, pipeline version). */
|
|
264
|
-
source?: string;
|
|
265
|
-
}
|
|
266
|
-
interface OutcomeFilter {
|
|
267
|
-
runIds?: string[];
|
|
268
|
-
since?: number;
|
|
269
|
-
until?: number;
|
|
270
|
-
label?: {
|
|
271
|
-
key: string;
|
|
272
|
-
value: string;
|
|
273
|
-
};
|
|
274
|
-
source?: string;
|
|
275
|
-
}
|
|
276
|
-
interface OutcomeStore {
|
|
277
|
-
append(outcome: DeploymentOutcome): Promise<void>;
|
|
278
|
-
/** All outcomes attached to this run (a single run can have many — multiple
|
|
279
|
-
* capture windows over deployment time). */
|
|
280
|
-
forRun(runId: string): Promise<DeploymentOutcome[]>;
|
|
281
|
-
list(filter?: OutcomeFilter): Promise<DeploymentOutcome[]>;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
/**
|
|
285
|
-
* Rubric predictive validity — does our eval rubric predict deployment
|
|
286
|
-
* outcomes?
|
|
287
|
-
*
|
|
288
|
-
* `correlationStudy` (already in this package) joins a `TraceStore` to an
|
|
289
|
-
* `OutcomeStore` and computes Pearson + Spearman + bootstrap CI for each
|
|
290
|
-
* (eval-metric, outcome-metric) pair. That answers "does X correlate with
|
|
291
|
-
* Y at all." `rubricPredictiveValidity` is the campaign-shaped wrapper
|
|
292
|
-
* around it: take a sequence of `RunRecord`s (the canonical campaign
|
|
293
|
-
* artifact) and a `DeploymentOutcomeStore`, join on `runId`, return a
|
|
294
|
-
* ranked verdict on every rubric whose dimension scores were captured in
|
|
295
|
-
* `outcome.raw`.
|
|
296
|
-
*
|
|
297
|
-
* The point — quoting the methodology doc — is that **without this loop
|
|
298
|
-
* every rubric is faith-based**. Once it's wired, you know which rubrics
|
|
299
|
-
* have earned their promotion power and which ones are decoration.
|
|
300
|
-
*
|
|
301
|
-
* const validity = await rubricPredictiveValidity({
|
|
302
|
-
* runs: lastQuarter,
|
|
303
|
-
* outcomes: shipFlagOutcomeStore,
|
|
304
|
-
* outcomeMetrics: ['revenue_lift', 'retention_30d', 'csat'],
|
|
305
|
-
* rubrics: ['anti_slop', 'semantic_concept', 'tool_recovery'],
|
|
306
|
-
* })
|
|
307
|
-
* for (const r of validity.ranked) {
|
|
308
|
-
* console.log(`${r.rubric} → ${r.bestOutcome}: ρ=${r.spearman.toFixed(2)}`)
|
|
309
|
-
* }
|
|
310
|
-
*
|
|
311
|
-
* The function is intentionally read-only. Use the verdict to deprecate
|
|
312
|
-
* decorative rubrics, re-weight composite scores, or trigger a
|
|
313
|
-
* recalibration sweep when predictive validity drops below a threshold.
|
|
314
|
-
*/
|
|
315
|
-
|
|
316
|
-
interface RubricPredictiveValidityInput {
|
|
317
|
-
/**
|
|
318
|
-
* Canonical campaign output. Each record's `outcome.raw[<rubricId>]`
|
|
319
|
-
* provides the eval score; missing keys are silently skipped per pair.
|
|
320
|
-
*/
|
|
321
|
-
runs: RunRecord[];
|
|
322
|
-
outcomes: OutcomeStore;
|
|
323
|
-
/**
|
|
324
|
-
* Outcome metric names to evaluate against. Each must appear in at
|
|
325
|
-
* least one `DeploymentOutcome.metrics` keyspace; pairs with too few
|
|
326
|
-
* joined samples are excluded from the result.
|
|
327
|
-
*/
|
|
328
|
-
outcomeMetrics: string[];
|
|
329
|
-
/**
|
|
330
|
-
* Rubric ids to evaluate. Must appear as keys in `RunRecord.outcome.raw`.
|
|
331
|
-
* If omitted, every numeric key in `outcome.raw` across the run set is
|
|
332
|
-
* treated as a rubric.
|
|
333
|
-
*/
|
|
334
|
-
rubrics?: string[];
|
|
335
|
-
/** Minimum joined-sample count before a pair is reported. Default 8. */
|
|
336
|
-
minSamples?: number;
|
|
337
|
-
/** Bootstrap resamples for CI. Default 500. */
|
|
338
|
-
bootstrapResamples?: number;
|
|
339
|
-
/** Random seed for the bootstrap (mulberry32). Default unset (Math.random). */
|
|
340
|
-
seed?: number;
|
|
341
|
-
/**
|
|
342
|
-
* Reduction when multiple outcomes attach to one runId. Default `'latest'`
|
|
343
|
-
* (most recently captured).
|
|
344
|
-
*/
|
|
345
|
-
reduction?: 'latest' | 'mean' | 'max';
|
|
346
|
-
}
|
|
347
|
-
interface RubricOutcomePair {
|
|
348
|
-
rubric: string;
|
|
349
|
-
outcome: string;
|
|
350
|
-
n: number;
|
|
351
|
-
pearson: number;
|
|
352
|
-
spearman: number;
|
|
353
|
-
ci95: {
|
|
354
|
-
low: number;
|
|
355
|
-
high: number;
|
|
356
|
-
};
|
|
357
|
-
/**
|
|
358
|
-
* Verdict bucket. `load_bearing` ≥ 0.7, `informative` ≥ 0.4,
|
|
359
|
-
* `decorative` < 0.4 in absolute correlation. A negative correlation
|
|
360
|
-
* with a desired outcome is also `decorative` — actively misleading
|
|
361
|
-
* is worse than uninformative.
|
|
362
|
-
*/
|
|
363
|
-
verdict: 'load_bearing' | 'informative' | 'decorative';
|
|
364
|
-
}
|
|
365
|
-
interface RubricRanking {
|
|
366
|
-
rubric: string;
|
|
367
|
-
/** Outcome metric this rubric correlated best with. */
|
|
368
|
-
bestOutcome: string;
|
|
369
|
-
spearman: number;
|
|
370
|
-
pearson: number;
|
|
371
|
-
n: number;
|
|
372
|
-
verdict: RubricOutcomePair['verdict'];
|
|
373
|
-
}
|
|
374
|
-
interface RubricPredictiveValidityReport {
|
|
375
|
-
pairs: RubricOutcomePair[];
|
|
376
|
-
/** Per-rubric best pair, sorted descending by |spearman|. */
|
|
377
|
-
ranked: RubricRanking[];
|
|
378
|
-
joinedSamples: number;
|
|
379
|
-
skippedRuns: number;
|
|
380
|
-
/** Rubrics that were declared but never produced a usable score. */
|
|
381
|
-
rubricsWithoutData: string[];
|
|
382
|
-
}
|
|
383
|
-
declare function rubricPredictiveValidity(input: RubricPredictiveValidityInput): Promise<RubricPredictiveValidityReport>;
|
|
384
|
-
|
|
385
|
-
/**
|
|
386
|
-
* Bootstrap-CI promotion gate.
|
|
387
|
-
*
|
|
388
|
-
* In any iterative-improvement loop (GEPA, prompt evolution, dataset
|
|
389
|
-
* curation), the question is "did this generation actually improve, or are
|
|
390
|
-
* we celebrating noise?". With small N and noisy outcomes, point-estimate
|
|
391
|
-
* deltas lie. Bootstrap confidence intervals tell the operator whether the
|
|
392
|
-
* delta is real before code or prompts get promoted.
|
|
393
|
-
*
|
|
394
|
-
* This module is pure functions — no I/O, no model calls. Easy to unit-test
|
|
395
|
-
* and to compose into any verdict gate.
|
|
396
|
-
*
|
|
397
|
-
* Default gate:
|
|
398
|
-
* - Bootstrap mean baseline vs candidate (1k resamples).
|
|
399
|
-
* - Compute the delta distribution; pass if the lower CI bound > 0.
|
|
400
|
-
* - Tunable confidence (default 95%) and resample count.
|
|
401
|
-
*
|
|
402
|
-
* Verdict semantics intentionally match the existing `experiments.jsonl`
|
|
403
|
-
* vocabulary:
|
|
404
|
-
* - ADVANCE: candidate's CI lower bound > baseline mean (real win)
|
|
405
|
-
* - KEEP: overlap, but candidate point estimate >= baseline (neutral)
|
|
406
|
-
* - REVERT: candidate's CI upper bound < baseline mean (real regression)
|
|
407
|
-
* - INCONCLUSIVE: not enough samples or CI straddles zero with no signal
|
|
408
|
-
*/
|
|
409
|
-
type Verdict = 'ADVANCE' | 'KEEP' | 'REVERT' | 'INCONCLUSIVE';
|
|
410
|
-
interface BootstrapResult {
|
|
411
|
-
baselineMean: number;
|
|
412
|
-
candidateMean: number;
|
|
413
|
-
/** candidateMean - baselineMean, point estimate. */
|
|
414
|
-
delta: number;
|
|
415
|
-
/** Lower bound of the (1 - alpha) CI on the delta. */
|
|
416
|
-
ciLower: number;
|
|
417
|
-
/** Upper bound of the (1 - alpha) CI on the delta. */
|
|
418
|
-
ciUpper: number;
|
|
419
|
-
/** Number of bootstrap resamples used. */
|
|
420
|
-
iterations: number;
|
|
421
|
-
alpha: number;
|
|
422
|
-
verdict: Verdict;
|
|
423
|
-
}
|
|
424
|
-
interface BootstrapOptions {
|
|
425
|
-
/** Confidence level alpha (default 0.05 → 95% CI). */
|
|
426
|
-
alpha?: number;
|
|
427
|
-
/** Number of resamples (default 1000). */
|
|
428
|
-
iterations?: number;
|
|
429
|
-
/**
|
|
430
|
-
* Minimum total samples (baseline + candidate) below which we always
|
|
431
|
-
* return INCONCLUSIVE — bootstrap with too few samples is meaningless.
|
|
432
|
-
* Default 6 (combined).
|
|
433
|
-
*/
|
|
434
|
-
minTotalSamples?: number;
|
|
435
|
-
/** RNG seed for reproducibility. Default: Math.random. */
|
|
436
|
-
seed?: number;
|
|
437
|
-
}
|
|
438
|
-
/**
|
|
439
|
-
* Compute the bootstrap CI on (candidateMean - baselineMean) and a verdict.
|
|
440
|
-
*
|
|
441
|
-
* Uses simple percentile bootstrap on the difference of resampled means.
|
|
442
|
-
* That's the standard non-parametric primitive — no distributional
|
|
443
|
-
* assumptions, robust to skew, easy to reason about.
|
|
444
|
-
*/
|
|
445
|
-
declare function bootstrapCi(baseline: number[], candidate: number[], options?: BootstrapOptions): BootstrapResult;
|
|
446
|
-
/**
|
|
447
|
-
* Judge-replay promotion gate.
|
|
448
|
-
*
|
|
449
|
-
* The cheap inner-loop judge that drives an evolution run is by definition
|
|
450
|
-
* fast and noisy. When you're about to promote a winning variant to the
|
|
451
|
-
* canonical default, you want a STRONGER judge (a more expensive model, a
|
|
452
|
-
* human grader, a separately-trained reward model) to confirm the win
|
|
453
|
-
* generalises beyond the inner loop.
|
|
454
|
-
*
|
|
455
|
-
* This helper takes raw winner + baseline outputs, scores both through the
|
|
456
|
-
* stronger judge, and applies `bootstrapCi`. ADVANCE means the stronger
|
|
457
|
-
* judge agrees the winner is real with the configured confidence. Doesn't
|
|
458
|
-
* matter what shape your "output" is — pass a string, an object, anything
|
|
459
|
-
* the judge can read.
|
|
460
|
-
*/
|
|
461
|
-
interface JudgeReplayGateArgs<TOutput> {
|
|
462
|
-
baselineOutputs: TOutput[];
|
|
463
|
-
candidateOutputs: TOutput[];
|
|
464
|
-
/** Stronger judge — async to allow LLM calls. Return a 0..N scalar score. */
|
|
465
|
-
judge: (output: TOutput) => Promise<number> | number;
|
|
466
|
-
alpha?: number;
|
|
467
|
-
iterations?: number;
|
|
468
|
-
/** RNG seed for reproducibility. */
|
|
469
|
-
seed?: number;
|
|
470
|
-
/** Maximum concurrent judge calls. Default 4. */
|
|
471
|
-
judgeConcurrency?: number;
|
|
472
|
-
}
|
|
473
|
-
/**
|
|
474
|
-
* Confirm a candidate's win with a stronger judge: score baseline and candidate outputs independently, then bootstrap a CI to verify the lift generalises beyond the inner loop.
|
|
475
|
-
*/
|
|
476
|
-
declare function judgeReplayGate<TOutput>(args: JudgeReplayGateArgs<TOutput>): Promise<BootstrapResult & {
|
|
477
|
-
baselineSamples: number;
|
|
478
|
-
candidateSamples: number;
|
|
479
|
-
}>;
|
|
480
|
-
|
|
481
|
-
/**
|
|
482
|
-
* Dataset — versioned, sliceable, content-hashed scenario collection.
|
|
483
|
-
*
|
|
484
|
-
* Scenarios stop being ephemeral arrays and become first-class
|
|
485
|
-
* artifacts. Every Dataset carries:
|
|
486
|
-
* - content hash (sha256 over canonicalized scenario array)
|
|
487
|
-
* - provenance (contributor, createdAt, sourceUrl)
|
|
488
|
-
* - split labels (train | dev | test | holdout)
|
|
489
|
-
* - difficulty tiers (easy | medium | hard | extreme)
|
|
490
|
-
* - tags (free-form, per-scenario)
|
|
491
|
-
*
|
|
492
|
-
* `Dataset.slice({ difficulty, split, holdout, seed })` returns a
|
|
493
|
-
* deterministic, reproducible subset. Holdout slices are locked: you
|
|
494
|
-
* can read them but `mutate` throws, which prevents "oh I'll just
|
|
495
|
-
* tweak that one scenario" contamination drift.
|
|
496
|
-
*/
|
|
497
|
-
type DatasetSplit = 'train' | 'dev' | 'test' | 'holdout';
|
|
498
|
-
type DatasetDifficulty = 'easy' | 'medium' | 'hard' | 'extreme';
|
|
499
|
-
interface DatasetScenario {
|
|
500
|
-
id: string;
|
|
501
|
-
/** Arbitrary payload; the framework doesn't interpret it. */
|
|
502
|
-
payload: unknown;
|
|
503
|
-
split?: DatasetSplit;
|
|
504
|
-
difficulty?: DatasetDifficulty;
|
|
505
|
-
/** Canary token that MUST NOT round-trip through a correct agent output. */
|
|
506
|
-
canary?: string;
|
|
507
|
-
/**
|
|
508
|
-
* Behavioral-canary forbidden pattern. A string OR a serialized regex
|
|
509
|
-
* (`/.../flags`) that the agent under test MUST NOT emit. Used by
|
|
510
|
-
* {@link import('./canary').checkBehavioralCanary | checkBehavioralCanary},
|
|
511
|
-
* which inverts the contamination-style semantic: presence in the
|
|
512
|
-
* agent output is a LEAK / failure, not a positive signal.
|
|
513
|
-
*
|
|
514
|
-
* Falls back to {@link canary} when omitted.
|
|
515
|
-
*/
|
|
516
|
-
forbiddenPattern?: string;
|
|
517
|
-
tags?: Record<string, string>;
|
|
518
|
-
}
|
|
519
|
-
interface DatasetProvenance {
|
|
520
|
-
contributor?: string;
|
|
521
|
-
createdAt: string;
|
|
522
|
-
sourceUrl?: string;
|
|
523
|
-
license?: string;
|
|
524
|
-
description?: string;
|
|
525
|
-
/** Monotonic human-readable version (e.g. "2026.04.20"). */
|
|
526
|
-
version: string;
|
|
527
|
-
}
|
|
528
|
-
interface DatasetManifest {
|
|
529
|
-
name: string;
|
|
530
|
-
provenance: DatasetProvenance;
|
|
531
|
-
/** sha256 hex over canonicalized scenarios. */
|
|
532
|
-
contentHash: string;
|
|
533
|
-
scenarioCount: number;
|
|
534
|
-
splitCounts: Record<DatasetSplit, number>;
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
/**
|
|
538
|
-
* HeldOutGate — first-class held-out paired-delta promotion gate.
|
|
539
|
-
*
|
|
540
|
-
* Encodes the "honesty override" pattern that lived inline in
|
|
541
|
-
* `~/webb/redteam/scripts/agent-eval-autoresearch.ts:138–171`.
|
|
542
|
-
* The optimizer's best-guess is one thing; what we should actually
|
|
543
|
-
* ship is another. The gate is the line between them.
|
|
544
|
-
*
|
|
545
|
-
* A candidate is promoted iff ALL three pass:
|
|
546
|
-
*
|
|
547
|
-
* 1. **Productive runs**: the candidate has at least
|
|
548
|
-
* `minProductiveRuns` paired observations on items where BOTH
|
|
549
|
-
* candidate and baseline produced a real (non-silent) score.
|
|
550
|
-
* 2. **Paired delta**: the lower bound of the bootstrap CI on the
|
|
551
|
-
* median per-item delta (candidate − baseline) on the HOLDOUT
|
|
552
|
-
* split is strictly greater than `pairedDeltaThreshold`.
|
|
553
|
-
* 3. **Overfit gap**: the candidate's gap between search-split
|
|
554
|
-
* score and holdout-split score is no worse (more positive)
|
|
555
|
-
* than the baseline's gap by more than `overfitGapThreshold`.
|
|
556
|
-
* "Better on search, worse on holdout" is the canonical
|
|
557
|
-
* overfit pattern; this catches it.
|
|
558
|
-
*
|
|
559
|
-
* The decision carries a machine-readable `rejectionCode` plus an
|
|
560
|
-
* `evidence` block with every number the gate looked at, so the
|
|
561
|
-
* downstream researcher / paper / dashboard can re-derive the
|
|
562
|
-
* verdict without re-running.
|
|
563
|
-
*
|
|
564
|
-
* See also:
|
|
565
|
-
* - `src/statistics.ts` for `pairedBootstrap` + `wilcoxonSignedRank`
|
|
566
|
-
* - `src/run-record.ts` for the input row schema
|
|
567
|
-
* - `src/reference-replay.ts` for the older, reference-replay-
|
|
568
|
-
* specific promotion path (still useful for replay-style evals).
|
|
569
|
-
*/
|
|
570
|
-
|
|
571
|
-
type HeldOutGateRejectionCode = 'few_runs' | 'negative_delta' | 'overfit_gap' | 'cost_ceiling';
|
|
572
|
-
interface GateEvidence {
|
|
573
|
-
/** Number of paired (candidate, baseline) holdout observations used. */
|
|
574
|
-
productiveRuns: number;
|
|
575
|
-
/** Median of (candidate − baseline) paired holdout deltas. */
|
|
576
|
-
medianPairedDelta: number;
|
|
577
|
-
/** Bootstrap CI on the median paired holdout delta. */
|
|
578
|
-
pairedCI: {
|
|
579
|
-
low: number;
|
|
580
|
-
high: number;
|
|
581
|
-
};
|
|
582
|
-
/** Wilcoxon signed-rank p-value on the paired holdout deltas. */
|
|
583
|
-
pairedPValue: number;
|
|
584
|
-
/** Mean candidate score on the search split (NaN if none). */
|
|
585
|
-
searchScore: number;
|
|
586
|
-
/** Mean candidate score on the holdout split (NaN if none). */
|
|
587
|
-
holdoutScore: number;
|
|
588
|
-
/** Candidate (search − holdout) gap. */
|
|
589
|
-
overfitGap: number;
|
|
590
|
-
/** Baseline (search − holdout) gap. */
|
|
591
|
-
baselineOverfitGap: number;
|
|
592
|
-
/** Median per-task USD cost across the candidate's runs. Recorded
|
|
593
|
-
* even when no `costPerTaskCeiling` is configured so downstream
|
|
594
|
-
* dashboards (intelligence.tangle.tools) can render \$/task per
|
|
595
|
-
* generation regardless of gating policy. */
|
|
596
|
-
medianCandidateCost: number;
|
|
597
|
-
/** Median per-task USD cost across the baseline runs, for
|
|
598
|
-
* symmetric reporting. */
|
|
599
|
-
medianBaselineCost: number;
|
|
600
|
-
}
|
|
601
|
-
interface GateDecision {
|
|
602
|
-
/** Final promote/no-promote verdict. */
|
|
603
|
-
promote: boolean;
|
|
604
|
-
/** The candidate that was evaluated. */
|
|
605
|
-
candidateId: string;
|
|
606
|
-
/** The baseline it was compared against. */
|
|
607
|
-
baselineId: string;
|
|
608
|
-
/** Every number the gate looked at, for audit + paper export. */
|
|
609
|
-
evidence: GateEvidence;
|
|
610
|
-
/** Human-readable reason. */
|
|
611
|
-
reason: string;
|
|
612
|
-
/** Machine-readable rejection code, or null on promote. */
|
|
613
|
-
rejectionCode: HeldOutGateRejectionCode | null;
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
/**
|
|
617
|
-
* Release confidence gate.
|
|
618
|
-
*
|
|
619
|
-
* This is the production-facing composition layer over the lower-level
|
|
620
|
-
* primitives:
|
|
621
|
-
* - Dataset manifests prove corpus/version coverage.
|
|
622
|
-
* - RunRecord rows prove reproducible search/holdout outcomes.
|
|
623
|
-
* - Multi-shot trace evidence carries turn counts and ASI diagnostics.
|
|
624
|
-
* - HeldOutGate decisions remain the paired promotion authority.
|
|
625
|
-
*
|
|
626
|
-
* The gate is intentionally pure and conservative. Missing declared evidence
|
|
627
|
-
* fails closed instead of being treated as a neutral zero.
|
|
628
|
-
*/
|
|
629
|
-
|
|
630
|
-
/** Severity of an actionable finding attached to a run/trace. */
|
|
631
|
-
type AsiSeverity = 'info' | 'warning' | 'error' | 'critical';
|
|
632
|
-
/** Actionable side-info — a diagnosed finding the loop can act on. */
|
|
633
|
-
interface ActionableSideInfo {
|
|
634
|
-
/** Stable expectation/check id when available. */
|
|
635
|
-
expectationId?: string;
|
|
636
|
-
/** Human-readable diagnosis of what happened. */
|
|
637
|
-
message: string;
|
|
638
|
-
severity?: AsiSeverity;
|
|
639
|
-
/** Concrete trace excerpt, file path, tool call, screenshot id, etc. */
|
|
640
|
-
evidence?: string;
|
|
641
|
-
/** Prompt/tool/context surface likely responsible. */
|
|
642
|
-
responsibleSurface?: string;
|
|
643
|
-
/** Suggested fix in natural language. */
|
|
644
|
-
suggestion?: string;
|
|
645
|
-
/** Whether this expectation was satisfied. Defaults to false for ASI rows. */
|
|
646
|
-
matched?: boolean;
|
|
647
|
-
metadata?: Record<string, unknown>;
|
|
648
|
-
}
|
|
649
|
-
type ReleaseConfidenceStatus = 'pass' | 'warn' | 'fail';
|
|
650
|
-
type ReleaseConfidenceAxisName = 'corpus' | 'quality' | 'generalization' | 'diagnostics' | 'efficiency';
|
|
651
|
-
interface ReleaseTraceEvidence {
|
|
652
|
-
scenarioId: string;
|
|
653
|
-
candidateId?: string;
|
|
654
|
-
split?: RunSplitTag;
|
|
655
|
-
score?: number;
|
|
656
|
-
ok?: boolean;
|
|
657
|
-
turnCount?: number;
|
|
658
|
-
costUsd?: number;
|
|
659
|
-
durationMs?: number;
|
|
660
|
-
failureMode?: string;
|
|
661
|
-
asi?: ActionableSideInfo[];
|
|
662
|
-
metadata?: Record<string, unknown>;
|
|
663
|
-
}
|
|
664
|
-
interface ReleaseConfidenceThresholds {
|
|
665
|
-
/** Require a Dataset manifest or explicit scenarios. Default true. */
|
|
666
|
-
requireCorpus?: boolean;
|
|
667
|
-
minScenarioCount?: number;
|
|
668
|
-
minSearchRuns?: number;
|
|
669
|
-
minHoldoutRuns?: number;
|
|
670
|
-
/** Require at least one holdout scenario/run. Default true. */
|
|
671
|
-
requireHoldout?: boolean;
|
|
672
|
-
minPassRate?: number;
|
|
673
|
-
minMeanScore?: number;
|
|
674
|
-
/** Search mean may exceed holdout mean by at most this much. */
|
|
675
|
-
maxOverfitGap?: number;
|
|
676
|
-
maxMeanCostUsd?: number;
|
|
677
|
-
maxP95WallMs?: number;
|
|
678
|
-
/** Low-score/failed rows must carry ASI. Default true. */
|
|
679
|
-
requireAsiForFailures?: boolean;
|
|
680
|
-
/** Score below this is considered a failure for ASI coverage. Default 0.5. */
|
|
681
|
-
failureScoreThreshold?: number;
|
|
682
|
-
}
|
|
683
|
-
interface ReleaseConfidenceInput {
|
|
684
|
-
target: string;
|
|
685
|
-
candidateId?: string;
|
|
686
|
-
baselineId?: string;
|
|
687
|
-
dataset?: DatasetManifest;
|
|
688
|
-
scenarios?: readonly DatasetScenario[];
|
|
689
|
-
runs?: readonly RunRecord[];
|
|
690
|
-
traces?: readonly ReleaseTraceEvidence[];
|
|
691
|
-
gateDecision?: GateDecision | null;
|
|
692
|
-
thresholds?: ReleaseConfidenceThresholds;
|
|
693
|
-
}
|
|
694
|
-
interface ReleaseConfidenceAxis {
|
|
695
|
-
name: ReleaseConfidenceAxisName;
|
|
696
|
-
status: ReleaseConfidenceStatus;
|
|
697
|
-
score: number;
|
|
698
|
-
detail: string;
|
|
699
|
-
}
|
|
700
|
-
interface ReleaseConfidenceIssue {
|
|
701
|
-
axis: ReleaseConfidenceAxisName;
|
|
702
|
-
severity: 'critical' | 'warning';
|
|
703
|
-
code: string;
|
|
704
|
-
detail: string;
|
|
705
|
-
}
|
|
706
|
-
interface ReleaseConfidenceMetrics {
|
|
707
|
-
scenarioCount: number;
|
|
708
|
-
searchRuns: number;
|
|
709
|
-
holdoutRuns: number;
|
|
710
|
-
passRate: number;
|
|
711
|
-
meanScore: number;
|
|
712
|
-
searchMeanScore: number;
|
|
713
|
-
holdoutMeanScore: number;
|
|
714
|
-
overfitGap: number;
|
|
715
|
-
meanCostUsd: number;
|
|
716
|
-
p95WallMs: number;
|
|
717
|
-
failedRows: number;
|
|
718
|
-
failuresWithAsi: number;
|
|
719
|
-
singleShotTraces: number;
|
|
720
|
-
multiShotTraces: number;
|
|
721
|
-
splitCounts: Record<DatasetSplit, number>;
|
|
722
|
-
domainCounts: Record<string, number>;
|
|
723
|
-
failureModeCounts: Record<string, number>;
|
|
724
|
-
responsibleSurfaceCounts: Record<string, number>;
|
|
725
|
-
}
|
|
726
|
-
interface ReleaseConfidenceScorecard {
|
|
727
|
-
target: string;
|
|
728
|
-
candidateId: string | null;
|
|
729
|
-
baselineId: string | null;
|
|
730
|
-
status: ReleaseConfidenceStatus;
|
|
731
|
-
promote: boolean;
|
|
732
|
-
axes: ReleaseConfidenceAxis[];
|
|
733
|
-
issues: ReleaseConfidenceIssue[];
|
|
734
|
-
metrics: ReleaseConfidenceMetrics;
|
|
735
|
-
dataset: DatasetManifest | null;
|
|
736
|
-
gateDecision: GateDecision | null;
|
|
737
|
-
summary: string;
|
|
738
|
-
}
|
|
739
|
-
declare function evaluateReleaseConfidence(input: ReleaseConfidenceInput): ReleaseConfidenceScorecard;
|
|
740
|
-
declare function assertReleaseConfidence(input: ReleaseConfidenceInput): ReleaseConfidenceScorecard;
|
|
741
|
-
|
|
742
|
-
interface RenderReleaseReportOptions {
|
|
743
|
-
title?: string;
|
|
744
|
-
runs?: readonly RunRecord[];
|
|
745
|
-
comparator?: string;
|
|
746
|
-
traceAnalystFindings?: readonly string[];
|
|
747
|
-
nextActions?: readonly string[];
|
|
748
|
-
}
|
|
749
|
-
declare function renderReleaseReport(scorecard: ReleaseConfidenceScorecard, options?: RenderReleaseReportOptions): string;
|
|
750
|
-
|
|
751
|
-
/**
|
|
752
|
-
* Always-valid sequential evaluation.
|
|
753
|
-
*
|
|
754
|
-
* `researchReport` assumes a single pre-specified analysis. Real
|
|
755
|
-
* consumers run campaigns weekly / nightly / per-PR; each new run silently
|
|
756
|
-
* inflates the false-discovery rate, because the BH-FDR guarantee is for
|
|
757
|
-
* the *first* look, not the 47th. Without time-uniform inference,
|
|
758
|
-
* launch-decision teams either (a) don't peek, which forfeits the cost
|
|
759
|
-
* advantage of stop-when-decisive, or (b) peek and pretend they didn't,
|
|
760
|
-
* which forfeits scientific validity.
|
|
761
|
-
*
|
|
762
|
-
* This module ships **e-value-based confidence sequences** for paired
|
|
763
|
-
* bounded outcomes. The methodology is the predictable plug-in betting
|
|
764
|
-
* martingale of Waudby-Smith & Ramdas (2024) — provably valid at *any*
|
|
765
|
-
* stopping time. Concretely:
|
|
766
|
-
*
|
|
767
|
-
* For paired deltas D_1, D_2, … ∈ [-c, c] with the null H_0: E[D] ≤ 0,
|
|
768
|
-
* a betting fraction λ_i is chosen using only D_{1..i-1} (predictable
|
|
769
|
-
* plug-in), and the running e-value is
|
|
770
|
-
*
|
|
771
|
-
* E_t = ∏_{i=1}^{t} (1 + λ_i · D_i)
|
|
772
|
-
*
|
|
773
|
-
* E_t is a non-negative martingale under H_0 with E[E_t] ≤ 1, so by
|
|
774
|
-
* Ville's inequality, P(∃ t : E_t ≥ 1/α) ≤ α — we can reject the null
|
|
775
|
-
* at any time without inflating the type-I error.
|
|
776
|
-
*
|
|
777
|
-
* Combined with `runEvalCampaign`, every consumer running rolling
|
|
778
|
-
* campaigns gains the ability to ship the moment evidence is decisive,
|
|
779
|
-
* stop-early on dead-on-arrival variants, and accumulate evidence across
|
|
780
|
-
* partial runs without spending the FDR budget. No new sweep is wasted.
|
|
781
|
-
*
|
|
782
|
-
* References:
|
|
783
|
-
* - Howard, S. R., Ramdas, A., McAuliffe, J., Sekhon, J. (2021).
|
|
784
|
-
* Time-uniform, nonparametric, nonasymptotic confidence sequences.
|
|
785
|
-
* Annals of Statistics, 49(2), 1055–1080.
|
|
786
|
-
* - Waudby-Smith, I., Ramdas, A. (2024). Estimating means of bounded
|
|
787
|
-
* random variables by betting. JRSS B, 86(1), 1–27.
|
|
788
|
-
*/
|
|
789
|
-
type SequentialDecision = 'promote_now' | 'continue' | 'reject_now' | 'equivalent';
|
|
790
|
-
interface PairedEvalueOptions {
|
|
791
|
-
/**
|
|
792
|
-
* Bound on |delta|. Default 1 (matching most score scales). Must satisfy
|
|
793
|
-
* c > 0; deltas outside [-c, c] are clipped with a warning attached to
|
|
794
|
-
* the return value.
|
|
795
|
-
*/
|
|
796
|
-
bound?: number;
|
|
797
|
-
/** Target Type-I error. Default 0.05. */
|
|
798
|
-
alpha?: number;
|
|
799
|
-
/**
|
|
800
|
-
* Region of Practical Equivalence on the *mean* paired delta. When
|
|
801
|
-
* supplied, the verdict can return `'equivalent'` once the running
|
|
802
|
-
* confidence sequence on the mean is fully contained in [low, high].
|
|
803
|
-
*/
|
|
804
|
-
rope?: {
|
|
805
|
-
low: number;
|
|
806
|
-
high: number;
|
|
807
|
-
};
|
|
808
|
-
/** Initial bet shrinkage (0 < scale ≤ 1). Default 0.5 — empirically robust. */
|
|
809
|
-
initialBetShrinkage?: number;
|
|
810
|
-
}
|
|
811
|
-
interface PairedEvalueStep {
|
|
812
|
-
/** 1-indexed observation count. */
|
|
813
|
-
t: number;
|
|
814
|
-
delta: number;
|
|
815
|
-
/** Running e-value E_t = ∏ (1 + λ_i · D_i). */
|
|
816
|
-
evalue: number;
|
|
817
|
-
/** Time-uniform p-value at stopping time t. */
|
|
818
|
-
pValue: number;
|
|
819
|
-
/** Lower bound of the empirical Bernstein confidence sequence at level 1-α. */
|
|
820
|
-
csLow: number;
|
|
821
|
-
csHigh: number;
|
|
822
|
-
/** Verdict at this stopping time. */
|
|
823
|
-
decision: SequentialDecision;
|
|
824
|
-
}
|
|
825
|
-
interface PairedEvalueSequence {
|
|
826
|
-
steps: PairedEvalueStep[];
|
|
827
|
-
/** The decision at the final step. */
|
|
828
|
-
finalDecision: SequentialDecision;
|
|
829
|
-
/** Index (1-based) at which a non-`continue` decision first fired, or null. */
|
|
830
|
-
decisionFiredAt: number | null;
|
|
831
|
-
/** True if any deltas were clipped to [-bound, bound]. */
|
|
832
|
-
clipped: boolean;
|
|
833
|
-
}
|
|
834
|
-
/**
|
|
835
|
-
* Run the paired e-value sequence over an in-order delta stream.
|
|
836
|
-
*
|
|
837
|
-
* Use for *streaming* / interim analyses: pass the deltas you have so
|
|
838
|
-
* far, get the verdict at every prefix length. The decision is
|
|
839
|
-
* monotone-stable in the sense that once `'reject_now'` or `'promote_now'`
|
|
840
|
-
* fires, the verdict at later steps remains decisive (the e-value is a
|
|
841
|
-
* non-negative martingale; once it crosses the threshold, it's crossed).
|
|
842
|
-
*/
|
|
843
|
-
declare function pairedEvalueSequence(deltas: number[], opts?: PairedEvalueOptions): PairedEvalueSequence;
|
|
844
|
-
interface InterimReleaseConfidenceInput {
|
|
845
|
-
/**
|
|
846
|
-
* One delta series per candidate (paired deltas vs comparator). Order
|
|
847
|
-
* within a series is the order the campaigns were run.
|
|
848
|
-
*/
|
|
849
|
-
deltaSeries: Array<{
|
|
850
|
-
candidateId: string;
|
|
851
|
-
deltas: number[];
|
|
852
|
-
}>;
|
|
853
|
-
alpha?: number;
|
|
854
|
-
bound?: number;
|
|
855
|
-
rope?: {
|
|
856
|
-
low: number;
|
|
857
|
-
high: number;
|
|
858
|
-
};
|
|
859
|
-
}
|
|
860
|
-
interface InterimReleaseConfidence {
|
|
861
|
-
candidates: Array<{
|
|
862
|
-
candidateId: string;
|
|
863
|
-
decision: SequentialDecision;
|
|
864
|
-
decisionFiredAt: number | null;
|
|
865
|
-
finalEvalue: number;
|
|
866
|
-
finalPValue: number;
|
|
867
|
-
pairs: number;
|
|
868
|
-
csLow: number;
|
|
869
|
-
csHigh: number;
|
|
870
|
-
}>;
|
|
871
|
-
/**
|
|
872
|
-
* Campaign-level recommendation: pick the strongest 'promote_now', else
|
|
873
|
-
* 'continue' if any candidate is still live, else 'reject_now' if every
|
|
874
|
-
* candidate is dead, else 'equivalent'.
|
|
875
|
-
*/
|
|
876
|
-
recommendation: {
|
|
877
|
-
decision: SequentialDecision;
|
|
878
|
-
candidateId: string | null;
|
|
879
|
-
};
|
|
880
|
-
}
|
|
881
|
-
/**
|
|
882
|
-
* Run interim sequential analyses across many candidates at once,
|
|
883
|
-
* preserving the time-uniform α guarantee for each candidate's series and
|
|
884
|
-
* synthesising a campaign-level recommendation. Designed to be called on
|
|
885
|
-
* every campaign tick — the recommendation is anytime-valid.
|
|
886
|
-
*/
|
|
887
|
-
declare function evaluateInterimReleaseConfidence(input: InterimReleaseConfidenceInput): InterimReleaseConfidence;
|
|
888
|
-
|
|
889
|
-
/**
|
|
890
|
-
* Wilcoxon signed-rank test — paired non-parametric alternative.
|
|
891
|
-
* Use when the differences aren't normally distributed.
|
|
892
|
-
*/
|
|
893
|
-
declare function wilcoxonSignedRank(before: number[], after: number[]): {
|
|
894
|
-
w: number;
|
|
895
|
-
p: number;
|
|
896
|
-
};
|
|
897
|
-
/**
|
|
898
|
-
* Benjamini–Hochberg false discovery rate. Returns adjusted q-values and
|
|
899
|
-
* significance at the target FDR; handles ties and preserves q monotonicity.
|
|
900
|
-
*/
|
|
901
|
-
declare function benjaminiHochberg(pValues: number[], fdr?: number): {
|
|
902
|
-
qValues: number[];
|
|
903
|
-
significant: boolean[];
|
|
904
|
-
};
|
|
905
|
-
interface PairedBootstrapResult {
|
|
906
|
-
/** Number of paired observations. */
|
|
907
|
-
n: number;
|
|
908
|
-
/** Median of paired deltas (after − before). */
|
|
909
|
-
median: number;
|
|
910
|
-
/** Mean of paired deltas. */
|
|
911
|
-
mean: number;
|
|
912
|
-
/** Lower bound of the bootstrap CI on the chosen statistic. */
|
|
913
|
-
low: number;
|
|
914
|
-
/** Upper bound of the bootstrap CI on the chosen statistic. */
|
|
915
|
-
high: number;
|
|
916
|
-
/** Confidence level used (e.g. 0.95). */
|
|
917
|
-
confidence: number;
|
|
918
|
-
/** Number of bootstrap resamples used. */
|
|
919
|
-
resamples: number;
|
|
920
|
-
}
|
|
921
|
-
interface PairedBootstrapOptions {
|
|
922
|
-
/** Confidence level. Default 0.95. */
|
|
923
|
-
confidence?: number;
|
|
924
|
-
/** Bootstrap resample count. Default 2000. */
|
|
925
|
-
resamples?: number;
|
|
926
|
-
/** Statistic to bootstrap. Default 'median'. */
|
|
927
|
-
statistic?: 'median' | 'mean';
|
|
928
|
-
/** Deterministic seed. If omitted, uses Math.random(). */
|
|
929
|
-
seed?: number;
|
|
930
|
-
}
|
|
931
|
-
/**
|
|
932
|
-
* Paired bootstrap on (after − before) deltas. Returns a CI on the chosen
|
|
933
|
-
* statistic (median by default); pairs are resampled with replacement. The
|
|
934
|
-
* lower bound is what the promotion gate checks — `low > threshold` means the
|
|
935
|
-
* gain is real at the confidence level. Throws on unequal sample sizes.
|
|
936
|
-
*/
|
|
937
|
-
declare function pairedBootstrap(before: number[], after: number[], opts?: PairedBootstrapOptions): PairedBootstrapResult;
|
|
938
|
-
|
|
939
|
-
/**
|
|
940
|
-
* FailureClusterView — groups failed runs by (failureClass, triggerTool,
|
|
941
|
-
* argHash-prefix) so weekly reviews can prioritize the top-N clusters.
|
|
942
|
-
*
|
|
943
|
-
* Each cluster includes: N runs, scenarios affected, representative
|
|
944
|
-
* error message, a proposed mitigation hint (rule → action table).
|
|
945
|
-
*/
|
|
946
|
-
|
|
947
|
-
interface FailureCluster {
|
|
948
|
-
failureClass: FailureClass;
|
|
949
|
-
/** Tool name when the trigger was a tool span, else undefined. */
|
|
950
|
-
toolName?: string;
|
|
951
|
-
/** First 16 chars of argHash — clusters similar args. */
|
|
952
|
-
argPrefix?: string;
|
|
953
|
-
/**
|
|
954
|
-
* Source dimension when the trigger was a judge span (e.g. `'format'`,
|
|
955
|
-
* `'safety'`, `'correctness'`). Lets cross-template aggregators
|
|
956
|
-
* group failures by the dimension that fired without overloading
|
|
957
|
-
* `argPrefix`. Optional — clusters without this field deserialize cleanly.
|
|
958
|
-
*/
|
|
959
|
-
dimension?: string;
|
|
960
|
-
runCount: number;
|
|
961
|
-
scenarioIds: string[];
|
|
962
|
-
exampleError?: string;
|
|
963
|
-
exampleRunId: string;
|
|
964
|
-
}
|
|
965
|
-
interface FailureClusterReport {
|
|
966
|
-
clusters: FailureCluster[];
|
|
967
|
-
totalFailures: number;
|
|
968
|
-
totalRuns: number;
|
|
969
|
-
}
|
|
970
|
-
|
|
971
|
-
/**
|
|
972
|
-
* Reporting helpers — production summaries and paper-quality figures — sit alongside `reporter.ts` rather
|
|
973
|
-
* than replacing it.
|
|
974
|
-
*
|
|
975
|
-
* Three artefacts:
|
|
976
|
-
*
|
|
977
|
-
* - `summaryTable` Markdown table of per-candidate means,
|
|
978
|
-
* 95% bootstrap CIs, BH-adjusted Wilcoxon
|
|
979
|
-
* p-values, and Cohen's d versus a
|
|
980
|
-
* comparator candidate.
|
|
981
|
-
* - `paretoChart` Abstract spec for a cost vs quality
|
|
982
|
-
* scatter, with gate decisions overlaid.
|
|
983
|
-
* Returns numbers + labels — caller
|
|
984
|
-
* chooses the plotting library.
|
|
985
|
-
* - `gainHistogram`
|
|
986
|
-
* Per-item paired holdout deltas as a
|
|
987
|
-
* histogram spec (bins + counts + median +
|
|
988
|
-
* CI). Same "data, not images" contract.
|
|
989
|
-
*
|
|
990
|
-
* The figure types are PlotSpecs — JSON-friendly, library-agnostic.
|
|
991
|
-
* They aren't React components and they aren't PNGs; they are
|
|
992
|
-
* what you'd hand to vega-lite, plotly, matplotlib, or your own
|
|
993
|
-
* Canvas renderer to draw the actual figure.
|
|
994
|
-
*/
|
|
995
|
-
|
|
996
|
-
interface SummaryTableOptions {
|
|
997
|
-
/** Comparator candidate id. Wilcoxon + Cohen's d are computed
|
|
998
|
-
* versus this candidate. Required for paired stats columns. */
|
|
999
|
-
comparator?: string;
|
|
1000
|
-
/** Which split to read scores from. Default 'holdout'. */
|
|
1001
|
-
split?: 'search' | 'holdout';
|
|
1002
|
-
/** Confidence level for the bootstrap CI on the mean. Default 0.95. */
|
|
1003
|
-
confidence?: number;
|
|
1004
|
-
/** FDR for BH adjustment of the comparison p-values. Default 0.05. */
|
|
1005
|
-
fdr?: number;
|
|
1006
|
-
}
|
|
1007
|
-
interface SummaryTableRow {
|
|
1008
|
-
candidateId: string;
|
|
1009
|
-
n: number;
|
|
1010
|
-
mean: number;
|
|
1011
|
-
ciLow: number;
|
|
1012
|
-
ciHigh: number;
|
|
1013
|
-
/** BH-adjusted q-value vs comparator. NaN if no comparator. */
|
|
1014
|
-
qValue: number;
|
|
1015
|
-
/** Cohen's d vs comparator. NaN if no comparator. */
|
|
1016
|
-
cohensD: number;
|
|
1017
|
-
}
|
|
1018
|
-
interface SummaryTable {
|
|
1019
|
-
rows: SummaryTableRow[];
|
|
1020
|
-
comparator: string | null;
|
|
1021
|
-
split: 'search' | 'holdout';
|
|
1022
|
-
/** Pre-rendered markdown — drop into a paper or PR. */
|
|
1023
|
-
markdown: string;
|
|
1024
|
-
}
|
|
1025
|
-
/**
|
|
1026
|
-
* Table 1 helper. Buckets runs by `candidateId`, computes mean +
|
|
1027
|
-
* bootstrap CI on the chosen split, and (when a comparator is given)
|
|
1028
|
-
* BH-adjusted Wilcoxon p + Cohen's d versus that comparator.
|
|
1029
|
-
*/
|
|
1030
|
-
declare function summaryTable(runs: RunRecord[], opts?: SummaryTableOptions): SummaryTable;
|
|
1031
|
-
interface ParetoPoint {
|
|
1032
|
-
candidateId: string;
|
|
1033
|
-
/** Mean USD cost per run on the chosen split. */
|
|
1034
|
-
cost: number;
|
|
1035
|
-
/** Mean score on the chosen split. */
|
|
1036
|
-
quality: number;
|
|
1037
|
-
/** Number of runs that informed this point. */
|
|
1038
|
-
n: number;
|
|
1039
|
-
/** Whether this candidate is on the Pareto frontier — high
|
|
1040
|
-
* quality, low cost, no dominator. */
|
|
1041
|
-
onFrontier: boolean;
|
|
1042
|
-
/** Optional gate verdict for this candidate, if a `GateDecision`
|
|
1043
|
-
* for it was passed in. */
|
|
1044
|
-
gate?: 'promote' | 'reject_few_runs' | 'reject_negative_delta' | 'reject_overfit_gap' | null;
|
|
1045
|
-
}
|
|
1046
|
-
interface ParetoFigureSpec {
|
|
1047
|
-
kind: 'pareto-cost-quality';
|
|
1048
|
-
split: 'search' | 'holdout';
|
|
1049
|
-
points: ParetoPoint[];
|
|
1050
|
-
axes: {
|
|
1051
|
-
x: 'costUsd';
|
|
1052
|
-
y: 'score';
|
|
1053
|
-
};
|
|
1054
|
-
}
|
|
1055
|
-
/**
|
|
1056
|
-
* Cost vs quality scatter spec. `gateDecisions` is keyed by
|
|
1057
|
-
* candidate id; if present, every point picks up the gate verdict
|
|
1058
|
-
* for overlay.
|
|
1059
|
-
*/
|
|
1060
|
-
declare function paretoChart(runs: RunRecord[], opts?: {
|
|
1061
|
-
split?: 'search' | 'holdout';
|
|
1062
|
-
gateDecisions?: Record<string, GateDecision>;
|
|
1063
|
-
}): ParetoFigureSpec;
|
|
1064
|
-
interface GainDistributionBin {
|
|
1065
|
-
/** Inclusive lower edge. */
|
|
1066
|
-
lo: number;
|
|
1067
|
-
/** Exclusive upper edge (or inclusive if it's the last bin). */
|
|
1068
|
-
hi: number;
|
|
1069
|
-
/** Number of pairs whose delta lands in this bin. */
|
|
1070
|
-
count: number;
|
|
1071
|
-
}
|
|
1072
|
-
interface GainDistributionFigureSpec {
|
|
1073
|
-
kind: 'gain-distribution';
|
|
1074
|
-
candidateId: string;
|
|
1075
|
-
comparator: string;
|
|
1076
|
-
split: 'search' | 'holdout';
|
|
1077
|
-
/** Number of pairs used. */
|
|
1078
|
-
n: number;
|
|
1079
|
-
bins: GainDistributionBin[];
|
|
1080
|
-
median: number;
|
|
1081
|
-
ci: {
|
|
1082
|
-
low: number;
|
|
1083
|
-
high: number;
|
|
1084
|
-
};
|
|
1085
|
-
}
|
|
1086
|
-
interface GainDistributionOptions {
|
|
1087
|
-
/** Number of histogram bins. Default 11 (so the centre is exact at 0). */
|
|
1088
|
-
bins?: number;
|
|
1089
|
-
/** Which split to use. Default 'holdout'. */
|
|
1090
|
-
split?: 'search' | 'holdout';
|
|
1091
|
-
/** Confidence level for the CI. Default 0.95. */
|
|
1092
|
-
confidence?: number;
|
|
1093
|
-
/** Bootstrap resamples. Default 2000. */
|
|
1094
|
-
resamples?: number;
|
|
1095
|
-
/** Deterministic seed. */
|
|
1096
|
-
seed?: number;
|
|
1097
|
-
}
|
|
1098
|
-
/**
|
|
1099
|
-
* Held-out improvement distribution: per-pair delta (candidate −
|
|
1100
|
-
* comparator), histogrammed. Includes the bootstrap CI on the median
|
|
1101
|
-
* delta — same primitive the promotion gate uses.
|
|
1102
|
-
*/
|
|
1103
|
-
declare function gainHistogram(runs: RunRecord[], candidateId: string, comparator: string, opts?: GainDistributionOptions): GainDistributionFigureSpec;
|
|
1104
|
-
type ResearchReportDecision = 'promote' | 'hold' | 'reject' | 'equivalent' | 'needs_more_data';
|
|
1105
|
-
/**
|
|
1106
|
-
* Hard floor below which a paired comparison is treated as uninformative
|
|
1107
|
-
* regardless of `minPairs`. Mirrors the lower limit on Wilcoxon signed-rank
|
|
1108
|
-
* exact tables; below this the test has no power to separate effect sizes.
|
|
1109
|
-
*/
|
|
1110
|
-
declare const RESEARCH_REPORT_HARD_PAIR_FLOOR = 6;
|
|
1111
|
-
interface ResearchReportOptions {
|
|
1112
|
-
/** Human-readable report title. */
|
|
1113
|
-
title?: string;
|
|
1114
|
-
/** Comparator candidate id. Required for statistical decision guidance. */
|
|
1115
|
-
comparator?: string;
|
|
1116
|
-
/** Which split to use for the primary decision. Default 'holdout'. */
|
|
1117
|
-
split?: 'search' | 'holdout';
|
|
1118
|
-
/** Confidence level used by lower-level report helpers. Default 0.95. */
|
|
1119
|
-
confidence?: number;
|
|
1120
|
-
/** FDR threshold for q-values. Default 0.05. */
|
|
1121
|
-
fdr?: number;
|
|
1122
|
-
/**
|
|
1123
|
-
* Soft floor on paired observations before issuing a directional
|
|
1124
|
-
* promote / reject. Below this we report `needs_more_data` and surface the
|
|
1125
|
-
* minimum detectable effect at the current N. Default 20 — chosen so the
|
|
1126
|
-
* Wilcoxon signed-rank approximation is reasonable and so the paired
|
|
1127
|
-
* bootstrap CI has non-degenerate coverage. Hard floor is enforced at
|
|
1128
|
-
* `RESEARCH_REPORT_HARD_PAIR_FLOOR` (6) regardless of this value.
|
|
1129
|
-
*/
|
|
1130
|
-
minPairs?: number;
|
|
1131
|
-
/**
|
|
1132
|
-
* Region of Practical Equivalence on the paired delta. When a candidate's
|
|
1133
|
-
* paired-delta CI is fully contained in `[low, high]`, the decision is
|
|
1134
|
-
* `equivalent` rather than `hold`. Sourced from the domain owner — there is
|
|
1135
|
-
* no statistically-defensible default.
|
|
1136
|
-
*/
|
|
1137
|
-
rope?: {
|
|
1138
|
-
low: number;
|
|
1139
|
-
high: number;
|
|
1140
|
-
};
|
|
1141
|
-
/**
|
|
1142
|
-
* Power for the minimum detectable effect (MDE) reported on each candidate.
|
|
1143
|
-
* Default 0.8.
|
|
1144
|
-
*/
|
|
1145
|
-
mdePower?: number;
|
|
1146
|
-
/**
|
|
1147
|
-
* Two-sided alpha for the MDE. Default matches `fdr` so the reported MDE
|
|
1148
|
-
* lines up with the test the report actually runs.
|
|
1149
|
-
*/
|
|
1150
|
-
mdeAlpha?: number;
|
|
1151
|
-
/** Optional held-out gate decisions keyed by candidate id. */
|
|
1152
|
-
gateDecisions?: Record<string, GateDecision>;
|
|
1153
|
-
/** Optional failure clusters from failureClusterView. */
|
|
1154
|
-
failureClusters?: FailureClusterReport;
|
|
1155
|
-
/** Build gain histograms for these candidates. Defaults to all non-comparator candidates. */
|
|
1156
|
-
candidateIds?: string[];
|
|
1157
|
-
/** Deterministic bootstrap seed passed to gainHistogram and the posterior helper. */
|
|
1158
|
-
seed?: number;
|
|
1159
|
-
/** Report timestamp. Defaults to current time. */
|
|
1160
|
-
generatedAt?: string;
|
|
1161
|
-
/**
|
|
1162
|
-
* Hash of a preregistered protocol (e.g. `signManifest({...}).contentHash`).
|
|
1163
|
-
* Embedded verbatim in the report so the analysis can be cited as the
|
|
1164
|
-
* preregistered one rather than a post-hoc fishing expedition.
|
|
1165
|
-
*/
|
|
1166
|
-
preregistrationHash?: string;
|
|
1167
|
-
}
|
|
1168
|
-
interface ResearchReportRecommendation {
|
|
1169
|
-
decision: ResearchReportDecision;
|
|
1170
|
-
candidateId: string | null;
|
|
1171
|
-
rationale: string[];
|
|
1172
|
-
risks: string[];
|
|
1173
|
-
nextActions: string[];
|
|
1174
|
-
}
|
|
1175
|
-
interface ResearchReportCandidate {
|
|
1176
|
-
candidateId: string;
|
|
1177
|
-
n: number;
|
|
1178
|
-
mean: number;
|
|
1179
|
-
ciLow: number;
|
|
1180
|
-
ciHigh: number;
|
|
1181
|
-
qValue: number;
|
|
1182
|
-
cohensD: number;
|
|
1183
|
-
meanDeltaVsComparator: number | null;
|
|
1184
|
-
pairedN: number;
|
|
1185
|
-
medianGain: number | null;
|
|
1186
|
-
meanGain: number | null;
|
|
1187
|
-
gainCi: {
|
|
1188
|
-
low: number;
|
|
1189
|
-
high: number;
|
|
1190
|
-
} | null;
|
|
1191
|
-
/**
|
|
1192
|
-
* Bayesian-bootstrap-style posterior summaries on the paired delta. Computed
|
|
1193
|
-
* from the same resamples that produce the gain CI; interpretable as
|
|
1194
|
-
* "fraction of resamples in which the candidate beats the comparator on
|
|
1195
|
-
* matched pairs."
|
|
1196
|
-
*/
|
|
1197
|
-
prGreaterThanZero: number | null;
|
|
1198
|
-
prInRope: number | null;
|
|
1199
|
-
/**
|
|
1200
|
-
* Minimum detectable effect (in score units) at the candidate's paired N,
|
|
1201
|
-
* the configured power, and the configured alpha. Standardised by the
|
|
1202
|
-
* observed paired-delta SD and inverted via `requiredSampleSize`. Reported
|
|
1203
|
-
* for every candidate so a `needs_more_data` verdict is actionable.
|
|
1204
|
-
*/
|
|
1205
|
-
mde: number | null;
|
|
1206
|
-
onParetoFrontier: boolean;
|
|
1207
|
-
gate?: ParetoPoint['gate'];
|
|
1208
|
-
decision: ResearchReportDecision;
|
|
1209
|
-
decisionReason: string;
|
|
1210
|
-
}
|
|
1211
|
-
interface ResearchReportMethodology {
|
|
1212
|
-
/**
|
|
1213
|
-
* Plain-language assumptions the report depends on. Read these first when
|
|
1214
|
-
* deciding whether the verdict is load-bearing for a launch decision.
|
|
1215
|
-
*/
|
|
1216
|
-
assumptions: string[];
|
|
1217
|
-
/** Tests and estimators the verdict was computed from. */
|
|
1218
|
-
methods: string[];
|
|
1219
|
-
/** Alternatives the author considered and why this report didn't take them. */
|
|
1220
|
-
alternatives: string[];
|
|
1221
|
-
/** Failure modes — when this report should NOT drive a decision. */
|
|
1222
|
-
whenNotToApply: string[];
|
|
1223
|
-
/** Citations for the methodological choices above. */
|
|
1224
|
-
citations: string[];
|
|
1225
|
-
}
|
|
1226
|
-
interface ResearchReport {
|
|
1227
|
-
kind: 'agent-eval-research-report';
|
|
1228
|
-
title: string;
|
|
1229
|
-
generatedAt: string;
|
|
1230
|
-
split: 'search' | 'holdout';
|
|
1231
|
-
comparator: string | null;
|
|
1232
|
-
/**
|
|
1233
|
-
* SHA-256 over the canonicalised set of `(runId, candidateId, split)` triples
|
|
1234
|
-
* the report was computed from, plus the comparator and split. Stable across
|
|
1235
|
-
* key insertion order; recomputable by the reader to verify provenance.
|
|
1236
|
-
*/
|
|
1237
|
-
runFingerprint: string;
|
|
1238
|
-
preregistrationHash: string | null;
|
|
1239
|
-
rope: {
|
|
1240
|
-
low: number;
|
|
1241
|
-
high: number;
|
|
1242
|
-
} | null;
|
|
1243
|
-
executiveSummary: string[];
|
|
1244
|
-
recommendation: ResearchReportRecommendation;
|
|
1245
|
-
candidates: ResearchReportCandidate[];
|
|
1246
|
-
summary: SummaryTable;
|
|
1247
|
-
charts: {
|
|
1248
|
-
pareto: ParetoFigureSpec;
|
|
1249
|
-
gains: GainDistributionFigureSpec[];
|
|
1250
|
-
};
|
|
1251
|
-
methodology: ResearchReportMethodology;
|
|
1252
|
-
failureClusters?: FailureClusterReport;
|
|
1253
|
-
markdown: string;
|
|
1254
|
-
html: string;
|
|
1255
|
-
}
|
|
1256
|
-
/**
|
|
1257
|
-
* Executive research report for CPO / AI-lead / launch-review consumption.
|
|
1258
|
-
*
|
|
1259
|
-
* Composes:
|
|
1260
|
-
* - `summaryTable` marginal stats with BH-FDR-adjusted q-values
|
|
1261
|
-
* - `paretoChart` cost-vs-quality frontier with gate overlay
|
|
1262
|
-
* - `gainHistogram` per-candidate paired-delta distribution
|
|
1263
|
-
* - paired posterior (this file): bootstrap CI on median, Pr(Δ>0),
|
|
1264
|
-
* Pr(Δ∈ROPE), MDE at the configured power
|
|
1265
|
-
*
|
|
1266
|
-
* Decisions are made on paired evidence — never on marginal means alone —
|
|
1267
|
-
* and respect any held-out gate decision the caller passes through. The
|
|
1268
|
-
* report embeds a SHA-256 fingerprint of the input run set and, optionally,
|
|
1269
|
-
* the hash of a preregistered protocol so a downstream reader can verify
|
|
1270
|
-
* provenance and that the analysis was the preregistered one.
|
|
1271
|
-
*
|
|
1272
|
-
* Async because the fingerprint uses Web Crypto via `hashJson`; deterministic
|
|
1273
|
-
* for any fixed `runs`, `seed`, and ROPE.
|
|
1274
|
-
*/
|
|
1275
|
-
declare function researchReport(runs: RunRecord[], opts?: ResearchReportOptions): Promise<ResearchReport>;
|
|
1276
|
-
|
|
1277
|
-
export { type BootstrapOptions, type BootstrapResult, type GainDistributionBin, type GainDistributionFigureSpec, type GainDistributionOptions, type InterimReleaseConfidence, type InterimReleaseConfidenceInput, type JudgeReplayGateArgs, type PairedBootstrapOptions, type PairedBootstrapResult, type PairedEvalueOptions, type PairedEvalueSequence, type PairedEvalueStep, type ParetoFigureSpec, type ParetoPoint, RESEARCH_REPORT_HARD_PAIR_FLOOR, type ReleaseConfidenceAxis, type ReleaseConfidenceAxisName, type ReleaseConfidenceInput, type ReleaseConfidenceIssue, type ReleaseConfidenceMetrics, type ReleaseConfidenceScorecard, type ReleaseConfidenceStatus, type ReleaseConfidenceThresholds, type ReleaseTraceEvidence, type RenderReleaseReportOptions, type ResearchReport, type ResearchReportCandidate, type ResearchReportDecision, type ResearchReportMethodology, type ResearchReportOptions, type ResearchReportRecommendation, type RubricOutcomePair, type RubricPredictiveValidityInput, type RubricPredictiveValidityReport, type RubricRanking, type SequentialDecision, type SummaryTable, type SummaryTableOptions, type SummaryTableRow, type Verdict, assertReleaseConfidence, benjaminiHochberg, bootstrapCi, evaluateInterimReleaseConfidence, evaluateReleaseConfidence, gainHistogram, judgeReplayGate, pairedBootstrap, pairedEvalueSequence, paretoChart, renderReleaseReport, researchReport, rubricPredictiveValidity, summaryTable, wilcoxonSignedRank };
|