@tangle-network/agent-eval 0.123.2 → 0.123.4

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.
Files changed (65) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +48 -4
  3. package/dist/analyst/index.d.ts +18 -3
  4. package/dist/analyst/index.js +5 -5
  5. package/dist/benchmarks/index.d.ts +10 -3
  6. package/dist/benchmarks/index.js +7 -7
  7. package/dist/campaign/index.d.ts +277 -250
  8. package/dist/campaign/index.js +20 -16
  9. package/dist/{chunk-EBDOTTZJ.js → chunk-4KO6D2BO.js} +3 -3
  10. package/dist/{chunk-3FCG7FBV.js → chunk-D5JZ7UDZ.js} +2 -2
  11. package/dist/{chunk-BGVTIE2C.js → chunk-GRCDRKII.js} +13 -2
  12. package/dist/chunk-GRCDRKII.js.map +1 -0
  13. package/dist/{chunk-FHFTYX2Q.js → chunk-JKDNAOF5.js} +2 -2
  14. package/dist/{chunk-RQP5UTK5.js → chunk-LT4J7ULK.js} +710 -359
  15. package/dist/chunk-LT4J7ULK.js.map +1 -0
  16. package/dist/{chunk-WXQTVEKM.js → chunk-MHPEGJHC.js} +3 -3
  17. package/dist/{chunk-J7S4YM27.js → chunk-MK3Q6GCL.js} +28 -14
  18. package/dist/chunk-MK3Q6GCL.js.map +1 -0
  19. package/dist/{chunk-DNVPOYUS.js → chunk-N7ZS6PEF.js} +2 -2
  20. package/dist/{chunk-4SOQ4ND2.js → chunk-QFQZ3U3X.js} +1 -1
  21. package/dist/chunk-QFQZ3U3X.js.map +1 -0
  22. package/dist/{chunk-WMJR67FX.js → chunk-QNVBALEX.js} +4 -4
  23. package/dist/{chunk-E3HAD4A3.js → chunk-SJT4OBVL.js} +2 -2
  24. package/dist/{chunk-HQY7LBV2.js → chunk-SUN7QLPB.js} +6 -6
  25. package/dist/{chunk-HQY7LBV2.js.map → chunk-SUN7QLPB.js.map} +1 -1
  26. package/dist/{chunk-5YMKIFYP.js → chunk-WP3GEXKI.js} +3 -3
  27. package/dist/cli.js +3 -3
  28. package/dist/contract/index.d.ts +18 -3
  29. package/dist/contract/index.js +7 -7
  30. package/dist/fuzz.d.ts +8 -1
  31. package/dist/fuzz.js +4 -4
  32. package/dist/index.d.ts +27 -9
  33. package/dist/index.js +14 -12
  34. package/dist/index.js.map +1 -1
  35. package/dist/multishot/index.d.ts +2 -2
  36. package/dist/openapi.json +1 -1
  37. package/dist/rl.d.ts +15 -2
  38. package/dist/rl.js +4 -2
  39. package/dist/rl.js.map +1 -1
  40. package/dist/{run-campaign-ZKR5MQMQ.js → run-campaign-I3JXKVAK.js} +3 -3
  41. package/dist/traces.d.ts +2 -2
  42. package/dist/traces.js +1 -1
  43. package/dist/wire/index.d.ts +16 -1
  44. package/dist/wire/index.js +3 -3
  45. package/docs/campaign-proposers.md +142 -131
  46. package/docs/concepts.md +30 -34
  47. package/docs/design/loop-taxonomy.md +2 -3
  48. package/docs/design.md +0 -1
  49. package/docs/multi-shot-optimization.md +41 -49
  50. package/package.json +1 -1
  51. package/dist/chunk-4SOQ4ND2.js.map +0 -1
  52. package/dist/chunk-BGVTIE2C.js.map +0 -1
  53. package/dist/chunk-J7S4YM27.js.map +0 -1
  54. package/dist/chunk-RQP5UTK5.js.map +0 -1
  55. package/docs/improvement-glossary.md +0 -204
  56. package/docs/self-improvement-map.md +0 -115
  57. /package/dist/{chunk-EBDOTTZJ.js.map → chunk-4KO6D2BO.js.map} +0 -0
  58. /package/dist/{chunk-3FCG7FBV.js.map → chunk-D5JZ7UDZ.js.map} +0 -0
  59. /package/dist/{chunk-FHFTYX2Q.js.map → chunk-JKDNAOF5.js.map} +0 -0
  60. /package/dist/{chunk-WXQTVEKM.js.map → chunk-MHPEGJHC.js.map} +0 -0
  61. /package/dist/{chunk-DNVPOYUS.js.map → chunk-N7ZS6PEF.js.map} +0 -0
  62. /package/dist/{chunk-WMJR67FX.js.map → chunk-QNVBALEX.js.map} +0 -0
  63. /package/dist/{chunk-E3HAD4A3.js.map → chunk-SJT4OBVL.js.map} +0 -0
  64. /package/dist/{chunk-5YMKIFYP.js.map → chunk-WP3GEXKI.js.map} +0 -0
  65. /package/dist/{run-campaign-ZKR5MQMQ.js.map → run-campaign-I3JXKVAK.js.map} +0 -0
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  planCampaignRun,
3
3
  runCampaign
