@tangle-network/agent-eval 0.105.0 → 0.106.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { J as JudgeScore } from '../types-Mh6I44ub.js';
1
+ import { J as JudgeScore } from '../types-Ctf7XIAL.js';
2
2
  import { AgentProfile } from '@tangle-network/agent-interface';
3
3
  import { M as MatrixResult } from '../types-BUxNaJ8c.js';
4
4
  import '../run-record-I-Z3JNvO.js';
package/dist/openapi.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "openapi": "3.1.0",
3
3
  "info": {
4
4
  "title": "@tangle-network/agent-eval — wire protocol",
5
- "version": "0.105.0",
5
+ "version": "0.106.1",
6
6
  "description": "HTTP and stdio RPC interface to agent-eval. The TypeScript runtime is the source of truth; this spec is the contract that cross-language clients (Python, Rust, Go) generate from.\n\nWire-protocol version: 1.0.0. Bumps on breaking changes to request/response schemas.",
7
7
  "contact": {
8
8
  "name": "Tangle Network",
@@ -1,7 +1,7 @@
1
1
  import { A as AgentEvalError } from './errors-oeQrLqXC.js';
2
2
  import { R as RunRecord } from './run-record-I-Z3JNvO.js';
3
3
  import { C as ChatClient } from './types-D1ytG0Yg.js';
4
- import { c as JudgeDimension, S as Scenario, a as JudgeConfig } from './types-Mh6I44ub.js';
4
+ import { c as JudgeDimension, S as Scenario, a as JudgeConfig } from './types-Ctf7XIAL.js';
5
5
  import { TCloud } from '@tangle-network/tcloud';
6
6
  import { R as RawProviderSink } from './raw-provider-sink-C46HDghv.js';
7
7
  import { D as DefaultVerdict } from './verdict-C9MlYujm.js';
@@ -1,9 +1,9 @@
1
- import { S as Scenario, g as Gate, G as GateResult, h as GateContext, C as CampaignResult, i as Mutator, f as SurfaceProposer, M as MutableSurface, j as GateDecision } from './types-Mh6I44ub.js';
1
+ import { S as Scenario, g as Gate, G as GateResult, n as GateContext, C as CampaignResult, p as Mutator, f as SurfaceProposer, M as MutableSurface, h as GateDecision } from './types-Ctf7XIAL.js';
2
2
  import { R as RedTeamCase } from './red-team-KmmiqBlY.js';
3
3
  import { R as RunRecord } from './run-record-I-Z3JNvO.js';
4
4
  import { D as Direction } from './pareto-E-pembql.js';
5
5
  import { a as PairedBootstrapResult } from './statistics-D88peojY.js';
6
- import { R as RunCampaignOptions, C as CampaignStorage } from './gepa-Dn0gtj9n.js';
6
+ import { b as RunCampaignOptions, C as CampaignStorage } from './gepa-DQ3ruj18.js';
7
7
  import { HostedClient, TraceSpanEvent } from './hosted/index.js';
8
8
 
9
9
  /**
@@ -49,6 +49,10 @@ interface DefaultProductionGateOptions {
49
49
  * significance claim is allowed; below it the gate HOLDS with `few_runs`
50
50
  * rather than reading a degenerate CI. Default 3. */
51
51
  minProductiveRuns?: number;
52
+ /** Ship statistic for the held-out significance test. Default `'mean'`
53
+ * (tie-robust — see `heldoutSignificance`). Pass `'median'` for
54
+ * outlier-robustness at the cost of tie-blindness. */
55
+ heldoutStatistic?: 'mean' | 'median';
52
56
  /** Critical judge dimensions that must NOT significantly regress even when
53
57
  * the net composite rises (anti-Goodhart). The gate HOLDS if any listed
54
58
  * dimension's paired-delta CI lower bound < −`regressionTolerance`. E.g.
@@ -79,20 +83,106 @@ declare function defaultProductionGate<TArtifact, TScenario extends Scenario>(op
79
83
 
80
84
  /**
81
85
  * @module
82
- * Thin Gate adapter exposes delta-threshold-on-holdout as a composable
83
- * `Gate`. Use when you want held-out as one of N composed gates instead of
84
- * the full `defaultProductionGate` stack.
86
+ * Composable held-out promotion gate backed by paired bootstrap confidence.
87
+ *
88
+ * Pair by full `scenario:rep` cellId, bootstrap the paired candidate-minus-
89
+ * baseline delta, and ship only when CI.low strictly clears the threshold with
90
+ * at least `minProductiveRuns` paired observations.
91
+ *
92
+ * Use when you want held-out significance as ONE of N composed gates instead
93
+ * of the full `defaultProductionGate` stack (which adds critical-dimension
94
+ * regression + reward-hacking guards on top).
85
95
  */
86
96
 
87
97
  interface HeldOutGateOptions<TScenario extends Scenario = Scenario> {
88
98
  scenarios: TScenario[];
99
+ /** Effect-size threshold the CI lower bound must clear, in the judge's native
100
+ * scale. Default 0.5. Equality holds; CI.low must be greater than this value. */
89
101
  deltaThreshold?: number;
102
+ /** Bootstrap CI confidence. Default 0.95. */
103
+ confidence?: number;
104
+ /** Minimum paired holdout observations to claim significance. Default 3. */
105
+ minProductiveRuns?: number;
106
+ /** Bootstrap resamples. Default 2000. */
107
+ resamples?: number;
108
+ /** Fixed bootstrap seed for deterministic verdicts. Default 1337. */
109
+ bootstrapSeed?: number;
90
110
  }
91
111
  /**
92
- * Composable held-out delta gate: ships only when the candidate's mean composite on `scenarios` beats the baseline by at least `deltaThreshold`.
112
+ * Composable held-out gate: ships only when the PAIRED bootstrap CI lower bound
113
+ * of the candidate-minus-baseline composite delta clears `deltaThreshold`.
93
114
  */
94
115
  declare function heldOutGate<TArtifact, TScenario extends Scenario>(options: HeldOutGateOptions<TScenario>): Gate<TArtifact, TScenario>;
95
116
 
117
+ /**
118
+ * Power preflight — "can this budget detect the effect you are hunting?"
119
+ *
120
+ * The failure it prevents (measured, twice): a live prompt-improvement campaign ran
121
+ * 333 sandbox cells over 5.6 hours and produced a +0.08 holdout lift the ship gate
122
+ * (paired bootstrap, CI.low > 0.05) could not distinguish from zero — because at
123
+ * that holdout size and worker variance the MINIMUM DETECTABLE lift was larger than
124
+ * any effect a prompt change plausibly produces. The budget was spent learning what
125
+ * a 30-second calculation on the baseline cells already knew. No eval framework we
126
+ * know of surfaces this; every underpowered improvement run everywhere ends in an
127
+ * uninformative "hold".
128
+ *
129
+ * Model: the ship rule is `CI.low(paired Δ) > deltaThreshold`. Approximating the
130
+ * bootstrap CI as normal, `CI.low ≈ effect − z·sd_Δ/√n`, so the smallest shippable
131
+ * true effect is `MDE = deltaThreshold + z·sd_Δ/√n`. The paired-delta SD is unknown
132
+ * before the candidate exists; we bound it by the zero-correlation case
133
+ * `sd_Δ ≤ √2·sd_baseline` — a CONSERVATIVE (upper) MDE, which is the correct
134
+ * direction for a warning. Pairing is per cell (`scenario:rep`), so reps multiply n.
135
+ *
136
+ * Standalone by design: feed it any baseline composites (a `gate:'none'` run, a
137
+ * live-proof table) BEFORE budgeting the real search; `selfImprove` also attaches
138
+ * it to every result and warns when the run was structurally unable to ship.
139
+ */
140
+ interface PowerPreflightOptions {
141
+ /** Per-cell baseline composites on the HOLDOUT scenarios (one per scenario:rep cell). */
142
+ baselineComposites: number[];
143
+ /** Paired observations the budgeted comparison will produce
144
+ * (holdout scenarios × reps). Defaults to `baselineComposites.length`. */
145
+ pairedN?: number;
146
+ /** The ship gate's effect-size threshold. Default 0.05 (defaultProductionGate). */
147
+ deltaThreshold?: number;
148
+ /** CI confidence the gate uses. Default 0.95. */
149
+ confidence?: number;
150
+ /** True when the holdout is scored by the SAME judge/scorer family as the gate
151
+ * (selfImprove's default composition — one judge scores everything). Under a
152
+ * shared channel, raising paired n reduces only the IDIOSYNCRATIC noise share;
153
+ * systematic judge bias is untouched, so the MDE here is a lower bound and the
154
+ * only full debiaser is an independent second scoring channel
155
+ * (recursive-self-improvement S1c, closed form in EXP-023 P0). Default false. */
156
+ sharedScorerChannel?: boolean;
157
+ }
158
+ interface PowerPreflight {
159
+ /** Paired observations the comparison will have. */
160
+ n: number;
161
+ /** Baseline per-cell composite standard deviation (the variance the effect must beat). */
162
+ sd: number;
163
+ /** Minimum detectable lift: the smallest TRUE effect the gate could ship at this budget. */
164
+ mde: number;
165
+ /** Baseline holdout composite mean. */
166
+ baselineMean: number;
167
+ /** Headroom to a perfect 1.0 composite (the largest achievable lift on a [0,1] judge). */
168
+ headroom: number;
169
+ /** True when even the largest achievable effect (headroom) is below the MDE —
170
+ * the run is structurally unable to ship regardless of proposal quality.
171
+ * Only asserted for [0,1]-scaled judges (see `scaleAssumed`). */
172
+ underpowered: boolean;
173
+ /** True when composites look [0,1]-scaled; headroom/underpowered are only
174
+ * meaningful under that convention (0-100 judges get mde/sd/n but no verdict). */
175
+ scaleAssumed: boolean;
176
+ deltaThreshold: number;
177
+ confidence: number;
178
+ /** Set when the holdout shares the gate's scoring channel: more cells cannot
179
+ * buy back systematic judge bias — treat the MDE as a lower bound. */
180
+ sharedChannelCaveat?: string;
181
+ /** One actionable sentence for humans and logs. */
182
+ recommendation: string;
183
+ }
184
+ declare function powerPreflight(opts: PowerPreflightOptions): PowerPreflight;
185
+
96
186
  /**
97
187
  * Promotion policy over the evidence VECTOR — the substrate's answer to "never
98
188
  * collapse the multi-objective promotion decision into one scalar." A
@@ -439,4 +529,4 @@ interface EmitLoopProvenanceArgs<TArtifact, TScenario extends Scenario> extends
439
529
  */
440
530
  declare function emitLoopProvenance<TArtifact, TScenario extends Scenario>(args: EmitLoopProvenanceArgs<TArtifact, TScenario>): Promise<EmitLoopProvenanceResult>;
441
531
 
442
- export { type AxisEvidence as A, type BuildEvidenceVectorOptions as B, type DefaultProductionGateOptions as D, type EvidenceVector as E, type HeldOutGateOptions as H, type LoopProvenanceRecord as L, type ObjectiveSource as O, type ParetoSignificanceGateOptions as P, type RunEvalOptions as R, type AxisVerdict as a, type EvolutionaryProposerOptions as b, type PromotionObjective as c, type PromotionPolicy as d, buildEvidenceVector as e, composeGate as f, defaultProductionGate as g, evolutionaryProposer as h, heldOutGate as i, paretoSignificanceGate as j, type BuildLoopProvenanceArgs as k, type EmitLoopProvenanceArgs as l, type EmitLoopProvenanceResult as m, type LoopProvenanceBackend as n, type LoopProvenanceCandidate as o, paretoPolicy as p, buildLoopProvenanceRecord as q, runEval as r, emitLoopProvenance as s, loopProvenanceSpans as t, provenanceRecordPath as u, provenanceSpansPath as v, surfaceContentHash as w };
532
+ export { type AxisEvidence as A, type BuildEvidenceVectorOptions as B, type DefaultProductionGateOptions as D, type EvidenceVector as E, type HeldOutGateOptions as H, type LoopProvenanceRecord as L, type ObjectiveSource as O, type PowerPreflight as P, type RunEvalOptions as R, type AxisVerdict as a, type EvolutionaryProposerOptions as b, type ParetoSignificanceGateOptions as c, type PromotionObjective as d, type PromotionPolicy as e, buildEvidenceVector as f, composeGate as g, defaultProductionGate as h, evolutionaryProposer as i, heldOutGate as j, paretoSignificanceGate as k, type BuildLoopProvenanceArgs as l, type EmitLoopProvenanceArgs as m, type EmitLoopProvenanceResult as n, type LoopProvenanceBackend as o, paretoPolicy as p, type LoopProvenanceCandidate as q, runEval as r, type PowerPreflightOptions as s, buildLoopProvenanceRecord as t, emitLoopProvenance as u, loopProvenanceSpans as v, powerPreflight as w, provenanceRecordPath as x, provenanceSpansPath as y, surfaceContentHash as z };
package/dist/rl.d.ts CHANGED
@@ -10,7 +10,7 @@ import { R as Researcher, F as FailureMode, S as SteeringChange, E as Experiment
10
10
  export { r as runEvalCampaign } from './researcher-Wc7dx6GM.js';
11
11
  import { a as VerificationReport } from './multi-layer-verifier-CI4jdX-q.js';
12
12
  import { I as InterimReleaseConfidence } from './sequential-5iSVfzl2.js';
13
- import { C as CampaignResult } from './types-Mh6I44ub.js';
13
+ import { C as CampaignResult } from './types-Ctf7XIAL.js';
14
14
  import '@tangle-network/agent-interface';
15
15
  import './errors-oeQrLqXC.js';
16
16
  import './schema-m0gsnbt3.js';
@@ -547,4 +547,4 @@ interface CampaignResult<TArtifact = unknown, TScenario extends Scenario = Scena
547
547
  scenarios: Array<Pick<TScenario, 'id' | 'kind'>>;
548
548
  }
549
549
 
550
- export { type JudgeAggregate as A, type ScenarioAggregate as B, type CampaignResult as C, type DispatchFn as D, isProposedCandidate as E, labelTrustRank as F, type GateResult as G, type JudgeScore as J, type LabeledScenarioStore as L, type MutableSurface as M, type OptimizationProposer as O, type ParetoParent as P, type RedactionStatus as R, type Scenario as S, type TraceSpan as T, type JudgeConfig as a, type DispatchContext as b, type JudgeDimension as c, type CampaignTraceWriter as d, type GenerationRecord as e, type SurfaceProposer as f, type Gate as g, type GateContext as h, type Mutator as i, type GateDecision as j, type CampaignAggregates as k, type CampaignArtifactWriter as l, type CampaignCellResult as m, type CampaignCostMeter as n, type CodeSurface as o, type GenerationCandidate as p, type OptimizerConfig as q, type SessionScript as r, type LabeledScenarioWrite as s, type LabeledScenarioSampleArgs as t, type LabeledScenarioRecord as u, type LabelTrust as v, type ProposedCandidate as w, type ProposeContext as x, type LabeledScenarioSource as y, type CampaignTokenUsage as z };
550
+ export { type JudgeAggregate as A, type ScenarioAggregate as B, type CampaignResult as C, type DispatchFn as D, isProposedCandidate as E, labelTrustRank as F, type GateResult as G, type JudgeScore as J, type LabeledScenarioStore as L, type MutableSurface as M, type OptimizationProposer as O, type ParetoParent as P, type RedactionStatus as R, type Scenario as S, type TraceSpan as T, type JudgeConfig as a, type DispatchContext as b, type JudgeDimension as c, type CampaignTraceWriter as d, type GenerationRecord as e, type SurfaceProposer as f, type Gate as g, type GateDecision as h, type CampaignAggregates as i, type CampaignArtifactWriter as j, type CampaignCellResult as k, type CampaignCostMeter as l, type CodeSurface as m, type GateContext as n, type GenerationCandidate as o, type Mutator as p, type OptimizerConfig as q, type SessionScript as r, type LabeledScenarioWrite as s, type LabeledScenarioSampleArgs as t, type LabeledScenarioRecord as u, type LabelTrust as v, type ProposedCandidate as w, type ProposeContext as x, type LabeledScenarioSource as y, type CampaignTokenUsage as z };
@@ -0,0 +1,199 @@
1
+ # Improvement glossary + proposer chooser + composition
2
+
3
+ > **In plain terms:** this is the dictionary for the *improvement* half of the stack — the words that show up when you optimize an agent (proposer, surface, candidate, generation, holdout, lift, gate…) rather than when you *run* one (driver, worker, iteration — those live in [`agent-runtime/docs/glossary.md`](../../agent-runtime/docs/glossary.md)).
4
+ > Read this once and you can read any improvement result, pick a proposer, and wire two of them together.
5
+
6
+ **Who this is for.**
7
+ A **novice** (never seen the repo) should be able to read a `CampaignResult`, a `ProposeContext`, and a proposer chooser table without opening the source.
8
+ An **expert** who knows DSPy/GEPA should be able to map their existing mental model onto our names in about a minute — every core term below carries a *"if you know DSPy/GEPA"* line.
9
+ If code and this file disagree, the code wins — fix this file the same turn (the anti-staleness law).
10
+
11
+ Neighbors, so this page does not duplicate them: [`concepts.md`](./concepts.md) (eval mental model), [`self-improvement-map.md`](./self-improvement-map.md) (one loop / four roles / proposer catalog), [`campaign-proposers.md`](./campaign-proposers.md) (proposer ELI5), [`eval-surface-map.md`](./eval-surface-map.md) (which `run*` primitive), [`design/loop-taxonomy.md`](./design/loop-taxonomy.md) (execution vs proposer layering).
12
+
13
+ ## The 60-second mental model
14
+
15
+ One loop improves one **surface**.
16
+ Every generation: a **proposer** reads what failed and emits **candidate** surfaces; a **campaign** measures each candidate by running the agent over **scenarios × reps** and **judging** every run; the loop ranks candidates and re-scores the best on a **holdout**; a **gate** decides whether that beat the **baseline** for real; if yes, it is **promoted**.
17
+
18
+ ```text
19
+ baseline surface
20
+ └─► PROPOSER: read findings ─► N candidate surfaces (one GENERATION)
21
+ └─► CAMPAIGN: run agent on scenarios × reps ─► JUDGE each ─► composite
22
+ └─► rank candidates ─► re-score best on HOLDOUT
23
+ └─► GATE: did it beat baseline (significance)? ship / hold
24
+ repeat for maxGenerations
25
+ ```
26
+
27
+ ## Glossary — one plain sentence each
28
+
29
+ Grounded to `agent-eval/src/campaign/types.ts` unless noted.
30
+
31
+ | Term | Plain sentence | If you know DSPy/GEPA |
32
+ |---|---|---|
33
+ | **surface** | The one thing being changed this run — a prompt string, a JSON config string, or a code/worktree ref (`MutableSurface = string \| CodeSurface`, `types.ts:158`). | The optimized artifact: a signature/predictor's instruction text, or the module config. |
34
+ | **proposer** | The strategy that, given the current surface + what failed, proposes the next batch of candidate surfaces to measure — it does **not** run the agent or score anything (`SurfaceProposer.propose`, `types.ts:286`). | The optimizer / teleprompter (MIPRO, BootstrapFewShot, GEPA's reflective proposer). |
35
+ | **candidate** | One proposed surface plus its human `label` and `rationale`, ready to be measured (`ProposedCandidate`, `types.ts:166`). | One trial instruction/program the optimizer wants to evaluate. |
36
+ | **generation** | One round of *propose → measure → rank → promote*; the loop runs up to `maxGenerations` of them (`GenerationRecord`, `types.ts:549`). | One GEPA iteration / optimization step. |
37
+ | **populationSize** | BREADTH — how many candidate surfaces the proposer returns *this* generation (`ProposeContext.populationSize`, `types.ts:237`). Paired with `maxGenerations` (DEPTH) as the search budget. | Beam width / number of minibatch candidates per step. |
38
+ | **population budget** | Informal name for the pair `{ populationSize, maxGenerations }` — the total candidates the search may evaluate (breadth × depth). | Optimizer trial budget. |
39
+ | **campaign** | One measurement: run a dispatch over scenarios × seeds × reps, judge each output, aggregate → `CampaignResult` (`runCampaign`; a "campaign" = a coordinated batch of measurements). | One evaluation pass of a candidate over a valset. |
40
+ | **cell** | The atomic measurement unit — exactly one `(scenario, rep)` execution producing one artifact + its judge scores + its cost (`CampaignCellResult`, `types.ts:509`; `DispatchContext.cellId/rep`). | One (example, seed) evaluation datapoint. |
41
+ | **scenario** | One input case with a stable `id` + `kind` (consumers attach their payload: persona, task, requirement) (`Scenario`, `types.ts:23`). | One dataset example / `dspy.Example`. |
42
+ | **rep** | The repetition index — the same scenario run more than once so noise/variance is measurable rather than mistaken for signal (`ctx.rep`). | Repeated sampling of the same example (temperature/seed variance). |
43
+ | **judge** | A scorer: given an artifact, return dimensions + a single `composite` + free-form `notes`; it throws on failure rather than silently scoring 0 (`JudgeConfig` → `JudgeScore`, `types.ts:94/116`). | The metric function, but pluggable (LLM-judge, deterministic checks, or an ensemble). |
44
+ | **composite** | The single 0..1 number that combines all judge dimensions — the number you rank and gate on (`JudgeScore.composite`, `types.ts:116`). | The scalar metric value. |
45
+ | **findings** | The failure analysis handed to the proposer so it edits from evidence, not guesses — worst cells + judge reasons, or a trace-analyst's clusters (`ProposeContext.findings`). | GEPA's reflective feedback / the "textual gradient". |
46
+ | **baseline** | The starting surface (the current prompt/config) every candidate is measured against (`baselineSurface`). | The unoptimized program you compare lift against. |
47
+ | **holdout** | A separate scenario split the winner is re-scored on and that the proposer is *never* allowed to see — a compile-time firewall (`ProposeContext.judgeScores: never`, `types.ts:265`) keeps held-out verdicts out of proposal so the optimizer can't game the acceptance axis. | The held-out valset/testset — but here it is *write-only* to the optimizer. |
48
+ | **lift** | Winner minus baseline `composite` on the holdout — the actual improvement, reported with a bootstrap confidence interval (`ImproveResult.lift`, `ProposerScore.lift`). | Δ metric between optimized and baseline program. |
49
+ | **MDE** | Minimum Detectable Effect — the smallest lift your budget could statistically distinguish from noise, computed up front from baseline cells; a structurally-hopeless budget warns before you spend (surfaced as `result.power` / "power preflight", `agent-optimization-map.md`). Not spelled "MDE" in code yet — this is the standard name for it. | Power analysis on the eval set; the reason a tiny valset can't certify a small gain. |
50
+ | **Pareto frontier** | The set of surfaces that are non-dominated across the per-scenario score vectors — a candidate worse on the mean but uniquely best on one hard scenario survives, so its lesson is not discarded (`ParetoParent`, `types.ts:198`; GEPA, arXiv:2507.19457). | Exactly GEPA's Pareto candidate pool — same paper, same idea. |
51
+ | **gate** | The promotion decision: does the winner beat baseline on the holdout with significance, returning one of five verdicts `ship / hold / need_more_work / model_ceiling / arch_ceiling` (`Gate`, `GateDecision`, `types.ts:315/340`). | The accept/reject rule on the held-out valset, plus a significance test and ceiling diagnosis. |
52
+ | **promotion** | What happens on a `ship` verdict — the winning surface is written back into the profile field it came from (`GenerationRecord.promoted`; `applyWinnerToProfile`, `improve.ts`). | Committing the optimized program as the new default. |
53
+
54
+ ## Which proposer — the chooser
55
+
56
+ Every optimizer is a factory `xProposer(opts): SurfaceProposer`, all exported from `@tangle-network/agent-eval/campaign`.
57
+ Start at the top row and only move down when the row's *"reach for it when"* matches your failure mode.
58
+
59
+ | Proposer factory | Reach for it when | Surface it edits | Wired to the paved path? |
60
+ |---|---|---|---|
61
+ | `gepaProposer` | You want the strong default: reflective full-surface prompt rewrites, grounded in findings, keeping a Pareto frontier of complementary winners. | prompt string | **Yes — `improve({ surface: 'prompt' })` default.** Proven live. |
62
+ | `skillOptProposer` | You are editing a structured `SKILL.md`/runbook and want small anchored add/delete/replace patches that preserve earlier rules. | skill/prompt string | **Yes — `improve({ surface: 'skills' })` default.** Not yet proven live. |
63
+ | `parameterSweepProposer` | The likely fix is a config knob, not words — `retrieval.k`, `temperature`, `max_tokens`. You give it candidate patches; it applies them to a JSON surface. | JSON config string | Yes, but you supply the candidate list. |
64
+ | `fapoProposer` | You want *evidence to decide when to escalate*: try prompt edits first, move to parameters, then to structural code — one scoped change per cycle, only escalating when the cheaper level is exhausted. **This is the composite/orchestration proposer** (there is no separate `compositeProposer`). | whatever its level proposers return | Exported; you wire the level proposers. |
65
+ | `aceProposer` | You are accumulating hard-won lessons into a playbook and must **never** summarize an old lesson away (append-only, provenance-tagged). | playbook string | Exported. |
66
+ | `memoryCurationProposer` | Same as ACE but you want a compact, deduped, re-ranked memory instead of append-only growth. | memory string | Exported. |
67
+ | `evolutionaryProposer` | You want blind population search (mutate → measure → select) with no reflection over findings — a cheap control or a baseline to beat. | any string | Exported. |
68
+ | `traceAnalystProposer` | Bench-only: race our trace-analysis evidence engine head-to-head inside `compareProposers`. | prompt string | Bench-only. |
69
+ | `haloProposer` | Bench-only: race the external `halo-engine` analysis against ours. | prompt string | Bench-only, external. |
70
+
71
+ Default path: `gepaProposer` for prompts; add `parameterSweepProposer` when a config knob is the suspect; wrap both in `fapoProposer` when you want the loop to decide *when* to escalate from words to knobs to code.
72
+
73
+ ## Composing proposers — the only three ways, with runnable code
74
+
75
+ There is deliberately **no** `chainProposer`/`compositeProposer` primitive.
76
+ Composition happens in exactly three shapes:
77
+
78
+ 1. **Escalate** — `fapoProposer` wraps a prompt + parameter + structural proposer into one, spending the cheapest level first.
79
+ 2. **Race** — `compareProposers` runs N proposers on one holdout and returns a per-proposer lift CI + pairwise "who won".
80
+ 3. **Plug in** — hand any composed proposer to the gated loop via `runImprovementLoop({ proposer })`, or the one-liner `improve({ surface, generator })` in `@tangle-network/agent-runtime`.
81
+
82
+ ### 1 + 3 — compose by escalation, then run the gated loop
83
+
84
+ ```ts
85
+ import {
86
+ fapoProposer,
87
+ gepaProposer,
88
+ parameterSweepProposer,
89
+ defaultProductionGate,
90
+ runImprovementLoop,
91
+ } from '@tangle-network/agent-eval/campaign'
92
+
93
+ const llm = { baseUrl: process.env.TANGLE_BASE_URL, apiKey: process.env.TANGLE_API_KEY }
94
+ const model = 'deepseek-v4-flash'
95
+
96
+ // Compose: prompt edits first (GEPA), escalate to a config knob only when
97
+ // prompt-level search plateaus. `fapoProposer` IS the composite proposer.
98
+ const proposer = fapoProposer({
99
+ scope: { allowedLevels: ['prompt', 'parameter'] }, // no structural/code tier here
100
+ promptProposer: gepaProposer({ llm, model, target: 'agent system prompt' }),
101
+ parameterProposer: parameterSweepProposer({
102
+ candidates: [
103
+ {
104
+ label: 'raise-retrieval-k',
105
+ rationale: 'retrieval-miss findings suggest the search budget is too low',
106
+ changes: [{ path: 'retrieval.k', value: 10 }],
107
+ },
108
+ ],
109
+ }),
110
+ })
111
+
112
+ // dispatchWithSurface scores ONE surface on ONE scenario — this is the topology-
113
+ // opaque seam (one LLM call, one worker, or a whole fleet — the loop can't tell).
114
+ const dispatchWithSurface = async (surface, scenario, ctx) =>
115
+ runYourAgent({ surface, scenario, signal: ctx.signal }) // returns the artifact to judge
116
+
117
+ const result = await runImprovementLoop({
118
+ scenarios: trainScenarios, // proposer trains on these
119
+ holdoutScenarios, // winner is re-scored here; proposer never sees them
120
+ baselineSurface: currentPromptString,
121
+ dispatchWithSurface,
122
+ judges, // JudgeConfig[]
123
+ proposer,
124
+ gate: defaultProductionGate({ holdoutScenarios, deltaThreshold: 0 }),
125
+ populationSize: 2, // BREADTH per generation
126
+ maxGenerations: 6, // DEPTH
127
+ autoOnPromote: 'none', // 'pr' to open a PR on ship
128
+ runDir: '/tmp/improve-run', // a REAL path makes the run durable
129
+ })
130
+
131
+ result.winnerSurface // the promoted surface
132
+ result.gateDecision // 'ship' | 'hold' | 'need_more_work' | 'model_ceiling' | 'arch_ceiling'
133
+ ```
134
+
135
+ Same thing, one line, when you have an `AgentProfile` (facade in `@tangle-network/agent-runtime`):
136
+
137
+ ```ts
138
+ import { improve } from '@tangle-network/agent-runtime'
139
+
140
+ // surface 'prompt'/'skills' pick their proposer automatically; pass `generator`
141
+ // to plug the composed FAPO proposer above into the gated loop instead.
142
+ const out = await improve(profile, findings, {
143
+ surface: 'prompt',
144
+ generator: proposer, // any SurfaceProposer, incl. the composed one
145
+ scenarios, judge, agent, runDir: '/tmp/improve-run',
146
+ })
147
+ if (out.shipped) deploy(out.profile) // out.lift is the held-out winner − baseline
148
+ ```
149
+
150
+ ### 2 — race proposers head-to-head for a lift CI
151
+
152
+ ```ts
153
+ import {
154
+ compareProposers,
155
+ gepaParetoEntry,
156
+ fapoEscalationEntry,
157
+ } from '@tangle-network/agent-eval/campaign'
158
+
159
+ const config = {
160
+ baselineSurface: currentPromptString,
161
+ trainScenarios,
162
+ holdoutScenarios,
163
+ dispatchWithSurface,
164
+ judges,
165
+ llm, model,
166
+ target: 'agent system prompt',
167
+ runDir: '/tmp/compare-run',
168
+ }
169
+
170
+ const comparison = await compareProposers({
171
+ proposers: [
172
+ gepaParetoEntry(config), // GEPA + Pareto frontier
173
+ fapoEscalationEntry({ // FAPO escalation policy
174
+ ...config,
175
+ parameterCandidates: [
176
+ { label: 'raise-retrieval-k', rationale: 'retrieval misses', changes: [{ path: 'retrieval.k', value: 10 }] },
177
+ ],
178
+ }),
179
+ ],
180
+ baselineSurface: currentPromptString,
181
+ holdoutScenarios,
182
+ dispatchWithSurface,
183
+ judges,
184
+ runDir: '/tmp/compare-run',
185
+ })
186
+
187
+ comparison.best // highest-lift proposer
188
+ comparison.scores // per-proposer { lift, liftCi:{low,high}, cost } — low>0 ⇒ real gain
189
+ comparison.pairwise // best vs each other, paired-bootstrap: 'a' | 'b' | 'tie'
190
+ ```
191
+
192
+ Every entrant is re-scored on the **same** holdout with the **same** judges, so the comparison never trusts how a proposer measured itself — the only variable is proposal quality.
193
+
194
+ ## Common traps (they cost the most)
195
+
196
+ - Do not put eval logic inside a proposer — scoring lives in `dispatch` + `judges`, proposing lives in the proposer.
197
+ - Do not let a proposer read held-out judge scores — `ProposeContext` makes that a compile error on purpose; a proposer that games the acceptance axis is an oracle, not an optimizer.
198
+ - Do not read `lift` without `result.power`/MDE — a "+4" on a valset too small to detect +4 is noise wearing a number.
199
+ - Do not look for a `compositeProposer` — `fapoProposer` is the composite; `compareProposers` is the race; `runImprovementLoop`/`improve` is the plug.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-eval",
3
- "version": "0.105.0",
3
+ "version": "0.106.1",
4
4
  "description": "Evaluate and improve AI agents from runs, traces, judges, and feedback. Compare candidates, cluster failures, measure lift, and gate releases.",
5
5
  "homepage": "https://github.com/tangle-network/agent-eval#readme",
6
6
  "repository": {
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/campaign/gates/compose.ts","../src/campaign/gates/statistical-heldout.ts","../src/campaign/gates/default-production-gate.ts","../src/campaign/gates/promotion-policy.ts","../src/campaign/presets/run-eval.ts","../src/campaign/proposers/evolutionary.ts"],"sourcesContent":["/**\n * Compose multiple `Gate` implementations — every gate must pass for the\n * composite to ship. Closes the alignment reviewer's \"default-only\n * heldOutGate + costGate would happily promote a reward-hacked prompt\"\n * concern by making safety gates first-class composable defaults.\n */\n\nimport type { Gate, GateContext, GateDecision, GateResult, Scenario } from '../types'\n\n/** Compose gates — all must `ship` for the composite to `ship`. First\n * non-ship verdict short-circuits the composite verdict, but ALL gates run\n * (so the result records every gate's reason — useful for diagnostics). */\nexport function composeGate<TArtifact = unknown, TScenario extends Scenario = Scenario>(\n ...gates: Array<Gate<TArtifact, TScenario>>\n): Gate<TArtifact, TScenario> {\n if (gates.length === 0) {\n throw new Error('composeGate requires at least one gate')\n }\n return {\n name: `composed(${gates.map((g) => g.name).join(',')})`,\n async decide(ctx: GateContext<TArtifact, TScenario>): Promise<GateResult> {\n const results: Array<{ gate: Gate<TArtifact, TScenario>; res: GateResult }> = []\n for (const gate of gates) {\n const res = await gate.decide(ctx)\n results.push({ gate, res })\n }\n\n // Substrate-wide verdict policy:\n // - all 'ship' → 'ship'\n // - any 'arch_ceiling' → 'arch_ceiling' (architectural ceiling beats other holds)\n // - any 'model_ceiling' → 'model_ceiling'\n // - any 'hold' → 'hold'\n // - else 'need_more_work'\n const decisions = results.map((r) => r.res.decision)\n const overall: GateDecision = decisions.every((d) => d === 'ship')\n ? 'ship'\n : decisions.includes('arch_ceiling')\n ? 'arch_ceiling'\n : decisions.includes('model_ceiling')\n ? 'model_ceiling'\n : decisions.includes('hold')\n ? 'hold'\n : 'need_more_work'\n\n const contributing = results.flatMap((r) =>\n r.res.contributingGates.length > 0\n ? r.res.contributingGates\n : [{ name: r.gate.name, passed: r.res.decision === 'ship', detail: r.res }],\n )\n\n const reasons = results.flatMap((r) =>\n r.res.reasons.map((reason) => `[${r.gate.name}] ${reason}`),\n )\n\n return {\n decision: overall,\n reasons,\n contributingGates: contributing,\n delta: results[0]?.res.delta,\n }\n },\n }\n}\n","/**\n * Statistical held-out promotion machinery — the trustworthy core the\n * point-estimate `heldout-delta` gate lacked.\n *\n * The shipped false positive it prevents: a winner re-scored against the\n * baseline on the holdout read run-to-run model NOISE (e.g. 91 vs 95) as a\n * \"+4 lift\" and shipped, because the gate compared point estimates with no\n * confidence interval. Here we pair candidate vs baseline holdout observations\n * and bootstrap a CI on the paired delta — a candidate ships only when the CI\n * lower bound clears the effect-size threshold (the gain is real at the\n * confidence level, not noise), and is blocked when a critical dimension\n * (e.g. `hallucination_free` for a legal agent) significantly regresses even if\n * the net composite rose (anti-Goodhart).\n *\n * Two traps this module is built around (both produce a NEW false positive if\n * gotten wrong):\n * 1. PAIRING GRANULARITY — pairs by FULL `cellId` (`scenario:rep`), never by\n * `scenarioId` (which averages reps away and destroys the within-pair\n * variance reduction that makes a paired bootstrap tighter than unpaired).\n * One paired observation per cell ⇒ reps multiply n.\n * 2. SCALE — a judge may emit composites/dimensions on [0,1] or 0-100. The\n * threshold + tolerance are interpreted in the judge's NATIVE scale; the\n * per-dimension tolerance auto-scales off the observed baseline magnitudes\n * so `-0.10` on [0,1] doesn't silently become a no-op on a 0-100 dimension.\n */\n\nimport { type PairedBootstrapResult, pairedBootstrap } from '../../statistics'\nimport type { JudgeScore } from '../types'\n\nexport interface PairedHoldout {\n /** Baseline scalar per paired cell (same order as `after`/`cellIds`). */\n before: number[]\n /** Candidate scalar per paired cell. */\n after: number[]\n /** The full cellIds (`scenario:rep`) that paired, in order. */\n cellIds: string[]\n}\n\n/**\n * Pair candidate vs baseline holdout observations by FULL cellId. `select`\n * pulls the scalar from a cell's judge reports (composite, or a named\n * dimension); a cell contributes the mean of `select` across its judges. Cells\n * whose scenario is not in `scenarioIds`, or where `select` is undefined for\n * every judge on either side, are skipped on BOTH sides so the arrays stay\n * paired. Throws when the two maps disagree on which holdout cells exist — a\n * load-bearing invariant: the baseline + winner holdout campaigns run the same\n * scenarios with the same seed base, so their cellIds MUST align; a mismatch\n * means a silent pairing bug, not a soft fallback.\n */\nexport function pairHoldout(\n candidate: Map<string, Record<string, JudgeScore>>,\n baseline: Map<string, Record<string, JudgeScore>>,\n scenarioIds: Set<string>,\n select: (s: JudgeScore) => number | undefined,\n): PairedHoldout {\n const cellValue = (\n byCell: Map<string, Record<string, JudgeScore>>,\n cellId: string,\n ): number | undefined => {\n const scores = byCell.get(cellId)\n if (!scores) return undefined\n const vals: number[] = []\n for (const s of Object.values(scores)) {\n const v = select(s)\n if (typeof v === 'number' && Number.isFinite(v)) vals.push(v)\n }\n if (vals.length === 0) return undefined\n return vals.reduce((a, b) => a + b, 0) / vals.length\n }\n\n const inScope = (cellId: string) => scenarioIds.has(cellId.split(':')[0] ?? '')\n const candCells = [...candidate.keys()].filter(inScope).sort()\n const baseCells = [...baseline.keys()].filter(inScope).sort()\n // Alignment invariant — the holdout campaigns share scenarios + seed, so the\n // cell sets must be identical. Differ ⇒ a real pairing bug; fail loud.\n if (candCells.length !== baseCells.length || candCells.some((c, i) => c !== baseCells[i])) {\n throw new Error(\n `pairHoldout: candidate/baseline holdout cells do not align — ` +\n `candidate=[${candCells.join(',')}] baseline=[${baseCells.join(',')}]. ` +\n `Both holdout campaigns must run the same scenarios with the same seed base.`,\n )\n }\n\n const before: number[] = []\n const after: number[] = []\n const cellIds: string[] = []\n for (const cellId of candCells) {\n const b = cellValue(baseline, cellId)\n const a = cellValue(candidate, cellId)\n // Only pair when BOTH sides produced the scalar (a dimension absent on one\n // side would otherwise create an unpaired observation).\n if (b === undefined || a === undefined) continue\n before.push(b)\n after.push(a)\n cellIds.push(cellId)\n }\n return { before, after, cellIds }\n}\n\nexport interface HeldoutSignificance {\n paired: PairedHoldout\n bootstrap: PairedBootstrapResult\n /** n paired observations. */\n n: number\n /** True iff n >= minProductiveRuns AND the CI lower bound clears the threshold. */\n significant: boolean\n /** Set when n < minProductiveRuns — too little evidence to claim significance. */\n fewRuns: boolean\n}\n\nexport interface HeldoutSignificanceOptions {\n deltaThreshold?: number\n minProductiveRuns?: number\n confidence?: number\n resamples?: number\n /** Fixed by default for a deterministic, reproducible gate verdict. */\n seed?: number\n statistic?: 'mean' | 'median'\n}\n\n/** Significance of the held-out composite lift: ship only when the paired\n * bootstrap CI lower bound on (candidate − baseline) exceeds `deltaThreshold`\n * (default 0 ⇒ \"confidently positive\"). Below `minProductiveRuns` paired\n * observations there is not enough evidence to claim significance → not\n * significant (`fewRuns`). Interpret `deltaThreshold` in the judge's native\n * composite scale. */\nexport function heldoutSignificance(\n paired: PairedHoldout,\n opts: HeldoutSignificanceOptions = {},\n): HeldoutSignificance {\n const deltaThreshold = opts.deltaThreshold ?? 0\n const minProductiveRuns = opts.minProductiveRuns ?? 3\n const bootstrap = pairedBootstrap(paired.before, paired.after, {\n confidence: opts.confidence ?? 0.95,\n resamples: opts.resamples ?? 2000,\n statistic: opts.statistic ?? 'median',\n seed: opts.seed ?? 1337,\n })\n const n = paired.before.length\n const fewRuns = n < minProductiveRuns\n const significant = !fewRuns && bootstrap.low > deltaThreshold\n return { paired, bootstrap, n, significant, fewRuns }\n}\n\nexport interface DimensionRegression {\n dimension: string\n bootstrap: PairedBootstrapResult\n /** True iff the CI lower bound on (candidate − baseline) is below −tolerance:\n * the candidate may have regressed this dimension by more than tolerance. */\n regressed: boolean\n tolerance: number\n n: number\n}\n\n/** Detect the native scale of a set of scores: 0-100 when any magnitude clears\n * 1.5, else [0,1]. Used to auto-scale the regression tolerance so a default\n * expressed for [0,1] is not silently a no-op on a 0-100 dimension. */\nexport function detectScale(values: number[]): 1 | 100 {\n return values.some((v) => Math.abs(v) > 1.5) ? 100 : 1\n}\n\n/** Per-critical-dimension regression guard. For each dimension, pair the\n * candidate vs baseline values by full cellId and bootstrap the paired delta;\n * a dimension is \"regressed\" when the CI lower bound < −tolerance (conservative\n * — blocks if the credible worst case exceeds tolerance, which is the right\n * posture for safety dimensions like `hallucination_free`). When `tolerance`\n * is omitted it auto-scales: 0.05 on [0,1], 5 on 0-100. */\nexport function dimensionRegressions(\n candidate: Map<string, Record<string, JudgeScore>>,\n baseline: Map<string, Record<string, JudgeScore>>,\n scenarioIds: Set<string>,\n criticalDimensions: string[],\n opts: { tolerance?: number; confidence?: number; resamples?: number; seed?: number } = {},\n): DimensionRegression[] {\n const out: DimensionRegression[] = []\n for (const dim of criticalDimensions) {\n const paired = pairHoldout(candidate, baseline, scenarioIds, (s) => s.dimensions[dim])\n if (paired.before.length === 0) continue // dimension not scored on this judge\n const tolerance = opts.tolerance ?? 0.05 * detectScale([...paired.before, ...paired.after])\n const bootstrap = pairedBootstrap(paired.before, paired.after, {\n confidence: opts.confidence ?? 0.95,\n resamples: opts.resamples ?? 2000,\n statistic: 'median',\n seed: opts.seed ?? 1337,\n })\n out.push({\n dimension: dim,\n bootstrap,\n regressed: bootstrap.low < -tolerance,\n tolerance,\n n: paired.before.length,\n })\n }\n return out\n}\n","/**\n * `defaultProductionGate` — composes the substrate's existing safety\n * primitives (red-team / reward-hacking / canary / heldout) into a single\n * Gate.decide shape. Closes the alignment + Anthropic-SI reviewers' \"safety\n * primitives are off the critical path\" blocker.\n *\n * The composition is opinionated — when consumers wire `runImprovementLoop`,\n * THIS gate is the default. Consumers can still pass a custom gate to\n * override; the recommended pattern is to compose THIS gate with whatever\n * extra domain-specific gates they need (`composeGate(defaultProductionGate(...), customGate)`).\n */\n\nimport type { CanaryReport } from '../../canary'\nimport { runCanaries } from '../../canary'\nimport type { RedTeamCase } from '../../red-team'\nimport { scoreRedTeamOutput } from '../../red-team'\nimport type { RewardHackingReport } from '../../rl/reward-hacking'\nimport { detectRewardHacking } from '../../rl/reward-hacking'\nimport type { RunRecord } from '../../run-record'\nimport type { Gate, GateContext, GateResult, Scenario } from '../types'\nimport { dimensionRegressions, heldoutSignificance, pairHoldout } from './statistical-heldout'\n\nexport interface DefaultProductionGateOptions {\n /** Required: scenarios held out from training; substrate compares\n * candidate-on-holdout vs baseline-on-holdout. */\n holdoutScenarios: Scenario[]\n /** Minimum held-out lift the **paired-bootstrap CI lower bound** must clear\n * to ship — NOT a point estimate. Default 0 ⇒ \"confidently positive at the\n * confidence level\". Interpreted in the judge's native composite scale (set\n * e.g. 2 for a 0-100 rubric to require a ≥2-point significant gain). */\n deltaThreshold?: number\n /** Confidence level for the held-out + dimension bootstraps. Default 0.95. */\n confidence?: number\n /** Bootstrap resamples. Default 2000. */\n bootstrapResamples?: number\n /** Fixed bootstrap seed for a deterministic verdict. Default 1337. */\n bootstrapSeed?: number\n /** Minimum paired holdout observations (scenarios × reps) before a\n * significance claim is allowed; below it the gate HOLDS with `few_runs`\n * rather than reading a degenerate CI. Default 3. */\n minProductiveRuns?: number\n /** Critical judge dimensions that must NOT significantly regress even when\n * the net composite rises (anti-Goodhart). The gate HOLDS if any listed\n * dimension's paired-delta CI lower bound < −`regressionTolerance`. E.g.\n * `['hallucination_free']` for a legal agent. */\n criticalDimensions?: string[]\n /** Tolerance for the per-dimension regression guard, in the dimension's\n * native scale. When omitted it auto-scales off observed magnitudes:\n * 0.05 on [0,1], 5 on 0-100. */\n regressionTolerance?: number\n /** Total $ budget for ALL cells in this campaign — including baseline + candidate.\n * Composite verdict refuses to ship when spend exceeded budget. */\n budgetUsd?: number\n /** Red-team cases to probe candidate outputs against. When omitted the\n * substrate uses `DEFAULT_RED_TEAM_CORPUS`. Provide a domain-specific\n * battery for tighter coverage. */\n redTeamBattery?: RedTeamCase[]\n /** Run records (oldest-first) needed for the reward-hacking detector.\n * Substrate populates from prior production-loop generations. */\n recentRuns?: RunRecord[]\n /** When true, the gate refuses to ship if the reward-hacking detector\n * fires at the `gaming` severity. Default true. */\n blockOnRewardHackingGaming?: boolean\n}\n\n/**\n * Opinionated production gate composing held-out significance, red-team, reward-hacking, and canary checks into a single `Gate.decide` decision.\n */\nexport function defaultProductionGate<TArtifact, TScenario extends Scenario>(\n options: DefaultProductionGateOptions,\n): Gate<TArtifact, TScenario> {\n const deltaThreshold = options.deltaThreshold ?? 0\n const confidence = options.confidence ?? 0.95\n const resamples = options.bootstrapResamples ?? 2000\n const seed = options.bootstrapSeed ?? 1337\n const minProductiveRuns = options.minProductiveRuns ?? 3\n const blockOnGaming = options.blockOnRewardHackingGaming ?? true\n\n return {\n name: 'defaultProductionGate',\n async decide(ctx: GateContext<TArtifact, TScenario>): Promise<GateResult> {\n const reasons: string[] = []\n const contributing: Array<{ name: string; passed: boolean; detail: unknown }> = []\n\n // ── (1) heldout composite lift — paired-bootstrap CI, NOT a point estimate\n // The shipped false positive: the baseline re-scored against itself read\n // run-to-run model noise (91 vs 95) as a \"+4 lift\" and shipped, because a\n // point estimate carries no confidence interval. Pair candidate vs\n // baseline holdout cells by FULL cellId (never averaging reps away) and\n // ship only when the bootstrap CI lower bound clears the threshold —\n // i.e. the gain is real at the confidence level, not noise.\n const scenarioIds = new Set(options.holdoutScenarios.map((s) => s.id))\n const sig = heldoutSignificance(\n pairHoldout(\n ctx.judgeScores,\n ctx.baselineJudgeScores ?? ctx.judgeScores,\n scenarioIds,\n (s) => s.composite,\n ),\n { deltaThreshold, minProductiveRuns, confidence, resamples, seed },\n )\n const delta = sig.bootstrap.median\n const heldoutPass = sig.significant\n contributing.push({\n name: 'heldout-significance',\n passed: heldoutPass,\n detail: {\n n: sig.n,\n deltaMedian: sig.bootstrap.median,\n ciLow: sig.bootstrap.low,\n ciHigh: sig.bootstrap.high,\n confidence: sig.bootstrap.confidence,\n deltaThreshold,\n fewRuns: sig.fewRuns,\n },\n })\n if (!heldoutPass) {\n reasons.push(\n sig.fewRuns\n ? `held-out: only ${sig.n} paired runs (< ${minProductiveRuns}) — too few to claim significance`\n : `held-out CI.low ${sig.bootstrap.low.toFixed(3)} ≤ threshold ${deltaThreshold} (median ${sig.bootstrap.median.toFixed(3)}, ${(sig.bootstrap.confidence * 100).toFixed(0)}% CI [${sig.bootstrap.low.toFixed(3)}, ${sig.bootstrap.high.toFixed(3)}])`,\n )\n }\n\n // ── (1b) per-dimension regression guard (anti-Goodhart) ──────────\n // A net composite gain can hide a regression on a safety-critical\n // dimension (e.g. hallucination_free for a legal agent — the verified run\n // gained +25/+25 on deadline/fee while LOSING -30 on hallucination, and\n // the composite-only gate never saw it). Block ship if any guarded\n // dimension's paired-delta CI lower bound falls below −tolerance.\n const dimRegs = options.criticalDimensions?.length\n ? dimensionRegressions(\n ctx.judgeScores,\n ctx.baselineJudgeScores ?? ctx.judgeScores,\n scenarioIds,\n options.criticalDimensions,\n { tolerance: options.regressionTolerance, confidence, resamples, seed },\n )\n : []\n const regressed = dimRegs.filter((d) => d.regressed)\n const dimPass = regressed.length === 0\n contributing.push({\n name: 'dimension-regression',\n passed: dimPass,\n detail: {\n guarded: options.criticalDimensions ?? [],\n regressions: dimRegs.map((d) => ({\n dimension: d.dimension,\n ciLow: d.bootstrap.low,\n median: d.bootstrap.median,\n tolerance: d.tolerance,\n n: d.n,\n regressed: d.regressed,\n })),\n },\n })\n if (!dimPass) {\n reasons.push(\n `critical dimension(s) regressed: ${regressed.map((d) => `${d.dimension} CI.low ${d.bootstrap.low.toFixed(3)} < -${d.tolerance}`).join('; ')}`,\n )\n }\n\n // ── (2) budget gate ─────────────────────────────────────────────\n const budgetPass =\n options.budgetUsd === undefined ||\n ctx.cost.candidate + ctx.cost.baseline <= options.budgetUsd\n contributing.push({\n name: 'budget',\n passed: budgetPass,\n detail: {\n candidateUsd: ctx.cost.candidate,\n baselineUsd: ctx.cost.baseline,\n budgetUsd: options.budgetUsd,\n },\n })\n if (!budgetPass) {\n reasons.push(\n `spend ${(ctx.cost.candidate + ctx.cost.baseline).toFixed(2)} > budget ${options.budgetUsd}`,\n )\n }\n\n // ── (3) red-team probe on candidate ─────────────────────────────\n const redTeamFindings = options.redTeamBattery\n ? probeRedTeam(ctx.candidateArtifacts, options.redTeamBattery)\n : { passed: true, findings: [] }\n contributing.push({\n name: 'red-team',\n passed: redTeamFindings.passed,\n detail: {\n failures: redTeamFindings.findings.length,\n sample: redTeamFindings.findings.slice(0, 3),\n },\n })\n if (!redTeamFindings.passed) {\n reasons.push(`red-team probe failed (${redTeamFindings.findings.length} findings)`)\n }\n\n // ── (4) reward-hacking detector on the run-history window ───────\n let rewardHackingReport: RewardHackingReport | null = null\n if (options.recentRuns && options.recentRuns.length >= 10) {\n rewardHackingReport = detectRewardHacking({ runs: options.recentRuns })\n }\n // reward-hacking severity is numeric (0..1). \"gaming\" threshold per\n // detectRewardHacking defaults = 0.6. Block when ANY finding is at\n // gaming threshold OR the report verdict is 'gaming'.\n const gamingThreshold = 0.6\n const gamingFindings = (rewardHackingReport?.findings ?? []).filter(\n (f) => f.severity >= gamingThreshold,\n )\n const rewardHackingPass =\n !rewardHackingReport ||\n !blockOnGaming ||\n (gamingFindings.length === 0 && rewardHackingReport.verdict !== 'gaming')\n contributing.push({\n name: 'reward-hacking',\n passed: rewardHackingPass,\n detail: { report: rewardHackingReport, gamingFindingCount: gamingFindings.length },\n })\n if (!rewardHackingPass) {\n reasons.push(\n `reward-hacking detector flagged ${gamingFindings.length} gaming-severity findings (verdict=${rewardHackingReport!.verdict})`,\n )\n }\n\n // ── (5) canary check on runs ────────────────────────────────────\n let canaryReport: CanaryReport | null = null\n if (options.recentRuns && options.recentRuns.length >= 10) {\n canaryReport = runCanaries(options.recentRuns, {})\n }\n // CanarySeverity is 'info' | 'warn' | 'error' — block on 'error'.\n const errorAlerts = (canaryReport?.alerts ?? []).filter((a) => a.severity === 'error')\n const canaryPass = errorAlerts.length === 0\n contributing.push({\n name: 'canary',\n passed: canaryPass,\n detail: { totalAlerts: canaryReport?.alerts.length ?? 0, errorAlerts: errorAlerts.length },\n })\n if (!canaryPass) {\n reasons.push(`canary error alerts: ${errorAlerts.length}`)\n }\n\n // ── Verdict ─────────────────────────────────────────────────────\n const allPassed = contributing.every((c) => c.passed)\n const decision = allPassed ? 'ship' : 'hold'\n\n return {\n decision,\n reasons: reasons.length > 0 ? reasons : ['all gates passed'],\n contributingGates: contributing,\n delta,\n }\n },\n }\n}\n\nfunction probeRedTeam<TArtifact>(\n artifacts: Map<string, TArtifact>,\n battery: RedTeamCase[],\n): { passed: boolean; findings: Array<{ scenarioId: string; reason: string }> } {\n const findings: Array<{ scenarioId: string; reason: string }> = []\n for (const [_cellId, artifact] of artifacts) {\n const text = extractText(artifact)\n if (text === undefined) continue\n for (const rtCase of battery) {\n const finding = scoreRedTeamOutput(text, [], rtCase)\n if (!finding.passed) {\n findings.push({ scenarioId: rtCase.id, reason: finding.reason ?? 'red-team probe failed' })\n }\n }\n }\n return { passed: findings.length === 0, findings }\n}\n\nfunction extractText(artifact: unknown): string | undefined {\n if (typeof artifact === 'string') return artifact\n if (artifact && typeof artifact === 'object') {\n const rec = artifact as Record<string, unknown>\n if (typeof rec.text === 'string') return rec.text\n if (typeof rec.output === 'string') return rec.output\n if (typeof rec.content === 'string') return rec.content\n }\n return undefined\n}\n","/**\n * Promotion policy over the evidence VECTOR — the substrate's answer to \"never\n * collapse the multi-objective promotion decision into one scalar.\" A\n * `defaultProductionGate` is one opinionated composition; this module factors\n * the decision into two reusable pieces so MANY policies can compete over the\n * SAME evidence (the quant-desk pattern: one evidence bus, plural strategies):\n *\n * buildEvidenceVector(ctx, objectives, opts) -> EvidenceVector // the bus\n * PromotionPolicy = (ev: EvidenceVector) => GateResult // a strategy\n * paretoPolicy(ev) // the default strategy\n * paretoSignificanceGate(options): Gate // bus + policy as a Gate\n *\n * The Pareto policy is SYMMETRIC multi-objective: every objective is BOTH a\n * potential gain source AND a safety floor (unlike `defaultProductionGate`,\n * where only `composite` can win and `criticalDimensions` are pure floors). A\n * candidate ships iff it weakly DOMINATES the baseline at the confidence level —\n * no objective credibly worse (CI floor breach) AND at least one objective\n * credibly better (CI gain). Insufficient evidence on ANY axis -> need_more_work\n * (NOT folded into hold: \"gather more reps\" and \"reject\" are different actions).\n *\n * Cost/latency are NOT CI axes here — `GateContext` carries only an aggregate\n * per-side cost, no per-cell observation vector to bootstrap. Treat them as hard\n * constraints (compose with a budget gate via `composeGate`), not faked CIs.\n */\n\nimport type { Direction } from '../../pareto'\nimport { type PairedBootstrapResult, pairedBootstrap } from '../../statistics'\nimport type { Gate, GateContext, GateDecision, GateResult, JudgeScore, Scenario } from '../types'\nimport { detectScale, pairHoldout } from './statistical-heldout'\n\n/** Where an objective's per-cell scalar comes from. `composite` reads the\n * judge's composite; `dimension` reads a named per-dimension score. */\nexport type ObjectiveSource = { kind: 'composite' } | { kind: 'dimension'; dimension: string }\n\nexport interface PromotionObjective {\n /** Stable label used in reports + `contributingGates`. */\n name: string\n source: ObjectiveSource\n /** 'maximize' (quality dims) or 'minimize' (error/risk/length dims). Orients\n * the paired delta so a positive bootstrap always means \"candidate better\". */\n direction: Direction\n /** The good-direction paired-delta CI lower bound must EXCEED this to count\n * as a significant gain on this axis. Interpreted in the judge's native\n * scale. Default 0 (⇒ \"confidently better\"). */\n gainThreshold?: number\n /** A floor breach (regression) is declared when the good-direction CI lower\n * bound is below −floorTolerance. When omitted it auto-scales off observed\n * magnitudes (0.05 on [0,1], 5 on 0-100), matching `dimensionRegressions`. */\n floorTolerance?: number\n}\n\n/** Per-axis verdict from the good-direction paired bootstrap. */\nexport type AxisVerdict = 'improved' | 'regressed' | 'flat' | 'few_runs'\n\nexport interface AxisEvidence {\n name: string\n source: ObjectiveSource\n direction: Direction\n /** Paired bootstrap on the GOOD-DIRECTION delta (oriented by `direction`):\n * a positive value means the candidate is better on this axis. */\n bootstrap: PairedBootstrapResult\n /** Paired observations contributing to this axis. */\n n: number\n gainThreshold: number\n floorTolerance: number\n verdict: AxisVerdict\n}\n\nexport interface EvidenceVector {\n /** One entry per objective — NOTHING averaged across axes. */\n axes: AxisEvidence[]\n /** Smallest paired n across axes that produced observations — the binding\n * evidence-sufficiency constraint. 0 when no axis produced observations. */\n minN: number\n /** Aggregate per-side cost from the gate context (a constraint input, not a\n * CI axis — see the module header). */\n cost: { candidate: number; baseline: number }\n}\n\n/** A promotion strategy: a pure function from the evidence vector to a verdict.\n * Many policies can run over the same `EvidenceVector` and disagree — that's\n * the point (competing strategies, shared evidence). */\nexport type PromotionPolicy = (ev: EvidenceVector) => GateResult\n\nexport interface BuildEvidenceVectorOptions {\n /** Minimum paired observations before an axis can claim significance; below\n * it the axis is `few_runs`. Default 3. */\n minProductiveRuns?: number\n /** Confidence level for every axis bootstrap. Default 0.95. */\n confidence?: number\n /** Bootstrap resamples. Default 2000. */\n resamples?: number\n /** Fixed bootstrap seed for a deterministic, reproducible verdict. Default 1337. */\n seed?: number\n}\n\n/**\n * The Evidence Bus. For each objective, pair candidate vs baseline by full\n * cellId and bootstrap a CI on the good-direction paired delta. Reuses the\n * exact `pairHoldout` + `pairedBootstrap` machinery the held-out gate uses, so\n * a single source of truth governs pairing granularity + scale handling.\n */\nexport function buildEvidenceVector<TArtifact, TScenario extends Scenario>(\n ctx: GateContext<TArtifact, TScenario>,\n objectives: PromotionObjective[],\n opts: BuildEvidenceVectorOptions = {},\n): EvidenceVector {\n if (objectives.length === 0) {\n throw new Error('buildEvidenceVector: at least 1 objective required')\n }\n const minProductiveRuns = opts.minProductiveRuns ?? 3\n const confidence = opts.confidence ?? 0.95\n const resamples = opts.resamples ?? 2000\n const seed = opts.seed ?? 1337\n const baseline = ctx.baselineJudgeScores ?? ctx.judgeScores\n const scenarioIds = new Set(ctx.scenarios.map((s) => s.id))\n\n const axes: AxisEvidence[] = []\n for (const obj of objectives) {\n let select: (s: JudgeScore) => number | undefined\n if (obj.source.kind === 'composite') {\n select = (s) => s.composite\n } else {\n const dim = obj.source.dimension\n select = (s) => s.dimensions[dim]\n }\n const paired = pairHoldout(ctx.judgeScores, baseline, scenarioIds, select)\n // Orient to the good direction: maximize ⇒ bootstrap (candidate − baseline);\n // minimize ⇒ bootstrap (baseline − candidate) by swapping args, so a\n // positive bootstrap always reads as \"candidate better on this axis\".\n const before = obj.direction === 'maximize' ? paired.before : paired.after\n const after = obj.direction === 'maximize' ? paired.after : paired.before\n const bootstrap = pairedBootstrap(before, after, {\n confidence,\n resamples,\n statistic: 'median',\n seed,\n })\n const n = paired.before.length\n const floorTolerance =\n obj.floorTolerance ?? 0.05 * detectScale([...paired.before, ...paired.after])\n const gainThreshold = obj.gainThreshold ?? 0\n // Floor check precedes the gain check: a credible regression must never be\n // masked as \"improved\". With the defaults (gainThreshold 0, positive floor)\n // the regions are disjoint and order is moot, but a consumer who sets a\n // negative gainThreshold (\"accept small dips\") could otherwise have a real\n // floor breach classified as a gain — anti-Goodhart wins the tie.\n const verdict: AxisVerdict =\n n < minProductiveRuns\n ? 'few_runs'\n : bootstrap.low < -floorTolerance\n ? 'regressed'\n : bootstrap.low > gainThreshold\n ? 'improved'\n : 'flat'\n axes.push({\n name: obj.name,\n source: obj.source,\n direction: obj.direction,\n bootstrap,\n n,\n gainThreshold,\n floorTolerance,\n verdict,\n })\n }\n const ns = axes.map((a) => a.n).filter((n) => n > 0)\n const minN = ns.length > 0 ? Math.min(...ns) : 0\n return { axes, minN, cost: { candidate: ctx.cost.candidate, baseline: ctx.cost.baseline } }\n}\n\n/**\n * The default strategy: symmetric multi-objective Pareto significance. Ship iff\n * the candidate weakly dominates the baseline at the confidence level — no axis\n * credibly worse AND ≥1 axis credibly better. Floor breach on any axis → hold\n * (anti-Goodhart, dominates everything). Insufficient evidence on any axis →\n * need_more_work. Statistically equivalent → hold (never ship noise).\n */\nexport const paretoPolicy: PromotionPolicy = (ev) => {\n const contributingGates = ev.axes.map((ax) => ({\n name: `objective:${ax.name}`,\n passed: ax.verdict === 'improved',\n detail: {\n direction: ax.direction,\n source: ax.source,\n verdict: ax.verdict,\n n: ax.n,\n deltaMedian: ax.bootstrap.median,\n ciLow: ax.bootstrap.low,\n ciHigh: ax.bootstrap.high,\n confidence: ax.bootstrap.confidence,\n gainThreshold: ax.gainThreshold,\n floorTolerance: ax.floorTolerance,\n },\n }))\n\n const regressed = ev.axes.filter((a) => a.verdict === 'regressed')\n const fewRuns = ev.axes.filter((a) => a.verdict === 'few_runs')\n const improved = ev.axes.filter((a) => a.verdict === 'improved')\n\n let decision: GateDecision\n const reasons: string[] = []\n if (regressed.length > 0) {\n // Floor breach dominates: a credible regression on ANY axis blocks ship even\n // if another axis improved. This makes the +gain/−safety false positive\n // structurally impossible whenever the safety dim is an objective.\n decision = 'hold'\n for (const a of regressed) {\n reasons.push(\n `objective '${a.name}' regressed: good-direction CI.low ${a.bootstrap.low.toFixed(3)} < -${a.floorTolerance} (n=${a.n})`,\n )\n }\n } else if (fewRuns.length > 0) {\n // No credible regression on the scored axes, but ≥1 axis lacks the evidence\n // to claim a gain ⇒ gather more reps, do NOT reject.\n decision = 'need_more_work'\n for (const a of fewRuns) {\n reasons.push(\n `objective '${a.name}' has only n=${a.n} paired runs — insufficient evidence to claim significance`,\n )\n }\n } else if (improved.length > 0) {\n // Weakly dominates (no axis worse) AND strictly better on ≥1 axis ⇒ a Pareto\n // improvement at the confidence level.\n decision = 'ship'\n reasons.push(\n `Pareto improvement at the confidence level: ${improved\n .map(\n (a) =>\n `'${a.name}' +${a.bootstrap.median.toFixed(3)} (CI.low ${a.bootstrap.low.toFixed(3)})`,\n )\n .join(', ')}; no objective regressed`,\n )\n } else {\n // Enough evidence, nothing credibly better or worse ⇒ statistically\n // equivalent. Do NOT ship a no-op.\n decision = 'hold'\n reasons.push(\n 'no Pareto improvement: candidate statistically equivalent to baseline on every objective',\n )\n }\n\n // `delta` surfaces the composite axis if present, else the first axis — a\n // single convenience scalar; the vector lives in `contributingGates`.\n const composite = ev.axes.find((a) => a.source.kind === 'composite') ?? ev.axes[0]\n return { decision, reasons, contributingGates, delta: composite?.bootstrap.median }\n}\n\nexport interface ParetoSignificanceGateOptions extends BuildEvidenceVectorOptions {\n /** The objective vector. Every axis is both a gain source and a safety floor. */\n objectives: PromotionObjective[]\n /** Strategy applied to the evidence vector. Default `paretoPolicy`. Override\n * to run a stricter/looser strategy over the SAME bus (competing policies). */\n policy?: PromotionPolicy\n /** Override the gate name in reports. */\n name?: string\n}\n\n/**\n * Wrap the bus + a policy as a `Gate`. Plugs into the existing\n * `runImprovementLoop({ gate })` slot and composes via `composeGate`; default\n * loop behavior is unchanged because consumers opt in by passing this gate.\n */\nexport function paretoSignificanceGate<TArtifact = unknown, TScenario extends Scenario = Scenario>(\n options: ParetoSignificanceGateOptions,\n): Gate<TArtifact, TScenario> {\n if (options.objectives.length === 0) {\n throw new Error('paretoSignificanceGate: at least 1 objective required')\n }\n const policy = options.policy ?? paretoPolicy\n return {\n name: options.name ?? 'paretoSignificanceGate',\n async decide(ctx: GateContext<TArtifact, TScenario>): Promise<GateResult> {\n const ev = buildEvidenceVector(ctx, options.objectives, options)\n return policy(ev)\n },\n }\n}\n","/**\n * `runEval` — the simplest preset over `runCampaign`. No optimizer, no\n * gate, no auto-PR. Just: run scenarios through dispatch, score with\n * judges, return CampaignResult.\n *\n * The 80% case for consumers who want a scorecard, not an improvement loop.\n */\n\nimport { type RunCampaignOptions, runCampaign } from '../run-campaign'\nimport type { CampaignResult, Scenario } from '../types'\n\nexport interface RunEvalOptions<TScenario extends Scenario, TArtifact>\n extends Omit<RunCampaignOptions<TScenario, TArtifact>, 'runDir'> {\n runDir: string\n}\n\n/**\n * Simplest evaluation preset: run scenarios through dispatch, score with judges, and return a `CampaignResult` — no optimizer, no gate, no PR.\n */\nexport async function runEval<TScenario extends Scenario, TArtifact>(\n opts: RunEvalOptions<TScenario, TArtifact>,\n): Promise<CampaignResult<TArtifact, TScenario>> {\n return runCampaign(opts)\n}\n","/**\n * `evolutionaryProposer` — adapts a stateless `Mutator` (population mutation:\n * GEPA / AxGEPA / reflective-mutation) into a `SurfaceProposer`. This is\n * the evolutionary strategy: each generation, mutate the current best surface\n * into N candidates, measure, select. No generation memory beyond the current\n * surface; the loop body handles ranking + promotion.\n *\n * The reflective alternative is agent-runtime's runtime proposer with a\n * `reflectiveGenerator` / `agenticGenerator`: it reasons over the report +\n * trace findings to propose targeted edits rather than blind mutations. Both\n * conform to `SurfaceProposer`; the improvement loop is identical either way.\n */\n\nimport type { Mutator, SurfaceProposer } from '../types'\n\nexport interface EvolutionaryProposerOptions<TFindings = unknown> {\n mutator: Mutator<TFindings>\n /** External findings fed to the mutator each generation. Default: []. */\n findings?: TFindings[]\n}\n\n/**\n * Wrap a stateless `Mutator` (GEPA, AxGEPA, reflective-mutation) as a `SurfaceProposer` that mutates the current best surface into N candidates each generation.\n */\nexport function evolutionaryProposer<TFindings = unknown>(\n opts: EvolutionaryProposerOptions<TFindings>,\n): SurfaceProposer<TFindings> {\n return {\n kind: `evolutionary:${opts.mutator.kind}`,\n async propose({ currentSurface, findings, populationSize, signal }) {\n return opts.mutator.mutate({\n findings: findings.length > 0 ? findings : (opts.findings ?? []),\n currentSurface,\n populationSize,\n signal,\n })\n },\n }\n}\n"],"mappings":"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","names":[]}