4
- } from "./chunk-3FCG7FBV.js";
4
+ } from "./chunk-D5JZ7UDZ.js";
5
5
  import "./chunk-PJQFMIOX.js";
6
- import "./chunk-BGVTIE2C.js";
6
+ import "./chunk-GRCDRKII.js";
7
7
  import "./chunk-VI2UW6B6.js";
8
8
  import "./chunk-ONWEPEDO.js";
9
9
  import "./chunk-PZ5AY32C.js";
@@ -11,4 +11,4 @@ export {
11
11
  planCampaignRun,
12
12
  runCampaign
13
13
  };
14
- //# sourceMappingURL=run-campaign-ZKR5MQMQ.js.map
14
+ //# sourceMappingURL=run-campaign-I3JXKVAK.js.map
package/dist/traces.d.ts CHANGED
@@ -2048,7 +2048,7 @@ declare function firstStringAttr(attrs: Record<string, unknown>, keys: readonly
2048
2048
  *
2049
2049
  * This is the offline ingestion primitive the AppWorld proposer bench and the
2050
2050
  * hosted Intelligence product both stand on: traces in, paper-grade rows
2051
- * out, ready for `compareProposers` / `analyzeRuns` / the promotion gate.
2051
+ * out, ready for `compareOptimizationMethods` / `analyzeRuns` / the promotion gate.
2052
2052
  *
2053
2053
  * Aggregation per trace:
2054
2054
  * - tokenUsage: reconcile input, output, cache-read, and cache-write across
@@ -2079,7 +2079,7 @@ interface OtlpToRunRecordsOptions {
2079
2079
  /** Logical experiment grouping for every produced record. */
2080
2080
  experimentId: string;
2081
2081
  /** Candidate (variant) id — the surface these traces exercised. The
2082
- * bench passes the proposer label here so `compareProposers` can pair rows. */
2082
+ * bench passes the proposer label here so `compareOptimizationMethods` can pair rows. */
2083
2083
  candidateId: string;
2084
2084
  /** Split assignment for every produced record. Default `'holdout'` —
2085
2085
  * ingested traces are evidence, not the optimizer's training pool. */
package/dist/traces.js CHANGED
@@ -27,7 +27,7 @@ import {
27
27
  scoreTraceInsightReadiness,
28
28
  tokenizeDomainWords,
29
29
  traceAnalystOnRunComplete
30
- } from "./chunk-4SOQ4ND2.js";
30
+ } from "./chunk-QFQZ3U3X.js";
31
31
  import "./chunk-H5UD2323.js";
32
32
  import {
33
33
  extractUsage,
@@ -50,9 +50,16 @@ interface CostReceiptInput extends CostUsage {
50
50
  costUnknown?: boolean;
51
51
  usageUnknown?: boolean;
52
52
  }
53
+ /** Per-million token rates for a model or endpoint not covered by package pricing. */
54
+ interface CustomTokenPricing {
55
+ inputUsdPerMillion: number;
56
+ outputUsdPerMillion: number;
57
+ }
53
58
  type MaximumCharge = {
54
59
  externallyEnforcedMaximumUsd: number;
55
60
  } | ({
61
+ customTokenPricing: CustomTokenPricing;
62
+ } & Pick<CostUsage, 'inputTokens' | 'outputTokens'>) | ({
56
63
  model: string;
57
64
  } & CostUsage);
58
65
  interface RunPaidCallInput<T> {
@@ -64,7 +71,7 @@ interface RunPaidCallInput<T> {
64
71
  model?: string;
65
72
  tags?: Record<string, string>;
66
73
  signal?: AbortSignal;
67
- /** Provider-enforced dollar maximum, or maximum priced token usage. Required when capped. */
74
+ /** Provider-enforced dollar maximum, or maximum token usage with known pricing. Required when capped. */
68
75
  maximumCharge?: MaximumCharge;
69
76
  /** `callId` can be forwarded as the provider's idempotency key. */
70
77
  execute(signal: AbortSignal, callId: string): Promise<T>;
@@ -668,6 +675,8 @@ interface LlmClientOptions {
668
675
  deadlineMs?: number;
669
676
  /** Total provider attempts. Legacy option name; default 3 (1 initial + 2 retries). */
670
677
  maxRetries?: number;
678
+ /** Token rates used when the provider omits cost or package pricing does not cover the model. */
679
+ customTokenPricing?: CustomTokenPricing;
671
680
  /**
672
681
  * Transport for requests that declare `jsonSchema`. `native` sends
673
682
  * `response_format: json_schema`; `json-object` sends the broadly supported
@@ -675,6 +684,12 @@ interface LlmClientOptions {
675
684
  * instructions. Default: `native`.
676
685
  */
677
686
  jsonSchemaTransport?: 'native' | 'json-object';
687
+ /**
688
+ * JSON payload parsing policy. `extract` accepts fenced or prose-prefixed JSON.
689
+ * `exact` requires the complete response content to be one JSON value.
690
+ * Default: `extract`.
691
+ */
692
+ jsonPayloadMode?: 'extract' | 'exact';
678
693
  /** Fetch implementation — defaults to global `fetch`. Override for custom transport (e.g. tests). */
679
694
  fetch?: typeof fetch;
680
695
  /**
@@ -34,9 +34,9 @@ import {
34
34
  runRpcOnce,
35
35
  startServer,
36
36
  startServerAsync
37
- } from "../chunk-EBDOTTZJ.js";
38
- import "../chunk-J7S4YM27.js";
39
- import "../chunk-BGVTIE2C.js";
37
+ } from "../chunk-4KO6D2BO.js";
38
+ import "../chunk-MK3Q6GCL.js";
39
+ import "../chunk-GRCDRKII.js";
40
40
  import "../chunk-VI2UW6B6.js";
41
41
  import "../chunk-PC4UYEBM.js";
42
42
  import "../chunk-ONWEPEDO.js";
@@ -1,170 +1,181 @@
1
- # Campaign Proposers, ELI5
1
+ # Candidate Generation and Method Comparison
2
2
 
3
- A campaign proposer is the part of an improvement loop that says: "try this
4
- candidate next."
3
+ `SurfaceProposer` generates candidate prompts or configs.
4
+ It does not run an agent, score output, or choose a winner.
5
5
 
6
- It does not run your agent. It does not score anything. It only proposes a new
7
- surface to measure. A surface is the thing you are changing: a prompt string, a
8
- serialized config string, or a code/worktree surface.
6
+ `OptimizationMethod` runs a complete search procedure on train and selection data.
7
+ It returns one selected surface plus its optimization cost.
9
8
 
10
- Use **proposer** for this role. Older optimizer APIs used "driver"; that word is
11
- now reserved for execution, sandbox, and router agents that actually drive
12
- workers.
9
+ `compareOptimizationMethods` runs multiple methods, waits for every method to finish, then scores their selected surfaces on the same final test data.
13
10
 
14
- ## The Loop
15
-
16
- ```text
17
- current surface
18
- -> proposer suggests candidate surfaces
19
- -> runCampaign runs each candidate on scenarios
20
- -> judges score the artifacts
21
- -> runOptimization picks the best candidate
22
- -> runImprovementLoop re-scores on holdout and gates release
23
- ```
24
-
25
- ## Proposer Input
26
-
27
- Every `SurfaceProposer.propose(ctx)` receives:
28
-
29
- | Field | Plain meaning |
11
+ | API | Responsibility |
30
12
  |---|---|
31
- | `currentSurface` | The prompt/config/code surface currently being improved. |
32
- | `history` | What candidates were tried before and how they scored. |
33
- | `findings` | Failure analysis or analyst findings from traces/eval runs. |
34
- | `populationSize` | How many candidates the loop asks for this generation. |
35
- | `generation` | Which generation number this is. |
36
- | `signal` | Abort signal for cancellation. |
37
- | `report` | Optional larger analysis report. |
38
- | `dataset` | Optional labeled scenario store. |
39
- | `paretoParents` | Optional non-dominated surfaces from prior generations. |
13
+ | `SurfaceProposer` | Suggest the next candidate surface. |
14
+ | `runOptimization` | Run and score candidates on training scenarios. |
15
+ | `runImprovementLoop` | Optimize one surface and apply a release rule on separate scenarios. |
16
+ | `OptimizationMethod` | Adapt one complete optimization procedure for comparison. |
17
+ | `compareOptimizationMethods` | Compare selected surfaces on shared final test data. |
40
18
 
41
- ## Proposer Output
19
+ ## Candidate Generators
42
20
 
43
- A proposer returns candidates:
21
+ Every `SurfaceProposer.propose(ctx)` receives the current surface, prior candidate scores, findings, requested candidate count, generation number, and cancellation signal.
22
+ It may also receive a larger analysis report, a labeled scenario store, or Pareto parents when the caller provides them.
23
+
24
+ A proposer may return a bare surface or a labeled candidate:
44
25
 
45
26
  ```ts
46
27
  {
47
- surface: 'the full new prompt or config',
48
- label: 'short name',
49
- rationale: 'why this change should help'
28
+ surface: 'the complete new prompt or config',
29
+ label: 'require-citations',
30
+ rationale: 'three training failures omitted source references',
50
31
  }
51
32
  ```
52
33
 
53
- Bare surfaces are still accepted, but `label` and `rationale` make results
54
- auditable, so new proposers should return `ProposedCandidate`.
55
-
56
- ## Which Proposer To Use
34
+ Use a labeled `ProposedCandidate` when you need the result to retain why the candidate was generated.
57
35
 
58
- | Proposer factory | Best when | Output surface |
36
+ | Factory | Use it for | Surface |
59
37
  |---|---|---|
60
- | `gepaProposer` | You want a strong prompt rewrite driven by prior scores and findings. | prompt string |
61
- | `skillOptProposer` | You are editing a structured skill/runbook and want anchored small patches. | prompt/skill string |
62
- | `aceProposer` | You want append-only lessons from findings, preserving every distinct lesson. | prompt/playbook string |
63
- | `memoryCurationProposer` | You want compact deduped lessons from findings. | prompt/playbook string |
64
- | `parameterSweepProposer` | You want FAPO-style config/parameter edits from a JSON config surface. | JSON string |
65
- | `fapoProposer` | You want the FAPO policy: prompt first, then parameter, then structural only when evidence supports escalation. | whatever its level proposer returns |
66
-
67
- ## FAPO Proposer
38
+ | `gepaProposer` | Rewrite a prompt from prior scores and findings. | string |
39
+ | `skillOptProposer` | Apply bounded edits to a structured skill or runbook. | string |
40
+ | `aceProposer` | Append distinct lessons from findings. | string |
41
+ | `memoryCurationProposer` | Deduplicate and compact lessons from findings. | string |
42
+ | `parameterSweepProposer` | Apply declared changes to a JSON config. | JSON string |
43
+ | `fapoProposer` | Try prompt, parameter, and optional structural changes under one escalation policy. | caller-defined |
68
44
 
69
- FAPO is not "another prompt mutator." The paper describes a reviewed escalation
70
- policy:
45
+ ## Compare Complete Methods
71
46
 
72
- 1. evaluate the current workflow,
73
- 2. attribute failures to prompt, parameter/config, or structure,
74
- 3. propose one scoped change,
75
- 4. review the change for scope/leakage/compatibility,
76
- 5. measure it,
77
- 6. keep moving or escalate only when the cheaper level is exhausted.
78
-
79
- The simplest useful setup is prompt plus JSON config. Structural/code edits are
80
- optional and should be injected by the app or runtime layer.
47
+ The following call owns the shared baseline, runner, judges, directories, and three data sets.
48
+ Method configuration contains only settings that differ by method.
81
49
 
82
50
  ```ts
83
51
  import {
84
- fapoProposer,
85
- gepaProposer,
86
- parameterSweepProposer,
87
- runImprovementLoop,
52
+ type BuiltinOptimizationMethodConfig,
53
+ compareOptimizationMethods,
54
+ gepaParetoMethod,
55
+ gepaReflectionMethod,
56
+ skillOptMethod,
88
57
  } from '@tangle-network/agent-eval/campaign'
89
58
 
90
- const proposer = fapoProposer({
91
- scope: { allowedLevels: ['prompt', 'parameter'] },
92
- promptProposer: gepaProposer({ llm, model, target: 'agent prompt' }),
93
- parameterProposer: parameterSweepProposer({
94
- candidates: [
95
- {
96
- label: 'raise-retrieval-k',
97
- rationale: 'retrieval misses indicate the search budget may be too low',
98
- changes: [{ path: 'retrieval.k', value: 10 }],
99
- },
100
- ],
101
- }),
102
- })
59
+ const methodConfig: BuiltinOptimizationMethodConfig<MyScenario, MyArtifact> = {
60
+ llm,
61
+ model,
62
+ target: 'the complete prompt or config being improved',
63
+ populationSize: 2,
64
+ maxGenerations: 3,
65
+ maxEpochs: 6,
66
+ }
103
67
 
104
- await runImprovementLoop({
105
- scenarios: trainScenarios,
106
- holdoutScenarios,
107
- baselineSurface: JSON.stringify(currentConfig),
68
+ const comparison = await compareOptimizationMethods<MyScenario, MyArtifact>({
69
+ methods: [
70
+ gepaReflectionMethod(methodConfig),
71
+ gepaParetoMethod(methodConfig),
72
+ skillOptMethod(methodConfig),
73
+ ],
74
+ baselineSurface,
75
+ trainScenarios,
76
+ selectionScenarios,
77
+ testScenarios,
108
78
  dispatchWithSurface,
109
79
  judges,
110
- proposer,
111
- gate,
112
- autoOnPromote: 'none',
113
80
  runDir,
114
- populationSize: 1,
115
- maxGenerations: 10,
81
+ optimizationRunOptions: {
82
+ costCeiling: 5,
83
+ dispatchTimeoutMs: 60_000,
84
+ maxConcurrency: 4,
85
+ },
86
+ optimizationConcurrency: 2,
87
+ costCeiling: 2,
88
+ maxConcurrency: 4,
89
+ confidence: 0.95,
116
90
  })
117
91
  ```
118
92
 
119
- If you do have a real code/worktree proposer, pass it as `structuralProposer`.
120
- `agent-eval` intentionally does not provide that proposer because this package
121
- measures candidates; the runtime or app owns code generation.
93
+ The runnable version is [`examples/compare-optimization-methods`](../examples/compare-optimization-methods/).
94
+
95
+ ## Data Use
96
+
97
+ | Set | Who can read it | Purpose |
98
+ |---|---|---|
99
+ | Train | Optimization methods and candidate generators | Generate and fit candidates. |
100
+ | Selection | Optimization methods | Accept candidates, stop early, and select one surface per method. |
101
+ | Test | `compareOptimizationMethods` only | Estimate final lift and rank methods. |
102
+
103
+ All three sets must be non-empty and pairwise disjoint by scenario ID.
104
+ Test must contain at least two scenarios.
105
+ Two is only an API minimum; use enough scenarios to detect the effect size that matters for your product.
106
+
107
+ Each method receives independent copies of train and selection scenarios.
108
+ The final test set is absent from `OptimizationMethodInput`.
109
+ Every method finishes before the first test call starts.
110
+ When `optimizationConcurrency` is greater than one, the shared runner and judges must support concurrent calls.
111
+
112
+ ## Execution And Cost
113
+
114
+ `optimizationConcurrency` controls how many methods run at once.
115
+ `optimizationRunOptions.maxConcurrency` controls scenario calls inside each method.
116
+ Top-level `maxConcurrency` controls scenario calls during final test scoring.
122
117
 
123
- For side-by-side experiments with existing proposers, use the compare entry:
118
+ `optimizationRunOptions.costCeiling` is a separate limit for each method.
119
+ Top-level `costCeiling` is one shared limit across baseline and selected-surface scoring on final test.
120
+
121
+ The result reports three cost objects:
124
122
 
125
123
  ```ts
126
- import {
127
- compareProposers,
128
- fapoEscalationEntry,
129
- gepaParetoEntry,
130
- } from '@tangle-network/agent-eval/campaign'
124
+ comparison.optimizationCost
125
+ comparison.testCost
126
+ comparison.totalCost
127
+ ```
131
128
 
132
- await compareProposers({
133
- proposers: [
134
- gepaParetoEntry(config),
135
- fapoEscalationEntry({
136
- ...config,
137
- parameterCandidates: [
138
- {
139
- label: 'raise-retrieval-k',
140
- rationale: 'retrieval misses indicate the search budget may be too low',
141
- changes: [{ path: 'retrieval.k', value: 10 }],
142
- },
143
- ],
144
- }),
145
- ],
146
- baselineSurface,
147
- holdoutScenarios,
148
- dispatchWithSurface,
149
- judges,
150
- runDir,
151
- })
129
+ Each object contains `totalCostUsd`, `accountingComplete`, and `incompleteReasons`.
130
+ An unknown provider charge therefore cannot appear as a trustworthy zero-dollar total.
131
+ Cost breaks a lift tie only when every method in that tied group reports complete accounting.
132
+
133
+ ## Read The Result
134
+
135
+ ```ts
136
+ for (const method of comparison.scores) {
137
+ console.log({
138
+ rank: method.rank,
139
+ name: method.name,
140
+ lift: method.lift,
141
+ interval: method.liftCi,
142
+ scenarios: method.scenarioScores,
143
+ optimizationCostUsd: method.optimizationCost.totalCostUsd,
144
+ costComplete: method.optimizationCost.accountingComplete,
145
+ })
146
+ }
152
147
  ```
153
148
 
154
- ## Common Mistakes
149
+ `rank` orders methods by estimated lift, then by cost only when every method with that lift has complete cost accounting.
150
+ It does not mean the higher-ranked method is conclusively better.
151
+ Read `liftCi` and `comparison.pairwise[].favored` before making that claim.
152
+ `scenarioScores` contains the paired values used to compute each method's result.
153
+
154
+ Repetitions are averaged within each test scenario before scenarios are resampled.
155
+ The intervals assume scenarios are the independent sampling units.
156
+
157
+ `confidence: 0.95` applies to the complete family of method-vs-baseline and possible method-vs-method contrasts.
158
+ The implementation adjusts each interval for that family and raises the default resample count when more methods require finer interval tails.
159
+ An explicit resample count that is too small is rejected before optimization starts.
160
+
161
+ The final test data is spent when this function ranks methods.
162
+ If you choose a method from this result and later claim its deployed effect, confirm that claim on new data that was not used for this ranking.
163
+
164
+ ## FAPO
165
+
166
+ `fapoProposer` can move from prompt edits to declared parameter edits and then to an injected structural proposer.
167
+ Every level must accept and return the same surface representation.
155
168
 
156
- - Do not put eval logic inside a proposer. Put it in `dispatch` and `judges`.
157
- - Do not let a proposer read held-out judge scores. `ProposeContext` makes this
158
- a type-level firewall.
159
- - Do not call FAPO a prompt-only optimizer. Its main value is evidence-based
160
- escalation beyond prompt edits.
161
- - Do not put Claude Code or sandbox-specific code in `agent-eval`. Structural
162
- code generation should be supplied as an injected `SurfaceProposer` from the
163
- runtime/app layer.
169
+ `agent-eval` does not generate repository code itself.
170
+ Pass a code-capable `structuralProposer` from your runtime or application when structural edits are part of the comparison.
164
171
 
165
- ## Simpler Mental Model
172
+ Use `fapoEscalationMethod(config)` to compare the complete FAPO procedure with other methods.
166
173
 
167
- Use this sentence when wiring a loop:
174
+ ## Common Errors
168
175
 
169
- > The proposer chooses candidates; the campaign measures them; the gate decides
170
- > whether the measured winner is safe to promote.
176
+ - Do not pass a raw `SurfaceProposer` to `compareOptimizationMethods`.
177
+ - Do not let a custom `OptimizationMethod` load final test rows from another source.
178
+ - Do not compare methods with different runners, judges, or final test scenarios.
179
+ - Do not read `method.optimizationCost` as total comparison cost.
180
+ - Do not report a dollar total as complete when `accountingComplete` is false.
181
+ - Do not reuse the final test set for repeated method selection and continue calling it untouched.
package/docs/concepts.md CHANGED
@@ -1,38 +1,36 @@
1
1
  # Concepts
2
2
 
3
- `agent-eval` is for deciding whether an agent run should pass, keep working, be
4
- replayed, be optimized, or be promoted.
3
+ `agent-eval` records agent runs, scores their outputs, compares variants, and applies caller-defined release rules.
5
4
 
6
- It exists because agent output is not evidence. A model can say a task is done
7
- while the build fails, the browser flow is broken, the integration was never
8
- connected, or the answer lacks required sources. The package gives products a
9
- shared way to record runs, check outcomes, classify failures, compare variants,
10
- and make release decisions.
5
+ A model can say a task is complete while the build fails, a browser flow is broken, an integration is disconnected, or required sources are missing.
6
+ This package lets code, model judges, and human feedback check those outcomes through the same run format.
11
7
 
12
8
  ## The top-level functions
13
9
 
14
- Everything funnels through `/contract`. Start with `defineAgentEval()` when you
15
- can; drop to the raw functions when you need lower-level control.
10
+ Start with `/contract` and `defineAgentEval()` for a new integration.
11
+ Use the lower-level functions when you need direct control over execution, storage, or statistics.
16
12
 
17
13
  | Function | When to call it | What you give it | What you get back |
18
14
  |---|---|---|---|
19
15
  | **`defineAgentEval()`** | You have scenarios, an agent, a judge, and a baseline surface, and you want one object you can score or improve. | scenarios, agent, judge, baseline surface | `{ evaluate(), improve() }` where `evaluate()` returns a campaign result and `improve()` returns a report |
20
- | **`selfImprove()`** | You have a closed loop: scenarios, judge, agent in hand, and you want the substrate to propose better candidates + gate them. | scenarios, agent, judge, baseline surface | `SelfImproveResult.insight: InsightReport` + ship/hold verdict + winner surface |
16
+ | **`selfImprove()`** | You want candidate generation, scoring, and a release decision in one call. | scenarios, agent, judge, baseline surface | report, ship/hold decision, winner surface |
21
17
  | **`loadEvalFixtureScenarios()`** | You want agents to add evals as folders with `PROMPT.md`, checks, and starter files. | `evals/<name>/PROMPT.md + EVAL.ts + package.json` | `Scenario[]` that runs through `runCampaign`; pair with `planEvalFixtureRun()` before spending tokens |
22
- | **`analyzeRuns()`** | You have observed runs (production traces, an approve/reject corpus, a CSV gold set) and want the same rigor packet without invoking an agent. | `RunRecord[]` + optional flags | `InsightReport` |
18
+ | **`analyzeRuns()`** | You have existing runs and do not need to invoke an agent. | `RunRecord[]` and options | `InsightReport` |
23
19
  | **Intake adapters** (`fromFeedbackTable`, `fromOtelSpans`) | Your data isn't already in `RunRecord` shape: it's in Obsidian, Sheets, an OTel collector, etc. | source-specific input | `RunRecord[]` ready to pipe into `analyzeRuns()` |
24
20
 
25
- The customer maturity stages: logs only ratings closed loop: map to these
26
- entry points. See [`customer-journeys.md`](./customer-journeys.md) for the
27
- runnable walkthroughs.
21
+ See [`customer-journeys.md`](./customer-journeys.md) for runnable paths from existing logs, human ratings, and a callable agent.
28
22
 
29
- The shape of the answer: `InsightReport`: is identical across all three paths. Distributional summary, paired-bootstrap lift CI, judge stats, inter-rater agreement, cost-quality Pareto, failure clusters, contamination check, outcome correlation, release axes, and a ranked recommendations array. Walked through section-by-section in [`insight-report.md`](./insight-report.md).
23
+ `analyzeRuns()` and the high-level contract return the same `InsightReport` shape.
24
+ It contains score distributions, paired lift intervals, judge agreement, cost, failure clusters, contamination checks, outcome correlation, and recommendations.
25
+ [`insight-report.md`](./insight-report.md) defines every field.
30
26
 
31
- ## The layering rule
27
+ ## Package Boundary
32
28
 
33
- `agent-eval` is the **substrate** at the bottom of the Tangle agent stack. `agent-runtime` and `agent-knowledge` depend on it; `agent-eval` MUST NOT import from either. Primitives that "feel like" they belong in a consumer but are actually substrate-shaped (validator verdicts, run records, scenarios, judge scores) live here. Primitives that genuinely require a running agent loop (`ValidationCtx` with iteration + signal + traceEmitter, sandbox `AgentRunSpec`) stay in `agent-runtime`.
29
+ `agent-runtime` and `agent-knowledge` may import `agent-eval`.
30
+ `agent-eval` must not import either package.
34
31
 
35
- The test: *does this concept make sense WITHOUT a running agent loop?* If yes, it's substrate. If no, it's runtime. The full rule is in [`/CLAUDE.md`](../CLAUDE.md#repo-layering--this-package-is-the-substrate).
32
+ Run records, scenarios, judge scores, statistics, and release decisions belong here because they work without an agent runtime.
33
+ Agent sessions, worker coordination, sandbox execution, and runtime-specific profiles belong in `agent-runtime`.
36
34
 
37
35
  ## Main Objects
38
36
 
@@ -43,8 +41,7 @@ The test: *does this concept make sense WITHOUT a running agent loop?* If yes, i
43
41
  | **Verifier** | A pipeline of judges run in order, with dependencies. | "install → typecheck → build → semantic" |
44
42
  | **Feedback trajectory** | A multi-shot record of attempts, approvals, rejections, edits, metrics, and policy outcomes. | "draft → user rejects → revised draft → approved → measured" |
45
43
 
46
- Everything else exists to make those objects useful in real product loops:
47
- traces, datasets, control runtime, optimizers, statistics, and reports.
44
+ Traces, datasets, optimization, statistics, and reports build on these objects.
48
45
 
49
46
  When the thing being evaluated is an agent that should keep working, use
50
47
  [`runAgentControlLoop`](./control-runtime.md). It turns validators into a
@@ -52,30 +49,28 @@ runtime loop: observe typed state, validate it, decide the next action, act,
52
49
  and repeat until the task passes, blocks, times out, spends too much, or stops
53
50
  making progress.
54
51
 
55
- When normal agent usage should become reusable training/eval signal, use
52
+ When normal agent usage should become reusable training or eval data, use
56
53
  [`FeedbackTrajectory`](./feedback-trajectories.md). It captures approvals,
57
54
  rejections, edits, option choices, metrics, and policy blocks as portable data
58
55
  that can seed memory, replay scenarios, and optimization.
59
56
 
60
- ## Vocabulary, plain English
57
+ ## Terms
61
58
 
62
59
  | Term | Plain English |
63
60
  |---|---|
64
61
  | **Artifact** | The thing being judged. Often a workdir of files, sometimes a string of text. |
65
- | **Snapshot** | A frozen view of an artifact (every file path → content). What the judge actually reads. |
62
+ | **Snapshot** | A frozen view of an artifact (every file path → content). This is the input the judge reads. |
66
63
  | **Harness** | A description of *how to run* the artifact: setup command, test command, working dir, timeout. |
67
- | **Sandbox driver** | The thing that actually executes commands inside the harness. Local subprocess, or remote container. |
64
+ | **Sandbox driver** | Executes commands inside the harness, using a local subprocess or remote container. |
68
65
  | **Layer** | One stage of a verifier pipeline (install, typecheck, build, semantic, …). |
69
66
  | **Finding** | A specific issue a judge found: file, line, severity, message. |
70
67
  | **Trace store** | The append-only log of every span/event during a run. Replay = read this back. |
71
68
  | **Composite score** | A 0..1 number combining all dimensions. The single number you gate on. |
72
69
  | **Rubric version** | A stable hash of the rubric. Scores from different rubric versions are not comparable. |
73
- | **Muffled gate** | A check that should fail loud but silently passes (e.g. `command || true`). The most expensive bug class in this codebase. |
74
70
 
75
71
  ## The feedback trajectory loop
76
72
 
77
- For agentic systems, the highest-quality labels often come from normal review
78
- workflow, not a separate labeling UI:
73
+ Normal review activity can provide labels without a separate labeling interface:
79
74
 
80
75
  ```text
81
76
  agent proposes -> user approves/rejects/edits/selects -> agent revises -> outcome is measured
@@ -124,7 +119,8 @@ A rubric describes:
124
119
  4. **Wins**: named positive patterns ("specific-component", "earned-detail").
125
120
  5. **System prompt**: what to tell the judging LLM about the persona and the task.
126
121
 
127
- Built-in rubrics ship in `src/wire/rubrics.ts` (e.g. `anti-slop` for technical-buyer voice). You can also pass a rubric inline: the same shape, just defined at the call site.
122
+ Built-in rubrics ship in `src/wire/rubrics.ts`, including `anti-slop` for technical-buyer voice.
123
+ You can also pass the same rubric shape inline at the call site.
128
124
 
129
125
  A rubric is plain data. The hash of that data is the `rubricVersion`. Two scores are only comparable if they used the same `rubricVersion`: change the rubric and you start a new comparison series.
130
126
 
@@ -187,12 +183,12 @@ release decision.
187
183
 
188
184
  ## Where to go next
189
185
 
190
- - **Confused by "GEPA / HALO / trace analysis / proposers everywhere"?** [self-improvement-map.md](./self-improvement-map.md): one loop, four roles, the proposer catalog (production vs bench-only), and why `gepa-refine` is the same loop on a test bench.
191
- - **Which `run*` primitive do I use, and how do I grade produced state?** [eval-surface-map.md](./eval-surface-map.md): the campaign/matrix/optimization/gate primitives as a pick-by-"use-when" table, plus the produced-state grading composition (verifyCompletion-as-judge: there is no persona-dispatch wrapper) and the in-band body contract.
192
- - **Need the layman feature map?** [feature-guide.md](./feature-guide.md): what each primitive does, when to use it, integration patterns, and guardrails.
193
- - **Just want to score a string against a rubric?** [wire-protocol.md](./wire-protocol.md): HTTP/RPC interface, pluggable from any language.
194
- - **Need a reusable driver/worker/evaluator loop?** [control-runtime.md](./control-runtime.md): generic runtime plus coding, browser, computer-use, and research integration patterns.
195
- - **Want review feedback to become eval/optimization data?** [feedback-trajectories.md](./feedback-trajectories.md): turn feedback into datasets, optimizer rows, and preference memory.
186
+ - **Choosing a candidate-generation method?** Read [campaign-proposers.md](./campaign-proposers.md) for the available methods, their inputs, and runnable composition examples.
187
+ - **Choosing a `run*` function or grading produced state?** Read [eval-surface-map.md](./eval-surface-map.md) for a use-case table and complete grading composition.
188
+ - **Need the feature map?** Read [feature-guide.md](./feature-guide.md) for integration patterns and operational limits.
189
+ - **Scoring a string from another language?** Read [wire-protocol.md](./wire-protocol.md) for the HTTP/RPC interface.
190
+ - **Building a driver and worker loop?** Read [control-runtime.md](./control-runtime.md) for coding, browser, computer-use, and research patterns.
191
+ - **Turning review feedback into reusable data?** Read [feedback-trajectories.md](./feedback-trajectories.md) for dataset, optimization, and preference-memory examples.
196
192
  - **Building a code-generator eval?** → Start with `BuilderSession`, `SandboxHarness`, and `MultiLayerVerifier`.
197
193
  - **Multi-layer verifier?** → Use [control-runtime.md](./control-runtime.md) and `MultiLayerVerifier` for ordered gates with dependencies.
198
194
  - **Adding a new judge or rubric?** → `src/wire/rubrics.ts` for the cross-language path; `src/anti-slop.ts` and `src/judges.ts` for the in-process path.
@@ -6,9 +6,8 @@ layers, and the layers were getting conflated. Every role below has exactly
6
6
  one meaning. Use these words and nothing else.
7
7
 
8
8
  Cross-links: [`concepts.md`](../concepts.md) (eval mental model),
9
- [`campaign-proposers.md`](../campaign-proposers.md) (proposer catalog),
10
- [`multi-shot-optimization.md`](../multi-shot-optimization.md) (GEPA), and
11
- [`self-improvement-map.md`](../self-improvement-map.md) (supported improvement paths).
9
+ [`campaign-proposers.md`](../campaign-proposers.md) (proposer catalog), and
10
+ [`multi-shot-optimization.md`](../multi-shot-optimization.md) (GEPA).
12
11
 
13
12
  ## Core Roles
14
13
 
package/docs/design.md CHANGED
@@ -64,7 +64,6 @@ These files record operating conventions for maintainers and internal agents.
64
64
  They are not adoption reference:
65
65
 
66
66
  - [`building-doctrine.md`](./building-doctrine.md): conventions our agents follow when consuming this package (reachable model defaults, probe-before-debug, experiment integrity checklist)
67
- - [`self-improvement-map.md`](./self-improvement-map.md): how the one improvement loop is pointed at different surfaces, and the proposer bake-off narrative
68
67
  - [`design/loop-taxonomy.md`](./design/loop-taxonomy.md): the internal vocabulary for execution drivers, workers, measurements, and proposers
69
68
  - [`research-report-methodology.md`](./research-report-methodology.md): the evidence standard our own research reports are held to
70
69
  - [`.claude/skills/agent-eval/SKILL.md`](../.claude/skills/agent-eval/SKILL.md): directives for LLM agents writing integration code, encoding bug classes we have already shipped and fixed once