@tangle-network/agent-eval 0.50.1 → 0.51.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,45 @@ All notable changes to `@tangle-network/agent-eval` and its sibling `agent-eval-
4
4
 
5
5
  ---
6
6
 
7
+ ## [0.51.0] — 2026-05-27 — skillOptDriver (SkillOpt methodology as a substrate driver)
8
+
9
+ ### Added
10
+
11
+ - **`skillOptDriver`** in `/campaign`. A section-aware, bounded-edit `ImprovementDriver` for structured natural-language procedures (SKILL.md files, runbooks, sectioned system prompts, judge rubrics with dimensions). Implements the SkillOpt methodology (Microsoft, 2026): treat the skill document as a trainable optimization target, train the procedure not the weights, constrain each generation to ≤N targeted edits to prevent useful-rule overwrites.
12
+ - **Edit-budget enforcement** — candidates that exceed `editBudget * 2` sentence-level diffs vs baseline are rejected at parse time. SkillOpt's "edit budget functions as a textual learning rate."
13
+ - **Section preservation** — H2 headings (or an explicit `preserveSections` allowlist) MUST appear unchanged in every candidate. Candidates that delete or rename sections are rejected.
14
+ - **Surface-typed** — throws on non-string surfaces; agent-runtime's `improvementDriver` handles code-tier.
15
+ - `extractH2Sections(text)` + `countSentenceEdits(a, b)` exported as named helpers for consumers writing custom drivers with similar invariants.
16
+
17
+ ### Scope (honest)
18
+
19
+ This is **batch SkillOpt** — one LLM call per generation produces all N candidates with the budget enforced as a prompt instruction + post-parse rejection. **Per-edit iteration** (propose 1 edit → validate → accept-or-reject → propose next) is a future 0.52.0 enhancement that needs a new `IncrementalImprovementDriver` interface; the substrate's current batch `ImprovementDriver` can run SkillOpt-style behavior with `populationSize=1` + `maxGenerations=N`, but a single driver invocation can't iterate per-edit yet. Tracked.
20
+
21
+ ### Notes
22
+
23
+ Selectable alongside `gepaDriver` and `evolutionaryDriver`. Use when the surface IS a structured doc; use `gepaDriver` when the surface is unstructured prose.
24
+
25
+ ---
26
+
27
+ ## [0.50.2] — 2026-05-27 — actionability fixes from real-data dogfood
28
+
29
+ ### Added
30
+
31
+ - **`ScalarDistribution.tailRuns?: Array<{runId, score}>`** — populated for the composite distribution. The report now names the 5 worst runs a customer should inspect first, instead of telling them to "investigate the lower tail" anonymously.
32
+ - **`InsightReport.costQuality.degraded?: {cost?, pareto?}`** — explicit per-axis degradation reasons when `costUsd` is all zero (cost axis carries no signal) or only a single candidate appears (Pareto collapses to a single point). Replaces the prior silent emission of meaningless single-point Pareto figures.
33
+ - **Composite-distribution recommendations.** When `composite.mean < 0.3`, the report emits a `critical/investigate` recommendation with the worst-5 runIds enumerated in the detail. Between 0.3 and 0.5, a `high/investigate` recommendation with the worst-3. Closes the gap where `recommendations: []` was being emitted for completely broken corpora.
34
+ - **Missing-judges flag.** When `judges` is empty across the corpus, the report emits a `medium/expand-corpus` recommendation pointing at `outcome.judgeScores.perJudge` enrichment. Before, the customer had no signal that per-dimension / calibration was unavailable because of input shape, not substrate failure.
35
+
36
+ ### Fixed
37
+
38
+ - `analyzeRuns()` on the legal-agent canonical run (n=36, mean composite = 0.002) now emits actionable recommendations naming specific failing scenarios; previously it returned `recommendations: []` for a fully-broken agent.
39
+
40
+ ### Notes
41
+
42
+ The four behavior changes are additive — fields are optional, no existing field shape changed. Dogfood-driven: surfaced by running `analyzeRuns()` against three real consumer datasets (legal-agent, agent-builder, gtm-agent golden run) and observing where the report was silent when it should have been loud.
43
+
44
+ ---
45
+
7
46
  ## [0.50.1] — 2026-05-27 — docs + examples
8
47
 
9
48
  ### Added
@@ -1,4 +1,4 @@
1
- import { T as TraceSpanEvent, H as HostedClient } from '../index-BRxz6qov.js';
1
+ import { T as TraceSpanEvent, H as HostedClient } from '../index-DQHtWQ57.js';
2
2
  import '../types-Dbj5gu8n.js';
3
3
  import '../summary-report-B7gNRX-r.js';
4
4
  import '../run-record-BGY6bHRh.js';
@@ -1,13 +1,72 @@
1
1
  export { C as CampaignStorage, D as DefaultProductionGateOptions, E as EvolutionaryDriverOptions, G as GepaDriverOptions, H as HeldOutGateOptions, O as OpenAutoPrOptions, a as OpenAutoPrResult, R as RunCampaignOptions, b as RunEvalOptions, c as RunImprovementLoopOptions, d as RunImprovementLoopResult, e as RunOptimizationOptions, f as RunOptimizationResult, g as composeGate, h as defaultProductionGate, i as evolutionaryDriver, j as fsCampaignStorage, k as gepaDriver, l as heldOutGate, m as inMemoryCampaignStorage, o as openAutoPr, r as runCampaign, n as runEval, p as runImprovementLoop, q as runOptimization, s as surfaceHash } from '../run-improvement-loop-BPMjNKMJ.js';
2
- import { L as LabeledScenarioStore, c as LabeledScenarioWrite, d as LabeledScenarioSampleArgs, e as LabeledScenarioRecord, C as CodeSurface } from '../types-Dbj5gu8n.js';
3
- export { f as CampaignAggregates, g as CampaignArtifactWriter, h as CampaignCellResult, i as CampaignCostMeter, j as CampaignResult, k as CampaignTraceWriter, b as DispatchContext, D as DispatchFn, G as Gate, l as GateContext, m as GateDecision, n as GateResult, o as GenerationCandidate, p as GenerationRecord, I as ImprovementDriver, q as JudgeAggregate, a as JudgeConfig, r as JudgeDimension, J as JudgeScore, s as LabeledScenarioSource, M as MutableSurface, t as Mutator, O as OptimizerConfig, P as ProposeContext, R as RedactionStatus, S as Scenario, u as ScenarioAggregate, v as SessionScript, T as TraceSpan } from '../types-Dbj5gu8n.js';
4
- import '../llm-client-BXVRUZyX.js';
5
- import '../errors-mje_cKOs.js';
6
- import '../raw-provider-sink-C46HDghv.js';
2
+ import { L as LlmClientOptions } from '../llm-client-BXVRUZyX.js';
3
+ import { I as ImprovementDriver, L as LabeledScenarioStore, c as LabeledScenarioWrite, d as LabeledScenarioSampleArgs, e as LabeledScenarioRecord, C as CodeSurface } from '../types-Dbj5gu8n.js';
4
+ export { f as CampaignAggregates, g as CampaignArtifactWriter, h as CampaignCellResult, i as CampaignCostMeter, j as CampaignResult, k as CampaignTraceWriter, b as DispatchContext, D as DispatchFn, G as Gate, l as GateContext, m as GateDecision, n as GateResult, o as GenerationCandidate, p as GenerationRecord, q as JudgeAggregate, a as JudgeConfig, r as JudgeDimension, J as JudgeScore, s as LabeledScenarioSource, M as MutableSurface, t as Mutator, O as OptimizerConfig, P as ProposeContext, R as RedactionStatus, S as Scenario, u as ScenarioAggregate, v as SessionScript, T as TraceSpan } from '../types-Dbj5gu8n.js';
7
5
  import '../red-team-30II1T4o.js';
8
6
  import '../dataset-BlwAtYYf.js';
7
+ import '../errors-mje_cKOs.js';
9
8
  import '../store-Db2Bv8Cf.js';
10
9
  import '../run-record-BGY6bHRh.js';
10
+ import '../raw-provider-sink-C46HDghv.js';
11
+
12
+ /**
13
+ * @experimental
14
+ *
15
+ * `skillOptDriver` — a section-aware, bounded-edit `ImprovementDriver` for
16
+ * structured natural-language procedures (SKILL.md files, runbooks, sectioned
17
+ * system prompts, judge rubrics with dimensions). Implements the SkillOpt
18
+ * methodology (Microsoft, 2026): treat the skill document as a trainable
19
+ * optimization target, train the procedure not the weights, constrain each
20
+ * generation to ≤N targeted edits to prevent useful-rule overwrites.
21
+ *
22
+ * Differs from `gepaDriver` in two specific ways:
23
+ *
24
+ * 1. **Bounded edits.** Each candidate must differ from the baseline by at
25
+ * most `editBudget` sentence-level changes. The "edit budget functions
26
+ * as a textual learning rate" — without it, an LLM proposal can rewrite
27
+ * so much that useful prior rules get overwritten.
28
+ *
29
+ * 2. **Section preservation.** When the surface is a structured doc, the
30
+ * H2 headers (and an opt-in `preserveSections` allowlist) are
31
+ * load-bearing for discoverability. Candidates that delete or rename
32
+ * preserved sections are rejected at parse time.
33
+ *
34
+ * Selectable alongside `gepaDriver` and `evolutionaryDriver`. Use this when
35
+ * the surface IS a structured doc; use `gepaDriver` when the surface is
36
+ * unstructured prose.
37
+ */
38
+
39
+ interface SkillOptDriverOptions {
40
+ /** Router transport (apiKey/baseUrl). */
41
+ llm: LlmClientOptions;
42
+ /** Model that performs the reflection. */
43
+ model: string;
44
+ /** What is being optimized — appears in the reflection prompt for orientation. */
45
+ target: string;
46
+ /** Max edits per generation — SkillOpt's "textual learning rate".
47
+ * Default 3. Lower = more conservative, higher = more exploratory. */
48
+ editBudget?: number;
49
+ /** Section headings the driver MUST preserve. When the surface is a
50
+ * structured skill doc, sections are load-bearing for discoverability.
51
+ * Default: auto-detected from H2 headers in the baseline. */
52
+ preserveSections?: string[];
53
+ /** Surface-specific mutation levers offered to the model. */
54
+ mutationPrimitives?: string[];
55
+ /** Top/bottom scenarios surfaced as evidence each generation. Default 3. */
56
+ evidenceK?: number;
57
+ /** Reflection sampling temperature. Default 0.7. */
58
+ temperature?: number;
59
+ /** Reflection max tokens. Default 6000. */
60
+ maxTokens?: number;
61
+ }
62
+ /** Internal — exported for tests. */
63
+ declare function extractH2Sections(text: string): string[];
64
+ /** Sentence-level edit distance. Counts distinct sentence add/remove/replace
65
+ * ops between baseline and candidate using a normalised line-by-line diff.
66
+ * Imperfect (treats trivial whitespace as identical) but tight enough to
67
+ * bound an LLM rewrite. Exported for tests. */
68
+ declare function countSentenceEdits(baseline: string, candidate: string): number;
69
+ declare function skillOptDriver(opts: SkillOptDriverOptions): ImprovementDriver;
11
70
 
12
71
  /**
13
72
  * @experimental
@@ -124,4 +183,4 @@ declare function gitWorktreeAdapter(opts: GitWorktreeAdapterOptions): WorktreeAd
124
183
  * as a ref under the adapter's worktree dir. */
125
184
  declare function resolveWorktreePath(surface: CodeSurface, worktreeDir?: string): string;
126
185
 
127
- export { CodeSurface, FsLabeledScenarioStore, type FsLabeledScenarioStoreOptions, type GitWorktreeAdapterOptions, LabeledScenarioRecord, LabeledScenarioSampleArgs, LabeledScenarioStore, LabeledScenarioStoreError, LabeledScenarioWrite, type Worktree, type WorktreeAdapter, WorktreeAdapterError, gitWorktreeAdapter, resolveWorktreePath };
186
+ export { CodeSurface, FsLabeledScenarioStore, type FsLabeledScenarioStoreOptions, type GitWorktreeAdapterOptions, ImprovementDriver, LabeledScenarioRecord, LabeledScenarioSampleArgs, LabeledScenarioStore, LabeledScenarioStoreError, LabeledScenarioWrite, type SkillOptDriverOptions, type Worktree, type WorktreeAdapter, WorktreeAdapterError, countSentenceEdits, extractH2Sections, gitWorktreeAdapter, resolveWorktreePath, skillOptDriver };
@@ -15,15 +15,133 @@ import {
15
15
  inMemoryCampaignStorage,
16
16
  runCampaign
17
17
  } from "../chunk-J3EIOI3O.js";
18
- import "../chunk-N4SBKEPJ.js";
18
+ import {
19
+ buildReflectionPrompt,
20
+ parseReflectionResponse
21
+ } from "../chunk-N4SBKEPJ.js";
19
22
  import "../chunk-YV7J7X5N.js";
20
23
  import "../chunk-WP7SY7AI.js";
21
24
  import "../chunk-GGE4NNQT.js";
22
- import "../chunk-VXNVVBZO.js";
25
+ import {
26
+ callLlm
27
+ } from "../chunk-VXNVVBZO.js";
23
28
  import "../chunk-PC4UYEBM.js";
24
29
  import "../chunk-QYJT52YW.js";
25
30
  import "../chunk-NSBPE2FW.js";
26
31
 
32
+ // src/campaign/drivers/skillopt.ts
33
+ var REFLECTION_SYSTEM = 'You are an expert prompt engineer applying the SkillOpt methodology. You will edit a structured natural-language procedure under TWO HARD CONSTRAINTS: (1) preserve every H2 section heading verbatim \u2014 do NOT delete, rename, or merge sections; (2) make at most EDIT_BUDGET targeted sentence-level edits per candidate \u2014 bounded edits prevent overwriting useful prior rules. Output ONLY a JSON object of shape {"proposals":[{"label":string,"rationale":string,"payload":string}]} where each `payload` is the FULL improved skill text. No prose outside the JSON.';
34
+ function extractH2Sections(text) {
35
+ const out = [];
36
+ for (const line of text.split("\n")) {
37
+ const match = /^##\s+(.+?)\s*$/.exec(line);
38
+ if (match) out.push(match[1]);
39
+ }
40
+ return out;
41
+ }
42
+ function countSentenceEdits(baseline, candidate) {
43
+ const norm = (s) => s.split(/(?<=[.!?])\s+|\n/g).map((p) => p.trim()).filter((p) => p.length > 0);
44
+ const a = new Set(norm(baseline));
45
+ const b = new Set(norm(candidate));
46
+ let edits = 0;
47
+ for (const s of a) if (!b.has(s)) edits++;
48
+ for (const s of b) if (!a.has(s)) edits++;
49
+ return edits;
50
+ }
51
+ function skillOptDriver(opts) {
52
+ const evidenceK = opts.evidenceK ?? 3;
53
+ const editBudget = opts.editBudget ?? 3;
54
+ if (editBudget < 1) {
55
+ throw new Error(
56
+ `skillOptDriver: editBudget must be >= 1, got ${editBudget} (use evolutionaryDriver with a noop mutator for measure-only runs)`
57
+ );
58
+ }
59
+ return {
60
+ kind: "skillopt",
61
+ async propose(ctx) {
62
+ if (typeof ctx.currentSurface !== "string") {
63
+ throw new Error(
64
+ `skillOptDriver: surface must be a string skill document; got ${typeof ctx.currentSurface}. Use evolutionaryDriver with a CodeSurface mutator for code-tier surfaces.`
65
+ );
66
+ }
67
+ const baseline = ctx.currentSurface;
68
+ const preserveSections = opts.preserveSections ?? extractH2Sections(baseline);
69
+ const { top, bottom, target } = buildEvidence(ctx, evidenceK, opts.target);
70
+ const reflectionUser = buildReflectionPrompt({
71
+ target,
72
+ parentPayload: baseline,
73
+ topTrials: top,
74
+ bottomTrials: bottom,
75
+ childCount: ctx.populationSize,
76
+ mutationPrimitives: opts.mutationPrimitives
77
+ });
78
+ const constraintPreamble = [
79
+ "",
80
+ "## SkillOpt constraints (hard rules \u2014 violations rejected)",
81
+ "",
82
+ `- Edit budget: at most ${editBudget} sentence-level edits per candidate.`,
83
+ "- Section preservation: every H2 heading below must appear unchanged in your output.",
84
+ ...preserveSections.map((s) => ` - \`## ${s}\``),
85
+ "",
86
+ "Reject any candidate in your own thinking that would delete a section, rename a heading, or exceed the edit budget. Make TARGETED, surgical edits \u2014 not rewrites.",
87
+ ""
88
+ ].join("\n");
89
+ const userPrompt = `${reflectionUser}${constraintPreamble}`;
90
+ const system = REFLECTION_SYSTEM.replace("EDIT_BUDGET", String(editBudget));
91
+ const result = await callLlm(
92
+ {
93
+ model: opts.model,
94
+ messages: [
95
+ { role: "system", content: system },
96
+ { role: "user", content: userPrompt }
97
+ ],
98
+ jsonMode: true,
99
+ temperature: opts.temperature ?? 0.7,
100
+ maxTokens: opts.maxTokens ?? 6e3
101
+ },
102
+ opts.llm
103
+ );
104
+ const proposals = parseReflectionResponse(result.content, ctx.populationSize);
105
+ const out = [];
106
+ for (const proposal of proposals) {
107
+ const text = typeof proposal.payload === "string" ? proposal.payload.trim() : "";
108
+ if (!text || text === baseline) continue;
109
+ if (!validateSections(text, preserveSections)) continue;
110
+ if (countSentenceEdits(baseline, text) > editBudget * 2) continue;
111
+ if (out.includes(text)) continue;
112
+ out.push(text);
113
+ }
114
+ return out;
115
+ }
116
+ };
117
+ }
118
+ function validateSections(candidate, required) {
119
+ if (required.length === 0) return true;
120
+ const have = new Set(extractH2Sections(candidate));
121
+ for (const section of required) {
122
+ if (!have.has(section)) return false;
123
+ }
124
+ return true;
125
+ }
126
+ function buildEvidence(ctx, evidenceK, baseTarget) {
127
+ const last = ctx.history.at(-1);
128
+ if (!last || last.candidates.length === 0) {
129
+ return { top: [], bottom: [], target: baseTarget };
130
+ }
131
+ const best = [...last.candidates].sort((a, b) => b.composite - a.composite)[0];
132
+ if (!best) return { top: [], bottom: [], target: baseTarget };
133
+ const byScore = [...best.scenarios].sort((a, b) => b.composite - a.composite);
134
+ const toTrace = (s) => ({
135
+ id: s.scenarioId,
136
+ score: s.composite
137
+ });
138
+ const top = byScore.slice(0, evidenceK).map(toTrace);
139
+ const bottom = byScore.slice(-evidenceK).reverse().map(toTrace);
140
+ const weakest = Object.entries(best.dimensions).sort((a, b) => a[1] - b[1]).slice(0, 3).map(([dim, value]) => `${dim} (${value.toFixed(2)})`);
141
+ const target = weakest.length > 0 ? `${baseTarget} \u2014 weakest dimensions: ${weakest.join(", ")}` : baseTarget;
142
+ return { top, bottom, target };
143
+ }
144
+
27
145
  // src/campaign/labeled-store/fs-adapter.ts
28
146
  import { createHash } from "crypto";
29
147
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
@@ -283,8 +401,10 @@ export {
283
401
  LabeledScenarioStoreError,
284
402
  WorktreeAdapterError,
285
403
  composeGate,
404
+ countSentenceEdits,
286
405
  defaultProductionGate,
287
406
  evolutionaryDriver,
407
+ extractH2Sections,
288
408
  fsCampaignStorage,
289
409
  gepaDriver,
290
410
  gitWorktreeAdapter,
@@ -296,6 +416,7 @@ export {
296
416
  runEval,
297
417
  runImprovementLoop,
298
418
  runOptimization,
419
+ skillOptDriver,
299
420
  surfaceHash
300
421
  };
301
422
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/campaign/labeled-store/fs-adapter.ts","../../src/campaign/worktree/index.ts"],"sourcesContent":["/**\n * @experimental\n *\n * Filesystem `LabeledScenarioStore` adapter. The default capture sink for\n * traces + eval artifacts. Production deployments typically swap for a\n * Turso/SQLite adapter (same interface).\n *\n * Records land as one JSONL file per source under `<root>/<source>.jsonl`.\n * Each line is a `LabeledScenarioRecord`. Append-only — no in-place edits.\n *\n * Safety properties enforced at write-time:\n *\n * - **Provenance required**: writes without `source`, `sourceVersionHash`,\n * `capturedAt`, `redactionStatus` are rejected. Closes the alignment\n * reviewer's data-poisoning gap.\n * - **Per-source rate limits**: optional `rateLimitBucket` + `maxWritesPerMinute`\n * stops a single tenant/source from flooding the store.\n *\n * Safety properties enforced at sample-time:\n *\n * - **Required split + capturedBefore**: substrate refuses to sample without\n * an explicit `split` ('train' | 'test') AND a temporal cutoff. Eliminates\n * accidental train/test contamination.\n * - **Default training-source filter**: when the store is sampled with\n * `split: 'train'`, production-trace records are EXCLUDED unless the\n * caller passes `filter.source: 'production-trace'` explicitly. Closes\n * the contamination-by-default gap flagged by the senior eval engineer.\n */\n\nimport { createHash } from 'node:crypto'\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport type {\n LabeledScenarioRecord,\n LabeledScenarioSampleArgs,\n LabeledScenarioSource,\n LabeledScenarioStore,\n LabeledScenarioWrite,\n} from '../types'\n\nexport interface FsLabeledScenarioStoreOptions {\n /** Root directory for JSONL files. Created if missing. */\n root: string\n /** Per-source rate limit. When set, writes exceeding the cap are rejected\n * with a typed error. Default: no limit. */\n maxWritesPerMinutePerBucket?: number\n /** Test seam — override `Date.now()` for deterministic tests. */\n now?: () => number\n}\n\nexport class LabeledScenarioStoreError extends Error {\n constructor(\n public readonly code: string,\n message: string,\n ) {\n super(message)\n this.name = 'LabeledScenarioStoreError'\n }\n}\n\ninterface RateLimitState {\n bucket: string\n windowStartMs: number\n count: number\n}\n\nexport class FsLabeledScenarioStore implements LabeledScenarioStore {\n private readonly now: () => number\n private readonly rateLimits = new Map<string, RateLimitState>()\n\n constructor(private readonly options: FsLabeledScenarioStoreOptions) {\n if (!existsSync(options.root)) mkdirSync(options.root, { recursive: true })\n this.now = options.now ?? Date.now\n }\n\n async observe(write: LabeledScenarioWrite): Promise<void> {\n this.assertProvenance(write)\n this.assertRateLimit(write)\n const record = this.toRecord(write)\n const path = this.pathForSource(write.source)\n const line = `${JSON.stringify(record)}\\n`\n // Append atomically. For high-throughput a writev-friendly buffered\n // implementation lands in the Turso adapter; FS adapter is for tests +\n // local dev + small workloads.\n appendLine(path, line)\n }\n\n async sample(args: LabeledScenarioSampleArgs): Promise<LabeledScenarioRecord[]> {\n if (!args.split) {\n throw new LabeledScenarioStoreError(\n 'split_required',\n 'sample() requires an explicit `split` (train | test) — substrate refuses ambiguous reads',\n )\n }\n if (!args.capturedBefore) {\n throw new LabeledScenarioStoreError(\n 'capturedBefore_required',\n 'sample() requires an explicit `capturedBefore` timestamp for temporal-split discipline',\n )\n }\n\n const all: LabeledScenarioRecord[] = []\n for (const source of ALL_SOURCES) {\n // Default training-source filter: when sampling train, EXCLUDE\n // production-trace records unless the caller asks for them.\n if (args.split === 'train' && source === 'production-trace') {\n const explicit = sourceFilterContains(args.filter?.source, 'production-trace')\n if (!explicit) continue\n }\n const path = this.pathForSource(source)\n if (!existsSync(path)) continue\n const lines = readFileSync(path, 'utf8').split('\\n').filter(Boolean)\n for (const line of lines) {\n let record: LabeledScenarioRecord\n try {\n record = JSON.parse(line) as LabeledScenarioRecord\n } catch {\n continue\n }\n if (!matchesFilter(record, args, source)) continue\n all.push(record)\n }\n }\n\n // Deterministic order: by capturedAt ascending, then recordHash.\n all.sort((a, b) => {\n if (a.capturedAt !== b.capturedAt) return a.capturedAt.localeCompare(b.capturedAt)\n return a.recordHash.localeCompare(b.recordHash)\n })\n\n return all.slice(0, args.count)\n }\n\n async size(): Promise<{ train: number; test: number; bySource: Record<string, number> }> {\n const bySource: Record<string, number> = {}\n let total = 0\n for (const source of ALL_SOURCES) {\n const path = this.pathForSource(source)\n if (!existsSync(path)) {\n bySource[source] = 0\n continue\n }\n const count = readFileSync(path, 'utf8').split('\\n').filter(Boolean).length\n bySource[source] = count\n total += count\n }\n // FS adapter doesn't track split assignments per-record (split is\n // computed at sample-time based on `capturedBefore`). For size(), we\n // report `train`+`test` as the same total — split is a sampling concept.\n return { train: total, test: total, bySource }\n }\n\n private assertProvenance(write: LabeledScenarioWrite): void {\n if (!write.source) {\n throw new LabeledScenarioStoreError(\n 'missing_source',\n 'LabeledScenarioWrite requires `source`',\n )\n }\n if (!write.sourceVersionHash || write.sourceVersionHash.length === 0) {\n throw new LabeledScenarioStoreError(\n 'missing_source_version',\n 'LabeledScenarioWrite requires `sourceVersionHash` (git sha or substrate version)',\n )\n }\n if (!write.capturedAt) {\n throw new LabeledScenarioStoreError(\n 'missing_captured_at',\n 'LabeledScenarioWrite requires `capturedAt` ISO timestamp',\n )\n }\n if (!write.redactionStatus) {\n throw new LabeledScenarioStoreError(\n 'missing_redaction_status',\n 'LabeledScenarioWrite requires explicit `redactionStatus` — raw / redacted-pii / redacted-secrets / fully-redacted',\n )\n }\n if (!ALL_SOURCES.includes(write.source)) {\n throw new LabeledScenarioStoreError(\n 'unknown_source',\n `LabeledScenarioWrite.source must be one of: ${ALL_SOURCES.join(', ')}`,\n )\n }\n }\n\n private assertRateLimit(write: LabeledScenarioWrite): void {\n const cap = this.options.maxWritesPerMinutePerBucket\n if (!cap || !write.rateLimitBucket) return\n const now = this.now()\n const windowMs = 60_000\n let state = this.rateLimits.get(write.rateLimitBucket)\n if (!state || now - state.windowStartMs >= windowMs) {\n state = { bucket: write.rateLimitBucket, windowStartMs: now, count: 0 }\n this.rateLimits.set(write.rateLimitBucket, state)\n }\n if (state.count >= cap) {\n throw new LabeledScenarioStoreError(\n 'rate_limit_exceeded',\n `LabeledScenarioStore: bucket ${write.rateLimitBucket} exceeded ${cap} writes/min`,\n )\n }\n state.count += 1\n }\n\n private toRecord(write: LabeledScenarioWrite): LabeledScenarioRecord {\n const recordHash = sha256(\n JSON.stringify({\n id: write.scenario.id,\n src: write.source,\n at: write.capturedAt,\n ver: write.sourceVersionHash,\n }),\n )\n // FS adapter assigns split at sample-time, but we cache a hint here\n // based on capturedAt vs the world's \"now\" — sampler overrides this.\n return {\n ...write,\n recordHash,\n split: 'train',\n }\n }\n\n private pathForSource(source: string): string {\n return join(this.options.root, `${source}.jsonl`)\n }\n}\n\nconst ALL_SOURCES: LabeledScenarioWrite['source'][] = [\n 'production-trace',\n 'eval-run',\n 'manual',\n 'red-team',\n 'synthetic',\n]\n\nfunction sourceFilterContains(\n filter: LabeledScenarioSource | LabeledScenarioSource[] | undefined,\n needle: LabeledScenarioSource,\n): boolean {\n if (!filter) return false\n if (Array.isArray(filter)) return filter.includes(needle)\n return filter === needle\n}\n\nfunction matchesFilter(\n record: LabeledScenarioRecord,\n args: LabeledScenarioSampleArgs,\n source: string,\n): boolean {\n // Temporal cutoff — train must be capturedAt < capturedBefore.\n if (args.split === 'train' && record.capturedAt >= args.capturedBefore) return false\n if (args.split === 'test' && record.capturedAt < args.capturedBefore) return false\n\n const f = args.filter\n if (!f) return true\n if (f.kind && record.scenario.kind !== f.kind) return false\n if (f.source) {\n const sources = Array.isArray(f.source) ? f.source : [f.source]\n if (!sources.includes(source as never)) return false\n }\n if (f.minComposite !== undefined || f.maxComposite !== undefined) {\n const composites = Object.values(record.judgeScores).map((s) => s.composite)\n const max = composites.length === 0 ? 0 : Math.max(...composites)\n if (f.minComposite !== undefined && max < f.minComposite) return false\n if (f.maxComposite !== undefined && max > f.maxComposite) return false\n }\n return true\n}\n\nfunction sha256(input: string): string {\n return createHash('sha256').update(input).digest('hex').slice(0, 16)\n}\n\nfunction appendLine(path: string, line: string): void {\n if (existsSync(path)) {\n const existing = readFileSync(path, 'utf8')\n writeFileSync(path, existing + line)\n } else {\n writeFileSync(path, line)\n }\n}\n","/**\n * @experimental\n *\n * VCS-pluggable worktree adapter. One improvement = one worktree, PR-like\n * (multiple commits allowed). A code-tier driver's `propose()` creates a\n * worktree, an agent commits the change into it, and `finalize()` returns a\n * `CodeSurface{ worktreeRef }` the measurement checks out to run the worker\n * against the changed code. On promotion the worktree becomes the PR branch.\n *\n * The interface is VCS-agnostic so a future `jj` ([jj-vcs](https://github.com/jj-vcs/jj))\n * adapter can slot in without touching driver code. Only the git adapter\n * ships today. See `docs/design/self-improvement-engine.md`.\n */\n\nimport { execFileSync } from 'node:child_process'\nimport { existsSync } from 'node:fs'\nimport { basename, isAbsolute, join } from 'node:path'\nimport type { CodeSurface } from '../types'\n\nexport interface Worktree {\n /** Absolute path to the checked-out worktree directory. */\n path: string\n /** The branch the worktree is on (becomes the PR branch on promotion). */\n branch: string\n /** The ref the worktree was forked from. */\n baseRef: string\n}\n\nexport interface WorktreeAdapter {\n /** Create an isolated worktree on a fresh branch off `baseRef`. */\n create(opts: { baseRef: string; label: string }): Promise<Worktree>\n /** Commit any pending changes in the worktree, then return a CodeSurface\n * pointing at it. The agent has already written its change into\n * `worktree.path` by the time this is called. */\n finalize(worktree: Worktree, summary: string): Promise<CodeSurface>\n /** Remove the worktree (and its branch) — called for losing candidates. */\n discard(worktree: Worktree): Promise<void>\n}\n\nexport class WorktreeAdapterError extends Error {\n constructor(\n message: string,\n readonly cause?: unknown,\n ) {\n super(message)\n this.name = 'WorktreeAdapterError'\n }\n}\n\nexport interface GitWorktreeAdapterOptions {\n /** Repo root the worktrees fork from. */\n repoRoot: string\n /** Directory worktrees are created under. Default: `<repoRoot>/.worktrees`. */\n worktreeDir?: string\n /** Branch-name prefix. Default: `improve`. */\n branchPrefix?: string\n /** Test seam — defaults to a real `git` runner. */\n git?: (args: string[], cwd: string) => string\n}\n\nfunction defaultGit(args: string[], cwd: string): string {\n try {\n return execFileSync('git', args, { cwd, encoding: 'utf8' }).trim()\n } catch (err) {\n const stderr =\n err && typeof err === 'object' && 'stderr' in err\n ? String((err as { stderr: unknown }).stderr)\n : ''\n throw new WorktreeAdapterError(`git ${args.join(' ')} failed: ${stderr || String(err)}`, err)\n }\n}\n\n/** Slugify a label into a branch-safe segment. */\nfunction slug(label: string): string {\n return (\n label\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 48) || 'candidate'\n )\n}\n\nexport function gitWorktreeAdapter(opts: GitWorktreeAdapterOptions): WorktreeAdapter {\n const git = opts.git ?? defaultGit\n const worktreeDir = opts.worktreeDir ?? join(opts.repoRoot, '.worktrees')\n const branchPrefix = opts.branchPrefix ?? 'improve'\n\n return {\n async create({ baseRef, label }) {\n const id = `${slug(label)}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`\n const branch = `${branchPrefix}/${id}`\n const path = join(worktreeDir, id)\n git(['worktree', 'add', '-b', branch, path, baseRef], opts.repoRoot)\n return { path, branch, baseRef }\n },\n\n async finalize(worktree, summary) {\n // Stage + commit any pending changes the agent left in the worktree.\n // A no-op commit is refused by git, so only commit when the tree is dirty.\n const status = git(['status', '--porcelain'], worktree.path)\n if (status.length > 0) {\n git(['add', '-A'], worktree.path)\n git(['commit', '-m', summary], worktree.path)\n }\n return {\n kind: 'code',\n worktreeRef: worktree.path,\n baseRef: worktree.baseRef,\n summary,\n }\n },\n\n async discard(worktree) {\n // Remove the worktree, then delete its branch. Force-remove because the\n // worktree may hold uncommitted experiment state we're discarding.\n git(['worktree', 'remove', '--force', worktree.path], opts.repoRoot)\n git(['branch', '-D', worktree.branch], opts.repoRoot)\n },\n }\n}\n\n/** Resolve a `CodeSurface`'s worktreeRef to a directory the measurement can\n * run the worker in. A path ref is returned as-is; anything else is treated\n * as a ref under the adapter's worktree dir. */\nexport function resolveWorktreePath(surface: CodeSurface, worktreeDir?: string): string {\n if (isAbsolute(surface.worktreeRef) && existsSync(surface.worktreeRef)) return surface.worktreeRef\n if (worktreeDir) return join(worktreeDir, basename(surface.worktreeRef))\n return surface.worktreeRef\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,SAAS,kBAAkB;AAC3B,SAAS,YAAY,WAAW,cAAc,qBAAqB;AACnE,SAAS,YAAY;AAmBd,IAAM,4BAAN,cAAwC,MAAM;AAAA,EACnD,YACkB,MAChB,SACA;AACA,UAAM,OAAO;AAHG;AAIhB,SAAK,OAAO;AAAA,EACd;AAAA,EALkB;AAMpB;AAQO,IAAM,yBAAN,MAA6D;AAAA,EAIlE,YAA6B,SAAwC;AAAxC;AAC3B,QAAI,CAAC,WAAW,QAAQ,IAAI,EAAG,WAAU,QAAQ,MAAM,EAAE,WAAW,KAAK,CAAC;AAC1E,SAAK,MAAM,QAAQ,OAAO,KAAK;AAAA,EACjC;AAAA,EAH6B;AAAA,EAHZ;AAAA,EACA,aAAa,oBAAI,IAA4B;AAAA,EAO9D,MAAM,QAAQ,OAA4C;AACxD,SAAK,iBAAiB,KAAK;AAC3B,SAAK,gBAAgB,KAAK;AAC1B,UAAM,SAAS,KAAK,SAAS,KAAK;AAClC,UAAM,OAAO,KAAK,cAAc,MAAM,MAAM;AAC5C,UAAM,OAAO,GAAG,KAAK,UAAU,MAAM,CAAC;AAAA;AAItC,eAAW,MAAM,IAAI;AAAA,EACvB;AAAA,EAEA,MAAM,OAAO,MAAmE;AAC9E,QAAI,CAAC,KAAK,OAAO;AACf,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,QAAI,CAAC,KAAK,gBAAgB;AACxB,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,UAAM,MAA+B,CAAC;AACtC,eAAW,UAAU,aAAa;AAGhC,UAAI,KAAK,UAAU,WAAW,WAAW,oBAAoB;AAC3D,cAAM,WAAW,qBAAqB,KAAK,QAAQ,QAAQ,kBAAkB;AAC7E,YAAI,CAAC,SAAU;AAAA,MACjB;AACA,YAAM,OAAO,KAAK,cAAc,MAAM;AACtC,UAAI,CAAC,WAAW,IAAI,EAAG;AACvB,YAAM,QAAQ,aAAa,MAAM,MAAM,EAAE,MAAM,IAAI,EAAE,OAAO,OAAO;AACnE,iBAAW,QAAQ,OAAO;AACxB,YAAI;AACJ,YAAI;AACF,mBAAS,KAAK,MAAM,IAAI;AAAA,QAC1B,QAAQ;AACN;AAAA,QACF;AACA,YAAI,CAAC,cAAc,QAAQ,MAAM,MAAM,EAAG;AAC1C,YAAI,KAAK,MAAM;AAAA,MACjB;AAAA,IACF;AAGA,QAAI,KAAK,CAAC,GAAG,MAAM;AACjB,UAAI,EAAE,eAAe,EAAE,WAAY,QAAO,EAAE,WAAW,cAAc,EAAE,UAAU;AACjF,aAAO,EAAE,WAAW,cAAc,EAAE,UAAU;AAAA,IAChD,CAAC;AAED,WAAO,IAAI,MAAM,GAAG,KAAK,KAAK;AAAA,EAChC;AAAA,EAEA,MAAM,OAAmF;AACvF,UAAM,WAAmC,CAAC;AAC1C,QAAI,QAAQ;AACZ,eAAW,UAAU,aAAa;AAChC,YAAM,OAAO,KAAK,cAAc,MAAM;AACtC,UAAI,CAAC,WAAW,IAAI,GAAG;AACrB,iBAAS,MAAM,IAAI;AACnB;AAAA,MACF;AACA,YAAM,QAAQ,aAAa,MAAM,MAAM,EAAE,MAAM,IAAI,EAAE,OAAO,OAAO,EAAE;AACrE,eAAS,MAAM,IAAI;AACnB,eAAS;AAAA,IACX;AAIA,WAAO,EAAE,OAAO,OAAO,MAAM,OAAO,SAAS;AAAA,EAC/C;AAAA,EAEQ,iBAAiB,OAAmC;AAC1D,QAAI,CAAC,MAAM,QAAQ;AACjB,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,QAAI,CAAC,MAAM,qBAAqB,MAAM,kBAAkB,WAAW,GAAG;AACpE,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,QAAI,CAAC,MAAM,YAAY;AACrB,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,QAAI,CAAC,MAAM,iBAAiB;AAC1B,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,QAAI,CAAC,YAAY,SAAS,MAAM,MAAM,GAAG;AACvC,YAAM,IAAI;AAAA,QACR;AAAA,QACA,+CAA+C,YAAY,KAAK,IAAI,CAAC;AAAA,MACvE;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,gBAAgB,OAAmC;AACzD,UAAM,MAAM,KAAK,QAAQ;AACzB,QAAI,CAAC,OAAO,CAAC,MAAM,gBAAiB;AACpC,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,WAAW;AACjB,QAAI,QAAQ,KAAK,WAAW,IAAI,MAAM,eAAe;AACrD,QAAI,CAAC,SAAS,MAAM,MAAM,iBAAiB,UAAU;AACnD,cAAQ,EAAE,QAAQ,MAAM,iBAAiB,eAAe,KAAK,OAAO,EAAE;AACtE,WAAK,WAAW,IAAI,MAAM,iBAAiB,KAAK;AAAA,IAClD;AACA,QAAI,MAAM,SAAS,KAAK;AACtB,YAAM,IAAI;AAAA,QACR;AAAA,QACA,gCAAgC,MAAM,eAAe,aAAa,GAAG;AAAA,MACvE;AAAA,IACF;AACA,UAAM,SAAS;AAAA,EACjB;AAAA,EAEQ,SAAS,OAAoD;AACnE,UAAM,aAAa;AAAA,MACjB,KAAK,UAAU;AAAA,QACb,IAAI,MAAM,SAAS;AAAA,QACnB,KAAK,MAAM;AAAA,QACX,IAAI,MAAM;AAAA,QACV,KAAK,MAAM;AAAA,MACb,CAAC;AAAA,IACH;AAGA,WAAO;AAAA,MACL,GAAG;AAAA,MACH;AAAA,MACA,OAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEQ,cAAc,QAAwB;AAC5C,WAAO,KAAK,KAAK,QAAQ,MAAM,GAAG,MAAM,QAAQ;AAAA,EAClD;AACF;AAEA,IAAM,cAAgD;AAAA,EACpD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,SAAS,qBACP,QACA,QACS;AACT,MAAI,CAAC,OAAQ,QAAO;AACpB,MAAI,MAAM,QAAQ,MAAM,EAAG,QAAO,OAAO,SAAS,MAAM;AACxD,SAAO,WAAW;AACpB;AAEA,SAAS,cACP,QACA,MACA,QACS;AAET,MAAI,KAAK,UAAU,WAAW,OAAO,cAAc,KAAK,eAAgB,QAAO;AAC/E,MAAI,KAAK,UAAU,UAAU,OAAO,aAAa,KAAK,eAAgB,QAAO;AAE7E,QAAM,IAAI,KAAK;AACf,MAAI,CAAC,EAAG,QAAO;AACf,MAAI,EAAE,QAAQ,OAAO,SAAS,SAAS,EAAE,KAAM,QAAO;AACtD,MAAI,EAAE,QAAQ;AACZ,UAAM,UAAU,MAAM,QAAQ,EAAE,MAAM,IAAI,EAAE,SAAS,CAAC,EAAE,MAAM;AAC9D,QAAI,CAAC,QAAQ,SAAS,MAAe,EAAG,QAAO;AAAA,EACjD;AACA,MAAI,EAAE,iBAAiB,UAAa,EAAE,iBAAiB,QAAW;AAChE,UAAM,aAAa,OAAO,OAAO,OAAO,WAAW,EAAE,IAAI,CAAC,MAAM,EAAE,SAAS;AAC3E,UAAM,MAAM,WAAW,WAAW,IAAI,IAAI,KAAK,IAAI,GAAG,UAAU;AAChE,QAAI,EAAE,iBAAiB,UAAa,MAAM,EAAE,aAAc,QAAO;AACjE,QAAI,EAAE,iBAAiB,UAAa,MAAM,EAAE,aAAc,QAAO;AAAA,EACnE;AACA,SAAO;AACT;AAEA,SAAS,OAAO,OAAuB;AACrC,SAAO,WAAW,QAAQ,EAAE,OAAO,KAAK,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,EAAE;AACrE;AAEA,SAAS,WAAW,MAAc,MAAoB;AACpD,MAAI,WAAW,IAAI,GAAG;AACpB,UAAM,WAAW,aAAa,MAAM,MAAM;AAC1C,kBAAc,MAAM,WAAW,IAAI;AAAA,EACrC,OAAO;AACL,kBAAc,MAAM,IAAI;AAAA,EAC1B;AACF;;;AC1QA,SAAS,oBAAoB;AAC7B,SAAS,cAAAA,mBAAkB;AAC3B,SAAS,UAAU,YAAY,QAAAC,aAAY;AAuBpC,IAAM,uBAAN,cAAmC,MAAM;AAAA,EAC9C,YACE,SACS,OACT;AACA,UAAM,OAAO;AAFJ;AAGT,SAAK,OAAO;AAAA,EACd;AAAA,EAJW;AAKb;AAaA,SAAS,WAAW,MAAgB,KAAqB;AACvD,MAAI;AACF,WAAO,aAAa,OAAO,MAAM,EAAE,KAAK,UAAU,OAAO,CAAC,EAAE,KAAK;AAAA,EACnE,SAAS,KAAK;AACZ,UAAM,SACJ,OAAO,OAAO,QAAQ,YAAY,YAAY,MAC1C,OAAQ,IAA4B,MAAM,IAC1C;AACN,UAAM,IAAI,qBAAqB,OAAO,KAAK,KAAK,GAAG,CAAC,YAAY,UAAU,OAAO,GAAG,CAAC,IAAI,GAAG;AAAA,EAC9F;AACF;AAGA,SAAS,KAAK,OAAuB;AACnC,SACE,MACG,YAAY,EACZ,QAAQ,eAAe,GAAG,EAC1B,QAAQ,YAAY,EAAE,EACtB,MAAM,GAAG,EAAE,KAAK;AAEvB;AAEO,SAAS,mBAAmB,MAAkD;AACnF,QAAM,MAAM,KAAK,OAAO;AACxB,QAAM,cAAc,KAAK,eAAeA,MAAK,KAAK,UAAU,YAAY;AACxE,QAAM,eAAe,KAAK,gBAAgB;AAE1C,SAAO;AAAA,IACL,MAAM,OAAO,EAAE,SAAS,MAAM,GAAG;AAC/B,YAAM,KAAK,GAAG,KAAK,KAAK,CAAC,IAAI,KAAK,IAAI,EAAE,SAAS,EAAE,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AAC9F,YAAM,SAAS,GAAG,YAAY,IAAI,EAAE;AACpC,YAAM,OAAOA,MAAK,aAAa,EAAE;AACjC,UAAI,CAAC,YAAY,OAAO,MAAM,QAAQ,MAAM,OAAO,GAAG,KAAK,QAAQ;AACnE,aAAO,EAAE,MAAM,QAAQ,QAAQ;AAAA,IACjC;AAAA,IAEA,MAAM,SAAS,UAAU,SAAS;AAGhC,YAAM,SAAS,IAAI,CAAC,UAAU,aAAa,GAAG,SAAS,IAAI;AAC3D,UAAI,OAAO,SAAS,GAAG;AACrB,YAAI,CAAC,OAAO,IAAI,GAAG,SAAS,IAAI;AAChC,YAAI,CAAC,UAAU,MAAM,OAAO,GAAG,SAAS,IAAI;AAAA,MAC9C;AACA,aAAO;AAAA,QACL,MAAM;AAAA,QACN,aAAa,SAAS;AAAA,QACtB,SAAS,SAAS;AAAA,QAClB;AAAA,MACF;AAAA,IACF;AAAA,IAEA,MAAM,QAAQ,UAAU;AAGtB,UAAI,CAAC,YAAY,UAAU,WAAW,SAAS,IAAI,GAAG,KAAK,QAAQ;AACnE,UAAI,CAAC,UAAU,MAAM,SAAS,MAAM,GAAG,KAAK,QAAQ;AAAA,IACtD;AAAA,EACF;AACF;AAKO,SAAS,oBAAoB,SAAsB,aAA8B;AACtF,MAAI,WAAW,QAAQ,WAAW,KAAKD,YAAW,QAAQ,WAAW,EAAG,QAAO,QAAQ;AACvF,MAAI,YAAa,QAAOC,MAAK,aAAa,SAAS,QAAQ,WAAW,CAAC;AACvE,SAAO,QAAQ;AACjB;","names":["existsSync","join"]}
1
+ {"version":3,"sources":["../../src/campaign/drivers/skillopt.ts","../../src/campaign/labeled-store/fs-adapter.ts","../../src/campaign/worktree/index.ts"],"sourcesContent":["/**\n * @experimental\n *\n * `skillOptDriver` — a section-aware, bounded-edit `ImprovementDriver` for\n * structured natural-language procedures (SKILL.md files, runbooks, sectioned\n * system prompts, judge rubrics with dimensions). Implements the SkillOpt\n * methodology (Microsoft, 2026): treat the skill document as a trainable\n * optimization target, train the procedure not the weights, constrain each\n * generation to ≤N targeted edits to prevent useful-rule overwrites.\n *\n * Differs from `gepaDriver` in two specific ways:\n *\n * 1. **Bounded edits.** Each candidate must differ from the baseline by at\n * most `editBudget` sentence-level changes. The \"edit budget functions\n * as a textual learning rate\" — without it, an LLM proposal can rewrite\n * so much that useful prior rules get overwritten.\n *\n * 2. **Section preservation.** When the surface is a structured doc, the\n * H2 headers (and an opt-in `preserveSections` allowlist) are\n * load-bearing for discoverability. Candidates that delete or rename\n * preserved sections are rejected at parse time.\n *\n * Selectable alongside `gepaDriver` and `evolutionaryDriver`. Use this when\n * the surface IS a structured doc; use `gepaDriver` when the surface is\n * unstructured prose.\n */\n\nimport { callLlm, type LlmClientOptions } from '../../llm-client'\nimport {\n buildReflectionPrompt,\n parseReflectionResponse,\n type TrialTrace,\n} from '../../reflective-mutation'\nimport type { ImprovementDriver, MutableSurface, ProposeContext } from '../types'\n\nconst REFLECTION_SYSTEM =\n 'You are an expert prompt engineer applying the SkillOpt methodology. ' +\n 'You will edit a structured natural-language procedure under TWO HARD ' +\n 'CONSTRAINTS: (1) preserve every H2 section heading verbatim — do NOT ' +\n 'delete, rename, or merge sections; (2) make at most EDIT_BUDGET targeted ' +\n 'sentence-level edits per candidate — bounded edits prevent overwriting ' +\n 'useful prior rules. Output ONLY a JSON object of shape ' +\n '{\"proposals\":[{\"label\":string,\"rationale\":string,\"payload\":string}]} ' +\n 'where each `payload` is the FULL improved skill text. No prose outside the JSON.'\n\nexport interface SkillOptDriverOptions {\n /** Router transport (apiKey/baseUrl). */\n llm: LlmClientOptions\n /** Model that performs the reflection. */\n model: string\n /** What is being optimized — appears in the reflection prompt for orientation. */\n target: string\n\n /** Max edits per generation — SkillOpt's \"textual learning rate\".\n * Default 3. Lower = more conservative, higher = more exploratory. */\n editBudget?: number\n\n /** Section headings the driver MUST preserve. When the surface is a\n * structured skill doc, sections are load-bearing for discoverability.\n * Default: auto-detected from H2 headers in the baseline. */\n preserveSections?: string[]\n\n /** Surface-specific mutation levers offered to the model. */\n mutationPrimitives?: string[]\n /** Top/bottom scenarios surfaced as evidence each generation. Default 3. */\n evidenceK?: number\n /** Reflection sampling temperature. Default 0.7. */\n temperature?: number\n /** Reflection max tokens. Default 6000. */\n maxTokens?: number\n}\n\n/** Internal — exported for tests. */\nexport function extractH2Sections(text: string): string[] {\n const out: string[] = []\n for (const line of text.split('\\n')) {\n const match = /^##\\s+(.+?)\\s*$/.exec(line)\n if (match) out.push(match[1]!)\n }\n return out\n}\n\n/** Sentence-level edit distance. Counts distinct sentence add/remove/replace\n * ops between baseline and candidate using a normalised line-by-line diff.\n * Imperfect (treats trivial whitespace as identical) but tight enough to\n * bound an LLM rewrite. Exported for tests. */\nexport function countSentenceEdits(baseline: string, candidate: string): number {\n const norm = (s: string) =>\n s\n .split(/(?<=[.!?])\\s+|\\n/g)\n .map((p) => p.trim())\n .filter((p) => p.length > 0)\n const a = new Set(norm(baseline))\n const b = new Set(norm(candidate))\n let edits = 0\n for (const s of a) if (!b.has(s)) edits++ // deletions\n for (const s of b) if (!a.has(s)) edits++ // additions\n return edits\n}\n\nexport function skillOptDriver(opts: SkillOptDriverOptions): ImprovementDriver {\n const evidenceK = opts.evidenceK ?? 3\n const editBudget = opts.editBudget ?? 3\n if (editBudget < 1) {\n throw new Error(\n `skillOptDriver: editBudget must be >= 1, got ${editBudget} (use evolutionaryDriver with a noop mutator for measure-only runs)`,\n )\n }\n return {\n kind: 'skillopt',\n async propose(ctx: ProposeContext): Promise<MutableSurface[]> {\n if (typeof ctx.currentSurface !== 'string') {\n throw new Error(\n `skillOptDriver: surface must be a string skill document; got ${typeof ctx.currentSurface}. Use evolutionaryDriver with a CodeSurface mutator for code-tier surfaces.`,\n )\n }\n const baseline = ctx.currentSurface\n const preserveSections = opts.preserveSections ?? extractH2Sections(baseline)\n\n const { top, bottom, target } = buildEvidence(ctx, evidenceK, opts.target)\n\n const reflectionUser = buildReflectionPrompt({\n target,\n parentPayload: baseline,\n topTrials: top,\n bottomTrials: bottom,\n childCount: ctx.populationSize,\n mutationPrimitives: opts.mutationPrimitives,\n })\n const constraintPreamble = [\n '',\n '## SkillOpt constraints (hard rules — violations rejected)',\n '',\n `- Edit budget: at most ${editBudget} sentence-level edits per candidate.`,\n '- Section preservation: every H2 heading below must appear unchanged in your output.',\n ...preserveSections.map((s) => ` - \\`## ${s}\\``),\n '',\n 'Reject any candidate in your own thinking that would delete a section, rename a heading, or exceed the edit budget. Make TARGETED, surgical edits — not rewrites.',\n '',\n ].join('\\n')\n const userPrompt = `${reflectionUser}${constraintPreamble}`\n const system = REFLECTION_SYSTEM.replace('EDIT_BUDGET', String(editBudget))\n\n const result = await callLlm(\n {\n model: opts.model,\n messages: [\n { role: 'system', content: system },\n { role: 'user', content: userPrompt },\n ],\n jsonMode: true,\n temperature: opts.temperature ?? 0.7,\n maxTokens: opts.maxTokens ?? 6000,\n },\n opts.llm,\n )\n\n const proposals = parseReflectionResponse(result.content, ctx.populationSize)\n const out: MutableSurface[] = []\n for (const proposal of proposals) {\n const text = typeof proposal.payload === 'string' ? proposal.payload.trim() : ''\n if (!text || text === baseline) continue\n if (!validateSections(text, preserveSections)) continue\n if (countSentenceEdits(baseline, text) > editBudget * 2) continue // x2: add+remove pair per edit\n if (out.includes(text)) continue\n out.push(text)\n }\n return out\n },\n }\n}\n\nfunction validateSections(candidate: string, required: string[]): boolean {\n if (required.length === 0) return true\n const have = new Set(extractH2Sections(candidate))\n for (const section of required) {\n if (!have.has(section)) return false\n }\n return true\n}\n\n/** Reused from gepaDriver pattern — build evidence from prior best candidate. */\nfunction buildEvidence(\n ctx: ProposeContext,\n evidenceK: number,\n baseTarget: string,\n): { top: TrialTrace[]; bottom: TrialTrace[]; target: string } {\n const last = ctx.history.at(-1)\n if (!last || last.candidates.length === 0) {\n return { top: [], bottom: [], target: baseTarget }\n }\n const best = [...last.candidates].sort((a, b) => b.composite - a.composite)[0]\n if (!best) return { top: [], bottom: [], target: baseTarget }\n\n const byScore = [...best.scenarios].sort((a, b) => b.composite - a.composite)\n const toTrace = (s: { scenarioId: string; composite: number }): TrialTrace => ({\n id: s.scenarioId,\n score: s.composite,\n })\n const top = byScore.slice(0, evidenceK).map(toTrace)\n const bottom = byScore.slice(-evidenceK).reverse().map(toTrace)\n\n const weakest = Object.entries(best.dimensions)\n .sort((a, b) => a[1] - b[1])\n .slice(0, 3)\n .map(([dim, value]) => `${dim} (${value.toFixed(2)})`)\n const target =\n weakest.length > 0 ? `${baseTarget} — weakest dimensions: ${weakest.join(', ')}` : baseTarget\n\n return { top, bottom, target }\n}\n","/**\n * @experimental\n *\n * Filesystem `LabeledScenarioStore` adapter. The default capture sink for\n * traces + eval artifacts. Production deployments typically swap for a\n * Turso/SQLite adapter (same interface).\n *\n * Records land as one JSONL file per source under `<root>/<source>.jsonl`.\n * Each line is a `LabeledScenarioRecord`. Append-only — no in-place edits.\n *\n * Safety properties enforced at write-time:\n *\n * - **Provenance required**: writes without `source`, `sourceVersionHash`,\n * `capturedAt`, `redactionStatus` are rejected. Closes the alignment\n * reviewer's data-poisoning gap.\n * - **Per-source rate limits**: optional `rateLimitBucket` + `maxWritesPerMinute`\n * stops a single tenant/source from flooding the store.\n *\n * Safety properties enforced at sample-time:\n *\n * - **Required split + capturedBefore**: substrate refuses to sample without\n * an explicit `split` ('train' | 'test') AND a temporal cutoff. Eliminates\n * accidental train/test contamination.\n * - **Default training-source filter**: when the store is sampled with\n * `split: 'train'`, production-trace records are EXCLUDED unless the\n * caller passes `filter.source: 'production-trace'` explicitly. Closes\n * the contamination-by-default gap flagged by the senior eval engineer.\n */\n\nimport { createHash } from 'node:crypto'\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport type {\n LabeledScenarioRecord,\n LabeledScenarioSampleArgs,\n LabeledScenarioSource,\n LabeledScenarioStore,\n LabeledScenarioWrite,\n} from '../types'\n\nexport interface FsLabeledScenarioStoreOptions {\n /** Root directory for JSONL files. Created if missing. */\n root: string\n /** Per-source rate limit. When set, writes exceeding the cap are rejected\n * with a typed error. Default: no limit. */\n maxWritesPerMinutePerBucket?: number\n /** Test seam — override `Date.now()` for deterministic tests. */\n now?: () => number\n}\n\nexport class LabeledScenarioStoreError extends Error {\n constructor(\n public readonly code: string,\n message: string,\n ) {\n super(message)\n this.name = 'LabeledScenarioStoreError'\n }\n}\n\ninterface RateLimitState {\n bucket: string\n windowStartMs: number\n count: number\n}\n\nexport class FsLabeledScenarioStore implements LabeledScenarioStore {\n private readonly now: () => number\n private readonly rateLimits = new Map<string, RateLimitState>()\n\n constructor(private readonly options: FsLabeledScenarioStoreOptions) {\n if (!existsSync(options.root)) mkdirSync(options.root, { recursive: true })\n this.now = options.now ?? Date.now\n }\n\n async observe(write: LabeledScenarioWrite): Promise<void> {\n this.assertProvenance(write)\n this.assertRateLimit(write)\n const record = this.toRecord(write)\n const path = this.pathForSource(write.source)\n const line = `${JSON.stringify(record)}\\n`\n // Append atomically. For high-throughput a writev-friendly buffered\n // implementation lands in the Turso adapter; FS adapter is for tests +\n // local dev + small workloads.\n appendLine(path, line)\n }\n\n async sample(args: LabeledScenarioSampleArgs): Promise<LabeledScenarioRecord[]> {\n if (!args.split) {\n throw new LabeledScenarioStoreError(\n 'split_required',\n 'sample() requires an explicit `split` (train | test) — substrate refuses ambiguous reads',\n )\n }\n if (!args.capturedBefore) {\n throw new LabeledScenarioStoreError(\n 'capturedBefore_required',\n 'sample() requires an explicit `capturedBefore` timestamp for temporal-split discipline',\n )\n }\n\n const all: LabeledScenarioRecord[] = []\n for (const source of ALL_SOURCES) {\n // Default training-source filter: when sampling train, EXCLUDE\n // production-trace records unless the caller asks for them.\n if (args.split === 'train' && source === 'production-trace') {\n const explicit = sourceFilterContains(args.filter?.source, 'production-trace')\n if (!explicit) continue\n }\n const path = this.pathForSource(source)\n if (!existsSync(path)) continue\n const lines = readFileSync(path, 'utf8').split('\\n').filter(Boolean)\n for (const line of lines) {\n let record: LabeledScenarioRecord\n try {\n record = JSON.parse(line) as LabeledScenarioRecord\n } catch {\n continue\n }\n if (!matchesFilter(record, args, source)) continue\n all.push(record)\n }\n }\n\n // Deterministic order: by capturedAt ascending, then recordHash.\n all.sort((a, b) => {\n if (a.capturedAt !== b.capturedAt) return a.capturedAt.localeCompare(b.capturedAt)\n return a.recordHash.localeCompare(b.recordHash)\n })\n\n return all.slice(0, args.count)\n }\n\n async size(): Promise<{ train: number; test: number; bySource: Record<string, number> }> {\n const bySource: Record<string, number> = {}\n let total = 0\n for (const source of ALL_SOURCES) {\n const path = this.pathForSource(source)\n if (!existsSync(path)) {\n bySource[source] = 0\n continue\n }\n const count = readFileSync(path, 'utf8').split('\\n').filter(Boolean).length\n bySource[source] = count\n total += count\n }\n // FS adapter doesn't track split assignments per-record (split is\n // computed at sample-time based on `capturedBefore`). For size(), we\n // report `train`+`test` as the same total — split is a sampling concept.\n return { train: total, test: total, bySource }\n }\n\n private assertProvenance(write: LabeledScenarioWrite): void {\n if (!write.source) {\n throw new LabeledScenarioStoreError(\n 'missing_source',\n 'LabeledScenarioWrite requires `source`',\n )\n }\n if (!write.sourceVersionHash || write.sourceVersionHash.length === 0) {\n throw new LabeledScenarioStoreError(\n 'missing_source_version',\n 'LabeledScenarioWrite requires `sourceVersionHash` (git sha or substrate version)',\n )\n }\n if (!write.capturedAt) {\n throw new LabeledScenarioStoreError(\n 'missing_captured_at',\n 'LabeledScenarioWrite requires `capturedAt` ISO timestamp',\n )\n }\n if (!write.redactionStatus) {\n throw new LabeledScenarioStoreError(\n 'missing_redaction_status',\n 'LabeledScenarioWrite requires explicit `redactionStatus` — raw / redacted-pii / redacted-secrets / fully-redacted',\n )\n }\n if (!ALL_SOURCES.includes(write.source)) {\n throw new LabeledScenarioStoreError(\n 'unknown_source',\n `LabeledScenarioWrite.source must be one of: ${ALL_SOURCES.join(', ')}`,\n )\n }\n }\n\n private assertRateLimit(write: LabeledScenarioWrite): void {\n const cap = this.options.maxWritesPerMinutePerBucket\n if (!cap || !write.rateLimitBucket) return\n const now = this.now()\n const windowMs = 60_000\n let state = this.rateLimits.get(write.rateLimitBucket)\n if (!state || now - state.windowStartMs >= windowMs) {\n state = { bucket: write.rateLimitBucket, windowStartMs: now, count: 0 }\n this.rateLimits.set(write.rateLimitBucket, state)\n }\n if (state.count >= cap) {\n throw new LabeledScenarioStoreError(\n 'rate_limit_exceeded',\n `LabeledScenarioStore: bucket ${write.rateLimitBucket} exceeded ${cap} writes/min`,\n )\n }\n state.count += 1\n }\n\n private toRecord(write: LabeledScenarioWrite): LabeledScenarioRecord {\n const recordHash = sha256(\n JSON.stringify({\n id: write.scenario.id,\n src: write.source,\n at: write.capturedAt,\n ver: write.sourceVersionHash,\n }),\n )\n // FS adapter assigns split at sample-time, but we cache a hint here\n // based on capturedAt vs the world's \"now\" — sampler overrides this.\n return {\n ...write,\n recordHash,\n split: 'train',\n }\n }\n\n private pathForSource(source: string): string {\n return join(this.options.root, `${source}.jsonl`)\n }\n}\n\nconst ALL_SOURCES: LabeledScenarioWrite['source'][] = [\n 'production-trace',\n 'eval-run',\n 'manual',\n 'red-team',\n 'synthetic',\n]\n\nfunction sourceFilterContains(\n filter: LabeledScenarioSource | LabeledScenarioSource[] | undefined,\n needle: LabeledScenarioSource,\n): boolean {\n if (!filter) return false\n if (Array.isArray(filter)) return filter.includes(needle)\n return filter === needle\n}\n\nfunction matchesFilter(\n record: LabeledScenarioRecord,\n args: LabeledScenarioSampleArgs,\n source: string,\n): boolean {\n // Temporal cutoff — train must be capturedAt < capturedBefore.\n if (args.split === 'train' && record.capturedAt >= args.capturedBefore) return false\n if (args.split === 'test' && record.capturedAt < args.capturedBefore) return false\n\n const f = args.filter\n if (!f) return true\n if (f.kind && record.scenario.kind !== f.kind) return false\n if (f.source) {\n const sources = Array.isArray(f.source) ? f.source : [f.source]\n if (!sources.includes(source as never)) return false\n }\n if (f.minComposite !== undefined || f.maxComposite !== undefined) {\n const composites = Object.values(record.judgeScores).map((s) => s.composite)\n const max = composites.length === 0 ? 0 : Math.max(...composites)\n if (f.minComposite !== undefined && max < f.minComposite) return false\n if (f.maxComposite !== undefined && max > f.maxComposite) return false\n }\n return true\n}\n\nfunction sha256(input: string): string {\n return createHash('sha256').update(input).digest('hex').slice(0, 16)\n}\n\nfunction appendLine(path: string, line: string): void {\n if (existsSync(path)) {\n const existing = readFileSync(path, 'utf8')\n writeFileSync(path, existing + line)\n } else {\n writeFileSync(path, line)\n }\n}\n","/**\n * @experimental\n *\n * VCS-pluggable worktree adapter. One improvement = one worktree, PR-like\n * (multiple commits allowed). A code-tier driver's `propose()` creates a\n * worktree, an agent commits the change into it, and `finalize()` returns a\n * `CodeSurface{ worktreeRef }` the measurement checks out to run the worker\n * against the changed code. On promotion the worktree becomes the PR branch.\n *\n * The interface is VCS-agnostic so a future `jj` ([jj-vcs](https://github.com/jj-vcs/jj))\n * adapter can slot in without touching driver code. Only the git adapter\n * ships today. See `docs/design/self-improvement-engine.md`.\n */\n\nimport { execFileSync } from 'node:child_process'\nimport { existsSync } from 'node:fs'\nimport { basename, isAbsolute, join } from 'node:path'\nimport type { CodeSurface } from '../types'\n\nexport interface Worktree {\n /** Absolute path to the checked-out worktree directory. */\n path: string\n /** The branch the worktree is on (becomes the PR branch on promotion). */\n branch: string\n /** The ref the worktree was forked from. */\n baseRef: string\n}\n\nexport interface WorktreeAdapter {\n /** Create an isolated worktree on a fresh branch off `baseRef`. */\n create(opts: { baseRef: string; label: string }): Promise<Worktree>\n /** Commit any pending changes in the worktree, then return a CodeSurface\n * pointing at it. The agent has already written its change into\n * `worktree.path` by the time this is called. */\n finalize(worktree: Worktree, summary: string): Promise<CodeSurface>\n /** Remove the worktree (and its branch) — called for losing candidates. */\n discard(worktree: Worktree): Promise<void>\n}\n\nexport class WorktreeAdapterError extends Error {\n constructor(\n message: string,\n readonly cause?: unknown,\n ) {\n super(message)\n this.name = 'WorktreeAdapterError'\n }\n}\n\nexport interface GitWorktreeAdapterOptions {\n /** Repo root the worktrees fork from. */\n repoRoot: string\n /** Directory worktrees are created under. Default: `<repoRoot>/.worktrees`. */\n worktreeDir?: string\n /** Branch-name prefix. Default: `improve`. */\n branchPrefix?: string\n /** Test seam — defaults to a real `git` runner. */\n git?: (args: string[], cwd: string) => string\n}\n\nfunction defaultGit(args: string[], cwd: string): string {\n try {\n return execFileSync('git', args, { cwd, encoding: 'utf8' }).trim()\n } catch (err) {\n const stderr =\n err && typeof err === 'object' && 'stderr' in err\n ? String((err as { stderr: unknown }).stderr)\n : ''\n throw new WorktreeAdapterError(`git ${args.join(' ')} failed: ${stderr || String(err)}`, err)\n }\n}\n\n/** Slugify a label into a branch-safe segment. */\nfunction slug(label: string): string {\n return (\n label\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 48) || 'candidate'\n )\n}\n\nexport function gitWorktreeAdapter(opts: GitWorktreeAdapterOptions): WorktreeAdapter {\n const git = opts.git ?? defaultGit\n const worktreeDir = opts.worktreeDir ?? join(opts.repoRoot, '.worktrees')\n const branchPrefix = opts.branchPrefix ?? 'improve'\n\n return {\n async create({ baseRef, label }) {\n const id = `${slug(label)}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`\n const branch = `${branchPrefix}/${id}`\n const path = join(worktreeDir, id)\n git(['worktree', 'add', '-b', branch, path, baseRef], opts.repoRoot)\n return { path, branch, baseRef }\n },\n\n async finalize(worktree, summary) {\n // Stage + commit any pending changes the agent left in the worktree.\n // A no-op commit is refused by git, so only commit when the tree is dirty.\n const status = git(['status', '--porcelain'], worktree.path)\n if (status.length > 0) {\n git(['add', '-A'], worktree.path)\n git(['commit', '-m', summary], worktree.path)\n }\n return {\n kind: 'code',\n worktreeRef: worktree.path,\n baseRef: worktree.baseRef,\n summary,\n }\n },\n\n async discard(worktree) {\n // Remove the worktree, then delete its branch. Force-remove because the\n // worktree may hold uncommitted experiment state we're discarding.\n git(['worktree', 'remove', '--force', worktree.path], opts.repoRoot)\n git(['branch', '-D', worktree.branch], opts.repoRoot)\n },\n }\n}\n\n/** Resolve a `CodeSurface`'s worktreeRef to a directory the measurement can\n * run the worker in. A path ref is returned as-is; anything else is treated\n * as a ref under the adapter's worktree dir. */\nexport function resolveWorktreePath(surface: CodeSurface, worktreeDir?: string): string {\n if (isAbsolute(surface.worktreeRef) && existsSync(surface.worktreeRef)) return surface.worktreeRef\n if (worktreeDir) return join(worktreeDir, basename(surface.worktreeRef))\n return surface.worktreeRef\n}\n"],"mappings":"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","names":["existsSync","join"]}
@@ -3,8 +3,8 @@ export { f as CampaignAggregates, g as CampaignArtifactWriter, h as CampaignCell
3
3
  import { C as CampaignStorage, d as RunImprovementLoopResult } from '../run-improvement-loop-BPMjNKMJ.js';
4
4
  export { D as DefaultProductionGateOptions, E as EvolutionaryDriverOptions, G as GepaDriverOptions, H as HeldOutGateOptions, R as RunCampaignOptions, b as RunEvalOptions, c as RunImprovementLoopOptions, g as composeGate, h as defaultProductionGate, i as evolutionaryDriver, j as fsCampaignStorage, k as gepaDriver, l as heldOutGate, m as inMemoryCampaignStorage, r as runCampaign, n as runEval, p as runImprovementLoop } from '../run-improvement-loop-BPMjNKMJ.js';
5
5
  export { D as DeploymentOutcome, F as FileSystemOutcomeStore, b as FileSystemOutcomeStoreOptions, I as InMemoryOutcomeStore, O as OutcomeStore } from '../outcome-store-D6KWmYvj.js';
6
- import { a as HostedTenant, I as InsightReport, T as TraceSpanEvent } from '../index-BRxz6qov.js';
7
- export { F as FailureClusterInsight, b as InterRaterInsight, J as JudgeInsight, L as LiftInsight, O as OutcomeCorrelationInsight, R as Recommendation, c as ReleaseSummary, S as ScalarDistribution } from '../index-BRxz6qov.js';
6
+ import { a as HostedTenant, I as InsightReport, T as TraceSpanEvent } from '../index-DQHtWQ57.js';
7
+ export { F as FailureClusterInsight, b as InterRaterInsight, J as JudgeInsight, L as LiftInsight, O as OutcomeCorrelationInsight, R as Recommendation, c as ReleaseSummary, S as ScalarDistribution } from '../index-DQHtWQ57.js';
8
8
  import { A as AnalystRegistry } from '../registry-8KAs18kY.js';
9
9
  import { a as DatasetScenario } from '../dataset-BlwAtYYf.js';
10
10
  import { R as RunRecord, a as RunSplitTag } from '../run-record-BGY6bHRh.js';
@@ -49,14 +49,27 @@ async function analyzeRuns(opts) {
49
49
  const bins = opts.histogramBins ?? 12;
50
50
  const threshold = opts.decisionThreshold ?? 0.02;
51
51
  const split = resolveSplit(runs, opts.split ?? "auto");
52
+ const compositeWithIds = runs.map((r) => ({ runId: r.runId, score: compositeOf(r, split) })).filter((p) => Number.isFinite(p.score));
52
53
  const composite = distributionOf(
53
- runs.map((r) => compositeOf(r, split)).filter(Number.isFinite),
54
- bins
54
+ compositeWithIds.map((p) => p.score),
55
+ bins,
56
+ compositeWithIds
55
57
  );
56
58
  const perDimension = computePerDimension(runs, bins);
59
+ const costs = runs.map((r) => r.costUsd).filter(Number.isFinite);
60
+ const costDist = distributionOf(costs, bins);
61
+ const pareto = paretoChart(runs, { split });
62
+ const degraded = {};
63
+ if (costs.length === 0 || costs.every((c) => c === 0)) {
64
+ degraded.cost = "no costUsd values recorded \u2014 cost axis carries no signal";
65
+ }
66
+ if (pareto.points.length < 2) {
67
+ degraded.pareto = pareto.points.length === 0 ? "no candidates \u2014 Pareto unavailable" : "single candidate \u2014 Pareto is a single point, not a frontier";
68
+ }
57
69
  const costQuality = {
58
- cost: distributionOf(runs.map((r) => r.costUsd).filter(Number.isFinite), bins),
59
- pareto: paretoChart(runs, { split })
70
+ cost: costDist,
71
+ pareto,
72
+ ...degraded.cost || degraded.pareto ? { degraded } : {}
60
73
  };
61
74
  const judges = computeJudgeInsights(runs);
62
75
  const interRater = opts.raterScores ? computeInterRater(opts.raterScores) : void 0;
@@ -101,7 +114,7 @@ function compositeOf(run, split) {
101
114
  const alt = split === "holdout" ? run.outcome.searchScore : run.outcome.holdoutScore;
102
115
  return Number.isFinite(alt) ? alt : Number.NaN;
103
116
  }
104
- function distributionOf(values, bins) {
117
+ function distributionOf(values, bins, withIds) {
105
118
  if (values.length === 0) {
106
119
  return {
107
120
  n: 0,
@@ -119,6 +132,7 @@ function distributionOf(values, bins) {
119
132
  const mean2 = sorted.reduce((s, v) => s + v, 0) / n;
120
133
  const variance = sorted.reduce((s, v) => s + (v - mean2) ** 2, 0) / n;
121
134
  const stddev = Math.sqrt(variance);
135
+ const tailRuns = withIds ? [...withIds].sort((a, b) => a.score - b.score).slice(0, Math.min(5, withIds.length)) : void 0;
122
136
  return {
123
137
  n,
124
138
  mean: mean2,
@@ -127,7 +141,8 @@ function distributionOf(values, bins) {
127
141
  stddev,
128
142
  min: sorted[0],
129
143
  max: sorted[n - 1],
130
- histogram: histogram(sorted, bins)
144
+ histogram: histogram(sorted, bins),
145
+ ...tailRuns ? { tailRuns } : {}
131
146
  };
132
147
  }
133
148
  function percentile(sorted, q) {
@@ -477,6 +492,38 @@ function buildReleaseScorecard(composite, lift, contamination) {
477
492
  }
478
493
  function buildRecommendations(ctx) {
479
494
  const out = [];
495
+ if (ctx.composite.n > 0) {
496
+ if (ctx.composite.mean < 0.3) {
497
+ const tail = ctx.composite.tailRuns ?? [];
498
+ const names = tail.slice(0, 5).map((t) => `${t.runId}=${t.score.toFixed(3)}`).join(", ");
499
+ out.push({
500
+ priority: "critical",
501
+ kind: "investigate",
502
+ title: `Composite mean ${ctx.composite.mean.toFixed(3)} is below the 0.3 floor \u2014 the agent is broken on this corpus`,
503
+ detail: tail.length > 0 ? `Worst ${tail.length} run${tail.length === 1 ? "" : "s"} to inspect first: ${names}. Histogram p50=${ctx.composite.p50.toFixed(3)}, p95=${ctx.composite.p95.toFixed(3)}.` : `Histogram p50=${ctx.composite.p50.toFixed(3)}, p95=${ctx.composite.p95.toFixed(3)}.`,
504
+ evidencePath: "composite.tailRuns"
505
+ });
506
+ } else if (ctx.composite.mean < 0.5) {
507
+ const tail = ctx.composite.tailRuns ?? [];
508
+ const names = tail.slice(0, 3).map((t) => `${t.runId}=${t.score.toFixed(3)}`).join(", ");
509
+ out.push({
510
+ priority: "high",
511
+ kind: "investigate",
512
+ title: `Composite mean ${ctx.composite.mean.toFixed(3)} is below 0.5 \u2014 investigate the lower tail before claiming the agent is healthy`,
513
+ detail: tail.length > 0 ? `Worst ${tail.length} run${tail.length === 1 ? "" : "s"}: ${names}. Histogram p50=${ctx.composite.p50.toFixed(3)}, p95=${ctx.composite.p95.toFixed(3)}.` : `Histogram p50=${ctx.composite.p50.toFixed(3)}, p95=${ctx.composite.p95.toFixed(3)}.`,
514
+ evidencePath: "composite.tailRuns"
515
+ });
516
+ }
517
+ }
518
+ if (Object.keys(ctx.judges).length === 0 && ctx.composite.n > 0) {
519
+ out.push({
520
+ priority: "medium",
521
+ kind: "expand-corpus",
522
+ title: "No judge scores recorded \u2014 per-dimension + calibration insights unavailable",
523
+ detail: "Records have no `outcome.judgeScores`. To unlock perDimension, judges, and calibration, attach a Judge run during your eval pass and populate `outcome.judgeScores.perJudge[judgeName][dimension] = score`. See `docs/insight-report.md` for the expected shape.",
524
+ evidencePath: "judges"
525
+ });
526
+ }
480
527
  if (ctx.lift) {
481
528
  const decisive = ctx.lift.ci95[0] > ctx.threshold;
482
529
  const inconclusive = ctx.lift.ci95[0] <= ctx.threshold && ctx.lift.ci95[1] > ctx.threshold;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/contract/analyze-runs.ts","../../src/contract/self-improve.ts","../../src/contract/intake/feedback-table.ts","../../src/contract/intake/otel-spans.ts"],"sourcesContent":["/**\n * # `analyzeRuns()` — turn a set of agent runs into an actionable decision packet.\n *\n * Wires the substrate's statistical, calibration, clustering, Pareto, and\n * release-confidence primitives into one `InsightReport`. Two top-level\n * entry points use this function:\n *\n * - `selfImprove()` calls it on the campaign output to attach a packet\n * to every run.\n * - Consumers with observed `RunRecord[]` (production traces, gold\n * corpora, approve/reject tables) call it directly via `analyzeRuns()`\n * for analysis without a closed loop.\n *\n * Every section is opt-in based on what the input data supports — the\n * function never invents signal. If runs carry no judge scores, `judges`\n * is empty. If there's no baseline/candidate split, `lift` is undefined.\n * If no `analyst` is wired, `failureClusters` is undefined.\n *\n * The `recommendations` array is the human-readable layer; everything\n * else is the evidence backing each recommendation.\n */\n\nimport type { AnalystRegistry } from '../analyst/registry'\nimport type { AnalystFinding } from '../analyst/types'\nimport { checkCanaries } from '../contamination-guard'\nimport type { DatasetScenario } from '../dataset'\nimport type { RunRecord } from '../run-record'\nimport { cohensD, pairedBootstrap, pairedMde, pairedTTest, requiredSampleSize } from '../statistics'\nimport { type ParetoFigureSpec, paretoChart } from '../summary-report'\n\nimport type {\n FailureClusterInsight,\n InsightReport,\n InterRaterInsight,\n JudgeInsight,\n LiftInsight,\n OutcomeCorrelationInsight,\n Recommendation,\n ScalarDistribution,\n} from './insight-report'\n\n// ── Public API ───────────────────────────────────────────────────────\n\nexport interface AnalyzeRunsOptions {\n /** The runs to analyze. */\n runs: RunRecord[]\n /** Which split to score against when reading composite from RunOutcome.\n * Default: holdout when ANY run has a `holdoutScore`, else search. */\n split?: 'search' | 'holdout' | 'auto'\n /** Pairwise analysis configuration. When both `baselineCandidateId` and\n * `candidateCandidateId` are present, lift is computed on paired\n * (experimentId, seed) tuples shared between the two sides. */\n baselineCandidateId?: string\n candidateCandidateId?: string\n /** Canary scenarios — checked against every run's raw output for\n * holdout contamination. */\n canaryScenarios?: DatasetScenario[]\n /** Analyst registry for failure clustering. When omitted, the\n * `failureClusters` section is left undefined. */\n analyst?: AnalystRegistry\n /** Downstream outcome metric per run (e.g. engagement rate, approval\n * rate, downstream pass rate). When present, the report includes\n * `outcomeCorrelation` + a simple linear reward model fit. */\n outcomeSignal?: {\n metric: string\n valueByRunId: Record<string, number>\n }\n /** Multi-rater feedback for inter-rater agreement. Each entry is one\n * rater's score for one run. Two or more raters → kappa + disagreement\n * triage list. */\n raterScores?: Array<{ runId: string; rater: string; score: number }>\n /** Number of histogram bins for distributional summaries. Default 12. */\n histogramBins?: number\n /** Decision threshold — the smallest composite lift the caller cares\n * about. Used by the recommendations engine to call ship vs hold.\n * Default 0.02. */\n decisionThreshold?: number\n}\n\nexport async function analyzeRuns(opts: AnalyzeRunsOptions): Promise<InsightReport> {\n const runs = opts.runs\n const bins = opts.histogramBins ?? 12\n const threshold = opts.decisionThreshold ?? 0.02\n const split = resolveSplit(runs, opts.split ?? 'auto')\n\n const composite = distributionOf(\n runs.map((r) => compositeOf(r, split)).filter(Number.isFinite) as number[],\n bins,\n )\n\n const perDimension = computePerDimension(runs, bins)\n\n const costQuality = {\n cost: distributionOf(runs.map((r) => r.costUsd).filter(Number.isFinite), bins),\n pareto: paretoChart(runs, { split }),\n }\n\n const judges = computeJudgeInsights(runs)\n\n const interRater = opts.raterScores ? computeInterRater(opts.raterScores) : undefined\n\n const lift = computeLift(runs, opts.baselineCandidateId, opts.candidateCandidateId, split)\n\n const failureClusters = opts.analyst\n ? await computeFailureClusters(runs, opts.analyst, split)\n : undefined\n\n const contamination = opts.canaryScenarios\n ? computeContamination(runs, opts.canaryScenarios)\n : undefined\n\n const outcomeCorrelation = opts.outcomeSignal\n ? computeOutcomeCorrelation(runs, opts.outcomeSignal, split)\n : undefined\n\n const release = buildReleaseScorecard(composite, lift, contamination)\n\n const recommendations = buildRecommendations({\n composite,\n judges,\n interRater,\n lift,\n failureClusters,\n contamination,\n outcomeCorrelation,\n threshold,\n })\n\n return {\n n: runs.length,\n composite,\n perDimension,\n costQuality,\n judges,\n interRater,\n lift,\n failureClusters,\n contamination,\n outcomeCorrelation,\n release,\n recommendations,\n }\n}\n\n// ── Composite + split selection ─────────────────────────────────────\n\nfunction resolveSplit(\n runs: RunRecord[],\n pref: 'search' | 'holdout' | 'auto',\n): 'search' | 'holdout' {\n if (pref !== 'auto') return pref\n const hasHoldout = runs.some((r) => Number.isFinite(r.outcome.holdoutScore))\n return hasHoldout ? 'holdout' : 'search'\n}\n\nfunction compositeOf(run: RunRecord, split: 'search' | 'holdout'): number {\n const primary = split === 'holdout' ? run.outcome.holdoutScore : run.outcome.searchScore\n if (Number.isFinite(primary)) return primary as number\n // Fall through to the other split if the preferred one is missing —\n // analyzeRuns shouldn't refuse to summarise a run just because the\n // caller asked for the split that wasn't recorded.\n const alt = split === 'holdout' ? run.outcome.searchScore : run.outcome.holdoutScore\n return Number.isFinite(alt) ? (alt as number) : Number.NaN\n}\n\n// ── Distribution helpers ────────────────────────────────────────────\n\nfunction distributionOf(values: number[], bins: number): ScalarDistribution {\n if (values.length === 0) {\n return {\n n: 0,\n mean: 0,\n p50: 0,\n p95: 0,\n stddev: 0,\n min: 0,\n max: 0,\n histogram: [],\n }\n }\n const sorted = [...values].sort((a, b) => a - b)\n const n = sorted.length\n const mean = sorted.reduce((s, v) => s + v, 0) / n\n const variance = sorted.reduce((s, v) => s + (v - mean) ** 2, 0) / n\n const stddev = Math.sqrt(variance)\n return {\n n,\n mean,\n p50: percentile(sorted, 0.5),\n p95: percentile(sorted, 0.95),\n stddev,\n min: sorted[0]!,\n max: sorted[n - 1]!,\n histogram: histogram(sorted, bins),\n }\n}\n\nfunction percentile(sorted: number[], q: number): number {\n if (sorted.length === 0) return 0\n if (sorted.length === 1) return sorted[0]!\n const idx = (sorted.length - 1) * q\n const lo = Math.floor(idx)\n const hi = Math.ceil(idx)\n if (lo === hi) return sorted[lo]!\n const w = idx - lo\n return sorted[lo]! * (1 - w) + sorted[hi]! * w\n}\n\n/** Even-width histogram over the value range. Returns inclusive-lo /\n * exclusive-hi bins (closed on right for the last bin) compatible with\n * the substrate's `GainDistributionBin` shape. */\nfunction histogram(sorted: number[], bins: number): ScalarDistribution['histogram'] {\n if (sorted.length === 0 || bins < 1) return []\n const min = sorted[0]!\n const max = sorted[sorted.length - 1]!\n if (min === max) return [{ lo: min, hi: max, count: sorted.length }]\n const width = (max - min) / bins\n const out: ScalarDistribution['histogram'] = []\n for (let i = 0; i < bins; i++) {\n const lo = min + i * width\n const hi = i === bins - 1 ? max : lo + width\n out.push({ lo, hi, count: 0 })\n }\n for (const v of sorted) {\n const idx = Math.min(bins - 1, Math.floor((v - min) / width))\n out[idx]!.count++\n }\n return out\n}\n\nfunction computePerDimension(runs: RunRecord[], bins: number): Record<string, ScalarDistribution> {\n // JudgeScoresRecord pre-aggregates `perDimMean` (mean across judges per\n // dimension). We collect those means across runs to produce a per-dim\n // distribution at the corpus level. Consumers who want per-judge\n // dimension values reach into `perJudge[judgeId][dim]` themselves.\n const byDim = new Map<string, number[]>()\n for (const run of runs) {\n const scores = run.outcome.judgeScores\n if (!scores) continue\n for (const [dim, value] of Object.entries(scores.perDimMean ?? {})) {\n if (!Number.isFinite(value)) continue\n const arr = byDim.get(dim) ?? []\n arr.push(value)\n byDim.set(dim, arr)\n }\n }\n const out: Record<string, ScalarDistribution> = {}\n for (const [dim, values] of byDim) out[dim] = distributionOf(values, bins)\n return out\n}\n\n// ── Judge insights ──────────────────────────────────────────────────\n\nfunction computeJudgeInsights(runs: RunRecord[]): Record<string, JudgeInsight> {\n // Each judge's per-run mean is the average of its per-dimension scores\n // for that run. We aggregate those means across all runs each judge\n // scored — giving consumers a \"this judge's typical verdict\" reading.\n const out: Record<string, JudgeInsight> = {}\n const byJudge = new Map<string, number[]>()\n for (const run of runs) {\n const scores = run.outcome.judgeScores\n if (!scores?.perJudge) continue\n for (const [judgeId, dims] of Object.entries(scores.perJudge)) {\n const dimValues = Object.values(dims).filter(Number.isFinite) as number[]\n if (dimValues.length === 0) continue\n const judgeMean = dimValues.reduce((s, v) => s + v, 0) / dimValues.length\n const arr = byJudge.get(judgeId) ?? []\n arr.push(judgeMean)\n byJudge.set(judgeId, arr)\n }\n }\n for (const [judgeId, values] of byJudge) {\n out[judgeId] = {\n n: values.length,\n meanScore: values.reduce((s, v) => s + v, 0) / values.length,\n }\n }\n return out\n}\n\n// ── Inter-rater agreement ───────────────────────────────────────────\n\nfunction computeInterRater(\n ratings: Array<{ runId: string; rater: string; score: number }>,\n): InterRaterInsight | undefined {\n const byRun = new Map<string, Array<{ rater: string; score: number }>>()\n for (const r of ratings) {\n if (!Number.isFinite(r.score)) continue\n const list = byRun.get(r.runId) ?? []\n list.push({ rater: r.rater, score: r.score })\n byRun.set(r.runId, list)\n }\n const raters = new Set(ratings.map((r) => r.rater))\n const jointlyRated: string[] = []\n for (const [runId, ratersForRun] of byRun) {\n const seen = new Set(ratersForRun.map((r) => r.rater))\n let all = true\n for (const r of raters) if (!seen.has(r)) all = false\n if (all) jointlyRated.push(runId)\n }\n if (raters.size < 2 || jointlyRated.length === 0) return undefined\n\n const raterList = [...raters].sort()\n const perPair: Record<string, number> = {}\n for (let i = 0; i < raterList.length; i++) {\n for (let j = i + 1; j < raterList.length; j++) {\n const a = raterList[i]!\n const b = raterList[j]!\n const aScores: number[] = []\n const bScores: number[] = []\n for (const runId of jointlyRated) {\n const ratersForRun = byRun.get(runId)!\n const sa = ratersForRun.find((r) => r.rater === a)?.score\n const sb = ratersForRun.find((r) => r.rater === b)?.score\n if (sa !== undefined && sb !== undefined) {\n aScores.push(sa)\n bScores.push(sb)\n }\n }\n perPair[`${a}::${b}`] = pearson(aScores, bScores)\n }\n }\n const pairKappas = Object.values(perPair)\n const kappa =\n pairKappas.length === 0 ? 0 : pairKappas.reduce((s, v) => s + v, 0) / pairKappas.length\n\n const disagreementCases = jointlyRated\n .map((runId) => {\n const ratersForRun = byRun.get(runId)!\n const scores = ratersForRun.map((r) => r.score)\n const range = Math.max(...scores) - Math.min(...scores)\n return { runId, ratings: ratersForRun, range }\n })\n .sort((a, b) => b.range - a.range)\n .slice(0, 20)\n\n return {\n raters: raters.size,\n jointlyRated: jointlyRated.length,\n kappa,\n perPair,\n disagreementCases,\n }\n}\n\nfunction pearson(a: number[], b: number[]): number {\n if (a.length !== b.length || a.length === 0) return 0\n const n = a.length\n const meanA = a.reduce((s, v) => s + v, 0) / n\n const meanB = b.reduce((s, v) => s + v, 0) / n\n let num = 0\n let denomA = 0\n let denomB = 0\n for (let i = 0; i < n; i++) {\n const da = a[i]! - meanA\n const db = b[i]! - meanB\n num += da * db\n denomA += da * da\n denomB += db * db\n }\n const denom = Math.sqrt(denomA * denomB)\n return denom === 0 ? 0 : num / denom\n}\n\n// ── Lift ────────────────────────────────────────────────────────────\n\nfunction computeLift(\n runs: RunRecord[],\n baselineId: string | undefined,\n candidateId: string | undefined,\n split: 'search' | 'holdout',\n): LiftInsight | undefined {\n let bId = baselineId\n let cId = candidateId\n if (!bId || !cId) {\n // Auto-detect: when exactly two distinct candidateIds appear, treat the\n // lower-mean side as baseline.\n const ids = [...new Set(runs.map((r) => r.candidateId))]\n if (ids.length !== 2) return undefined\n const [idA, idB] = ids as [string, string]\n const meanA = mean(runs.filter((r) => r.candidateId === idA).map((r) => compositeOf(r, split)))\n const meanB = mean(runs.filter((r) => r.candidateId === idB).map((r) => compositeOf(r, split)))\n bId = meanA <= meanB ? idA : idB\n cId = meanA <= meanB ? idB : idA\n }\n\n const baseline = runs.filter((r) => r.candidateId === bId)\n const candidate = runs.filter((r) => r.candidateId === cId)\n if (baseline.length === 0 || candidate.length === 0) return undefined\n\n // Pair on (experimentId, seed). When that key doesn't match, fall back\n // to ordinal pairing — common for fresh runs from the same scenario list.\n const baselineByKey = new Map(baseline.map((r) => [pairingKey(r), r]))\n const pairedBaseline: number[] = []\n const pairedCandidate: number[] = []\n let usedKeyPairing = false\n for (const cand of candidate) {\n const b = baselineByKey.get(pairingKey(cand))\n if (b) {\n const bC = compositeOf(b, split)\n const cC = compositeOf(cand, split)\n if (Number.isFinite(bC) && Number.isFinite(cC)) {\n pairedBaseline.push(bC)\n pairedCandidate.push(cC)\n usedKeyPairing = true\n }\n }\n }\n if (!usedKeyPairing) {\n const n = Math.min(baseline.length, candidate.length)\n for (let i = 0; i < n; i++) {\n const bC = compositeOf(baseline[i]!, split)\n const cC = compositeOf(candidate[i]!, split)\n if (Number.isFinite(bC) && Number.isFinite(cC)) {\n pairedBaseline.push(bC)\n pairedCandidate.push(cC)\n }\n }\n }\n if (pairedBaseline.length === 0) return undefined\n\n const baselineMean = mean(pairedBaseline)\n const candidateMean = mean(pairedCandidate)\n const delta = candidateMean - baselineMean\n\n const bootstrap = pairedBootstrap(pairedBaseline, pairedCandidate, {\n confidence: 0.95,\n resamples: 2000,\n statistic: 'mean',\n })\n const tTest = pairedTTest(pairedBaseline, pairedCandidate)\n const d = cohensD(pairedBaseline, pairedCandidate)\n const mde = pairedMde({ nPaired: pairedBaseline.length, power: 0.8, alpha: 0.05 })\n const requiredN = requiredSampleSize({\n effect: Math.max(Math.abs(delta), 1e-6),\n power: 0.8,\n alpha: 0.05,\n })\n\n return {\n baselineMean,\n candidateMean,\n delta,\n ci95: [bootstrap.low, bootstrap.high],\n pValue: tTest.p,\n n: pairedBaseline.length,\n cohensD: d,\n mde,\n requiredN,\n }\n}\n\nfunction pairingKey(r: RunRecord): string {\n return `${r.experimentId}::${r.seed}`\n}\n\nfunction mean(arr: number[]): number {\n return arr.length === 0 ? 0 : arr.reduce((s, v) => s + v, 0) / arr.length\n}\n\n// ── Failure clustering ──────────────────────────────────────────────\n\nasync function computeFailureClusters(\n runs: RunRecord[],\n analyst: AnalystRegistry,\n split: 'search' | 'holdout',\n): Promise<FailureClusterInsight | undefined> {\n const failed = runs.filter((r) => compositeOf(r, split) < 0.5 || r.failureMode !== undefined)\n if (failed.length === 0) return { clusters: [], totalFailures: 0 }\n\n const clusters = new Map<string, { exemplars: string[]; share: number }>()\n for (const run of failed) {\n try {\n const result = await analyst.run(run.runId, {\n kind: 'run-record',\n run,\n } as Parameters<typeof analyst.run>[1])\n for (const finding of result.findings as AnalystFinding[]) {\n const key = finding.area || finding.analyst_id || 'unclassified'\n const c = clusters.get(key) ?? { exemplars: [], share: 0 }\n if (c.exemplars.length < 5) c.exemplars.push(run.runId)\n clusters.set(key, c)\n }\n } catch {\n const c = clusters.get('analyst-error') ?? { exemplars: [], share: 0 }\n if (c.exemplars.length < 5) c.exemplars.push(run.runId)\n clusters.set('analyst-error', c)\n }\n }\n const clusterList = [...clusters.entries()].map(([id, c]) => ({\n id,\n name: id,\n share: c.exemplars.length / failed.length,\n exemplars: c.exemplars,\n }))\n clusterList.sort((a, b) => b.share - a.share)\n return { clusters: clusterList, totalFailures: failed.length }\n}\n\n// ── Contamination ──────────────────────────────────────────────────\n\nfunction computeContamination(\n runs: RunRecord[],\n canaries: DatasetScenario[],\n): InsightReport['contamination'] {\n let leaks = 0\n const details: Array<{ runId: string; canary: string; matched: string }> = []\n for (const run of runs) {\n const output = stringifyOutput(run)\n if (!output) continue\n const leaksHere = checkCanaries(output, canaries)\n for (const leak of leaksHere) {\n leaks++\n details.push({ runId: run.runId, canary: leak.canary, matched: leak.evidence })\n }\n }\n return { leaks, holdoutAuditPassed: leaks === 0, details }\n}\n\nfunction stringifyOutput(run: RunRecord): string | undefined {\n // RunRecord doesn't fix where \"the agent's output\" lives — different\n // consumers stash it differently. We probe the common shapes: the\n // outcome.raw map (numeric only by design — unlikely to contain text),\n // and any string-valued fields tucked under metadata via type casting.\n // Consumers with bespoke shapes pass canaryScenarios only when they\n // know their runs carry a stringifiable surface.\n const metadata = (run as unknown as { metadata?: Record<string, unknown> }).metadata\n if (typeof metadata?.output === 'string') return metadata.output\n if (typeof metadata?.text === 'string') return metadata.text\n return undefined\n}\n\n// ── Outcome correlation + linear reward model ──────────────────────\n\nfunction computeOutcomeCorrelation(\n runs: RunRecord[],\n outcome: { metric: string; valueByRunId: Record<string, number> },\n split: 'search' | 'holdout',\n): OutcomeCorrelationInsight | undefined {\n const xs: number[] = []\n const ys: number[] = []\n for (const run of runs) {\n const y = outcome.valueByRunId[run.runId]\n if (y === undefined || !Number.isFinite(y)) continue\n const x = compositeOf(run, split)\n if (!Number.isFinite(x)) continue\n xs.push(x)\n ys.push(y)\n }\n if (xs.length < 3) return undefined\n\n const p = pearson(xs, ys)\n const s = spearman(xs, ys)\n const meanX = mean(xs)\n const meanY = mean(ys)\n let num = 0\n let denom = 0\n for (let i = 0; i < xs.length; i++) {\n num += (xs[i]! - meanX) * (ys[i]! - meanY)\n denom += (xs[i]! - meanX) ** 2\n }\n const slope = denom === 0 ? 0 : num / denom\n const intercept = meanY - slope * meanX\n const ssTot = ys.reduce((a, y) => a + (y - meanY) ** 2, 0)\n const ssRes = ys.reduce((a, y, i) => a + (y - (intercept + slope * xs[i]!)) ** 2, 0)\n const r2 = ssTot === 0 ? 0 : 1 - ssRes / ssTot\n\n return {\n metric: outcome.metric,\n n: xs.length,\n pearson: p,\n spearman: s,\n rewardModel: { intercept, slope, r2 },\n }\n}\n\nfunction spearman(a: number[], b: number[]): number {\n if (a.length !== b.length || a.length === 0) return 0\n return pearson(rank(a), rank(b))\n}\n\nfunction rank(arr: number[]): number[] {\n const indexed = arr.map((v, i) => ({ v, i }))\n indexed.sort((x, y) => x.v - y.v)\n const ranks = new Array(arr.length).fill(0)\n let i = 0\n while (i < indexed.length) {\n let j = i\n while (j + 1 < indexed.length && indexed[j + 1]!.v === indexed[i]!.v) j++\n const avg = (i + j + 2) / 2\n for (let k = i; k <= j; k++) ranks[indexed[k]!.i] = avg\n i = j + 1\n }\n return ranks\n}\n\n// ── Release confidence scorecard ───────────────────────────────────\n\nfunction buildReleaseScorecard(\n composite: ScalarDistribution,\n lift: LiftInsight | undefined,\n contamination: InsightReport['contamination'],\n): InsightReport['release'] {\n // Synthesise a minimal scorecard from the rolled-up signal. The\n // substrate's `evaluateReleaseConfidence` primitive consumes a richer\n // input shape that callers can produce by wiring SLO definitions; the\n // shape here is the contract `selfImprove`/`analyzeRuns` consumers\n // receive automatically. They can call `evaluateReleaseConfidence`\n // directly when they want SLO-based axis evaluation.\n const axes: InsightReport['release']['axes'] = []\n const liftPass =\n lift === undefined || lift.ci95[0] > 0\n ? ('pass' as const)\n : lift.delta > 0\n ? ('warn' as const)\n : ('fail' as const)\n axes.push({\n name: 'quality-lift',\n status: liftPass,\n detail: lift\n ? `delta=${lift.delta.toFixed(3)}, CI95=[${lift.ci95[0].toFixed(3)}, ${lift.ci95[1].toFixed(3)}], n=${lift.n}`\n : 'no baseline/candidate pair available',\n })\n const contamPass =\n contamination === undefined || contamination.leaks === 0 ? ('pass' as const) : ('fail' as const)\n axes.push({\n name: 'contamination',\n status: contamPass,\n detail: contamination ? `${contamination.leaks} canary leak(s)` : 'no canaries supplied',\n })\n axes.push({\n name: 'composite-distribution',\n status: composite.mean >= 0.5 ? 'pass' : composite.mean >= 0.3 ? 'warn' : 'fail',\n detail: `mean=${composite.mean.toFixed(3)}, p50=${composite.p50.toFixed(3)}, p95=${composite.p95.toFixed(3)} over n=${composite.n}`,\n })\n const status = axes.some((a) => a.status === 'fail')\n ? 'fail'\n : axes.some((a) => a.status === 'warn')\n ? 'warn'\n : 'pass'\n return {\n status,\n axes,\n issues: [],\n }\n}\n\n// ── Recommendations engine ─────────────────────────────────────────\n\ninterface RecommendationContext {\n composite: ScalarDistribution\n judges: Record<string, JudgeInsight>\n interRater?: InterRaterInsight\n lift?: LiftInsight\n failureClusters?: FailureClusterInsight\n contamination?: InsightReport['contamination']\n outcomeCorrelation?: OutcomeCorrelationInsight\n threshold: number\n}\n\nfunction buildRecommendations(ctx: RecommendationContext): Recommendation[] {\n const out: Recommendation[] = []\n\n if (ctx.lift) {\n const decisive = ctx.lift.ci95[0] > ctx.threshold\n const inconclusive = ctx.lift.ci95[0] <= ctx.threshold && ctx.lift.ci95[1] > ctx.threshold\n if (decisive) {\n out.push({\n priority: 'critical',\n kind: 'ship',\n title: `Ship — lift ${ctx.lift.delta.toFixed(3)} (95% CI ${ctx.lift.ci95[0].toFixed(3)}..${ctx.lift.ci95[1].toFixed(3)})`,\n detail: `Holdout lift exceeds threshold ${ctx.threshold} with 95% bootstrap confidence (n=${ctx.lift.n}, p=${ctx.lift.pValue.toFixed(4)}, d=${ctx.lift.cohensD.toFixed(2)}).`,\n evidencePath: 'lift',\n })\n } else if (inconclusive) {\n out.push({\n priority: 'high',\n kind: 'expand-corpus',\n title: `Inconclusive — need ~${ctx.lift.requiredN} paired runs (have ${ctx.lift.n}) at current effect size`,\n detail: `CI straddles threshold. Current MDE at 80% power is ${ctx.lift.mde.toFixed(3)}; observed delta is ${ctx.lift.delta.toFixed(3)}.`,\n evidencePath: 'lift',\n })\n } else {\n out.push({\n priority: 'critical',\n kind: 'hold',\n title: `Hold — lift CI lower bound ${ctx.lift.ci95[0].toFixed(3)} is at or below threshold ${ctx.threshold}`,\n detail: `Bootstrap CI provides no statistical evidence the candidate is better. Consider tightening the mutation or expanding the holdout.`,\n evidencePath: 'lift',\n })\n }\n }\n\n if (ctx.contamination && ctx.contamination.leaks > 0) {\n out.push({\n priority: 'critical',\n kind: 'fix',\n title: `${ctx.contamination.leaks} canary leak${ctx.contamination.leaks === 1 ? '' : 's'} detected`,\n detail: `Holdout integrity is compromised. The lift number is unreliable until you investigate.`,\n evidencePath: 'contamination',\n })\n }\n\n if (ctx.interRater && ctx.interRater.kappa < 0.5) {\n out.push({\n priority: 'high',\n kind: 'recalibrate',\n title: `Inter-rater agreement κ=${ctx.interRater.kappa.toFixed(2)} is below 0.5`,\n detail: `Raters disagree on what 'good' looks like. Top disagreement cases listed in interRater.disagreementCases — consider a triage meeting or refining the rubric.`,\n evidencePath: 'interRater',\n })\n }\n\n if (ctx.failureClusters && ctx.failureClusters.clusters.length > 0) {\n const top = ctx.failureClusters.clusters[0]!\n out.push({\n priority: 'high',\n kind: 'investigate',\n title: `Top failure cluster: ${top.name} (${(top.share * 100).toFixed(0)}% of failures)`,\n detail: `${ctx.failureClusters.totalFailures} runs failed. The largest cluster groups ${top.exemplars.length} exemplars under '${top.name}'.`,\n evidencePath: 'failureClusters.clusters[0]',\n })\n }\n\n if (ctx.outcomeCorrelation && Math.abs(ctx.outcomeCorrelation.spearman) < 0.3) {\n out.push({\n priority: 'medium',\n kind: 'recalibrate',\n title: `Judge scores decoupled from ${ctx.outcomeCorrelation.metric} (Spearman ρ=${ctx.outcomeCorrelation.spearman.toFixed(2)})`,\n detail: `Your judges score what they were trained to score, but it isn't predicting downstream ${ctx.outcomeCorrelation.metric}. Consider retraining the judge against ${ctx.outcomeCorrelation.metric} as the gold signal.`,\n evidencePath: 'outcomeCorrelation',\n })\n }\n\n return out\n}\n\n// ── Re-export pareto figure spec for hosted-side rendering ─────────\n\nexport type { ParetoFigureSpec }\n","/**\n * # `selfImprove()` — the LAND-tier one-shot.\n *\n * The cheapest possible call site to run a real closed-loop self-\n * improvement over your agent. Wraps `runImprovementLoop` with smart\n * defaults and a budget-shaped options API; every escape hatch the\n * substrate exposes is reachable from here without losing the\n * one-function feel.\n *\n * Defaults picked to match the LAND-tier story:\n * - In-memory storage (no filesystem touch).\n * - `gepaDriver` reflective mutation with copywriting-flavored primitives\n * (override `driver` or `mutationPrimitives` for any domain).\n * - `defaultProductionGate` with `deltaThreshold: 0.05`.\n * - Held-out split = 25% of scenarios, deterministic by id hash.\n * - 3 generations × population 2 (raise via `budget` for more search).\n * - `autoOnPromote: 'none'` (we don't open PRs unless you ask).\n *\n * Want one-click? Provide `agent` + `scenarios` + `judge`. Done.\n * Want distributed? Pass `cellPlacement` + an `httpDispatch`-backed\n * agent. Want a code-tier surface? Pass a `MutableSurface` + your own\n * `driver`. Same function.\n */\n\nimport { gepaDriver } from '../campaign/drivers/gepa'\nimport { defaultProductionGate } from '../campaign/gates/default-production-gate'\nimport {\n type RunImprovementLoopResult,\n runImprovementLoop,\n} from '../campaign/presets/run-improvement-loop'\nimport { type CampaignStorage, inMemoryCampaignStorage } from '../campaign/storage'\nimport type {\n CampaignCellResult,\n DispatchContext,\n Gate,\n ImprovementDriver,\n JudgeConfig,\n MutableSurface,\n Scenario,\n} from '../campaign/types'\nimport { createHostedClient, type HostedTenant } from '../hosted/client'\nimport type { EvalRunCellScore, EvalRunEvent, EvalRunGenerationSnapshot } from '../hosted/types'\nimport type { JudgeScoresRecord, RunRecord } from '../run-record'\nimport { analyzeRuns } from './analyze-runs'\nimport type { InsightReport } from './insight-report'\n\nexport interface SelfImproveBudget {\n /** Hard $ ceiling across all cells in baseline + every generation. Cells\n * beyond the ceiling are skipped (cost-aware, not aborted). */\n dollars?: number\n /** How many improvement generations to explore. Default 3. Set 0 to\n * skip improvement entirely (selfImprove becomes a baseline-only run). */\n generations?: number\n /** Candidates the driver proposes per generation. Default 2. */\n populationSize?: number\n /** Max concurrent cells across the loop. Default 2. */\n maxConcurrency?: number\n /** Fraction of `scenarios` held out from training, used for the gate.\n * Default 0.25. Ignored when `holdoutScenarios` is set explicitly. */\n holdoutFraction?: number\n /** Explicit held-out scenarios; overrides `holdoutFraction`. */\n holdoutScenarios?: Scenario[]\n}\n\nexport interface SelfImproveLlm {\n /** Endpoint base URL. Default Tangle Router. */\n baseUrl?: string\n /** Bearer token. Default `process.env.OPENAI_API_KEY`. */\n apiKey?: string\n /** Model id used by `gepaDriver` reflection. Default\n * `anthropic/claude-sonnet-4.6`. */\n model?: string\n}\n\nexport type SelfImproveProgressEvent =\n | { kind: 'baseline.started'; scenarios: number }\n | { kind: 'baseline.completed'; compositeMean: number; durationMs: number }\n | { kind: 'generation.started'; index: number; populationSize: number }\n | { kind: 'generation.completed'; index: number; bestComposite: number; durationMs: number }\n | { kind: 'gate.decided'; decision: string; lift: number }\n\nexport interface SelfImproveOptions<TScenario extends Scenario, TArtifact> {\n /**\n * Your agent — a function that takes the current `MutableSurface`\n * (typically a system prompt the loop is optimizing) plus the\n * scenario + cell ctx, and returns the artifact your judge scores.\n *\n * Same shape as `RunOptimizationOptions.dispatchWithSurface`. Wrap a\n * plain `Dispatch` if you don't have a surface seam:\n *\n * agent: (_surface, scenario, ctx) => yourPlainDispatch(scenario, ctx)\n *\n * That mode evaluates without mutating any surface — useful as a\n * baseline-only run (set `budget.generations = 0`).\n */\n agent: (surface: MutableSurface, scenario: TScenario, ctx: DispatchContext) => Promise<TArtifact>\n\n /** Scenarios to evaluate against. Train/holdout split is computed from\n * these unless `budget.holdoutScenarios` is set explicitly. */\n scenarios: TScenario[]\n\n /** Judge that scores artifacts. Bring your own; use `langchainJudge`\n * from `/adapters/langchain` for a Runnable-shaped one. */\n judge: JudgeConfig<TArtifact, TScenario>\n\n /** Starting surface — system prompt, JSON config, anything `MutableSurface`\n * accepts. The driver mutates this each generation. */\n baselineSurface: MutableSurface\n\n /** Budget + loop shape. All fields optional; defaults pick the LAND-tier\n * story. */\n budget?: SelfImproveBudget\n\n /** Custom driver. Default is `gepaDriver` configured from `llm` +\n * `mutationPrimitives`. */\n driver?: ImprovementDriver\n\n /** Default-driver overrides — used when `driver` is unset. */\n mutationPrimitives?: string[]\n driverTarget?: string\n\n /** Custom gate. Default is `defaultProductionGate` with\n * `deltaThreshold: 0.05` on the held-out split. */\n gate?: Gate<TArtifact, TScenario>\n\n /** LLM config consumed by the default `gepaDriver`. Ignored if you pass\n * your own `driver`. */\n llm?: SelfImproveLlm\n\n /** Storage backend. Default `inMemoryCampaignStorage()` — nothing\n * persists past the call. Pass `fsCampaignStorage()` to write to disk. */\n storage?: CampaignStorage\n\n /** Run directory (logical for in-memory storage, real path for fs).\n * Default `mem://selfImprove-<timestamp>`. */\n runDir?: string\n\n /** Distributed-driver seam — same as `RunCampaignOptions.cellPlacement`.\n * Returns an opaque placement key the substrate forwards to your agent\n * as `ctx.placement`. Combined with `httpDispatch` from\n * `/adapters/http`, fans cells across regions. */\n cellPlacement?: (input: {\n scenario: TScenario\n rep: number\n generation?: number\n }) => string | undefined\n\n /** Streaming hook — fires on baseline + each generation + gate decision.\n * Consumer routes events wherever (UI, dashboard, logs). */\n onProgress?: (event: SelfImproveProgressEvent) => void\n\n /** Auto-promotion behavior on a ship decision. Default `'none'` — we\n * return the winner; you ship it however you ship. `'pr'` opens a\n * GitHub PR via `openAutoPr`; requires `ghOwner` + `ghRepo`. */\n autoOnPromote?: 'pr' | 'none'\n ghOwner?: string\n ghRepo?: string\n\n /**\n * Opt-in: ship eval-run events to a hosted orchestrator (ours, your\n * self-hosted one, or any compatible implementation of the\n * `docs/hosted-ingest-spec.md` wire format). When set, the substrate\n * POSTs the final `EvalRunEvent` to `${endpoint}/v1/ingest/eval-runs`\n * after the loop completes. Failures are logged but do not fail the\n * loop — local result is always returned.\n *\n * For our orchestrator: `{ endpoint: 'https://orchestrator.tangle.tools/v1', apiKey, tenantId }`.\n *\n * For your self-hosted: any URL serving the wire format. See\n * `examples/hosted-ingest-server/` for the reference receiver.\n */\n hostedTenant?: HostedTenant\n\n /** Free-form labels attached to the hosted event (env, branch, model id,\n * etc.). Ignored when `hostedTenant` is unset. */\n hostedLabels?: Record<string, string>\n}\n\nexport interface SelfImproveResult<TScenario extends Scenario, TArtifact> {\n /** Composite mean across all scenarios, baseline run. */\n baseline: {\n compositeMean: number\n perScenario: Record<string, number>\n }\n /** Composite mean on the held-out set, winner run. */\n winner: {\n compositeMean: number\n perScenario: Record<string, number>\n surface: MutableSurface\n }\n /** `winner.compositeMean - baselineOnHoldout.compositeMean`. Positive\n * means the gate observed improvement. */\n lift: number\n /** `defaultProductionGate.decide()` result. */\n gateDecision: 'ship' | 'hold' | 'need_more_work' | 'model_ceiling' | 'arch_ceiling'\n /** Number of generations actually explored (may be less than the\n * budget if the driver gave up early). */\n generationsExplored: number\n /** Wall-clock total. */\n durationMs: number\n /** Total cost across baseline + every generation. */\n totalCostUsd: number\n /**\n * Rigor packet: distributional summary, paired-bootstrap lift CI,\n * judge stats, contamination check, recommendations. Wired through\n * `analyzeRuns()` on the baseline + winner cells of the campaign.\n * Hosted-tier dashboards render this as the v3-vs-v4 decision view.\n */\n insight: InsightReport\n /**\n * Raw substrate result for advanced inspection — full per-generation\n * candidates, full campaign artifacts, all judge scores. Useful for\n * debugging or reporting beyond the summary.\n */\n raw: RunImprovementLoopResult<TArtifact, TScenario>\n}\n\n/**\n * Deterministic train/holdout split by a stable hash of `scenario.id`,\n * so the same scenario set always splits the same way across runs.\n */\nfunction splitTrainHoldout<TScenario extends Scenario>(\n scenarios: TScenario[],\n fraction: number,\n): { train: TScenario[]; holdout: TScenario[] } {\n // Stable fnv-1a-ish hash of the id for ordering.\n function hash(s: string): number {\n let h = 2166136261 >>> 0\n for (let i = 0; i < s.length; i++) {\n h ^= s.charCodeAt(i)\n h = Math.imul(h, 16777619) >>> 0\n }\n return h\n }\n const sorted = [...scenarios].sort((a, b) => hash(a.id) - hash(b.id))\n const nHoldout = Math.max(1, Math.min(sorted.length - 1, Math.round(sorted.length * fraction)))\n return {\n holdout: sorted.slice(0, nHoldout),\n train: sorted.slice(nHoldout),\n }\n}\n\nfunction meanComposite(byScenario: Record<string, { meanComposite: number }>): {\n compositeMean: number\n perScenario: Record<string, number>\n} {\n const perScenario: Record<string, number> = {}\n const values: number[] = []\n for (const [id, agg] of Object.entries(byScenario)) {\n perScenario[id] = agg.meanComposite\n values.push(agg.meanComposite)\n }\n return {\n compositeMean: values.length === 0 ? 0 : values.reduce((s, v) => s + v, 0) / values.length,\n perScenario,\n }\n}\n\nconst DEFAULT_MUTATION_PRIMITIVES = [\n 'Tighten the hook: lead with the specific user outcome.',\n 'Replace generic adjectives with specific verbs or proof numbers.',\n \"Anchor every claim in something the scenario's brief literally supports.\",\n 'Honor the surface-shape constraint (length, register, audience vocabulary).',\n]\n\n/**\n * One-shot self-improvement loop. See module docstring for defaults +\n * extension points.\n *\n * @example Minimum (LAND tier):\n *\n * const result = await selfImprove({\n * agent: (surface, scenario, ctx) => myAgent(surface, scenario, ctx.signal),\n * scenarios,\n * judge,\n * baselineSurface: DEFAULT_PROMPT,\n * })\n * console.log(`lift: ${result.lift.toFixed(3)} (${result.gateDecision})`)\n *\n * @example Distributed (workers in three regions):\n *\n * await selfImprove({\n * agent: httpDispatch({ resolveUrl: ({ placement }) => REGION_URLS[placement!] }),\n * scenarios,\n * judge,\n * baselineSurface: DEFAULT_PROMPT,\n * cellPlacement: ({ scenario }) => scenario.region,\n * budget: { maxConcurrency: 12 },\n * })\n */\nexport async function selfImprove<TScenario extends Scenario, TArtifact>(\n opts: SelfImproveOptions<TScenario, TArtifact>,\n): Promise<SelfImproveResult<TScenario, TArtifact>> {\n const startedAt = Date.now()\n\n const budget = opts.budget ?? {}\n const generations = budget.generations ?? 3\n const populationSize = budget.populationSize ?? 2\n const maxConcurrency = budget.maxConcurrency ?? 2\n const holdoutFraction = budget.holdoutFraction ?? 0.25\n const costCeiling = budget.dollars\n\n const explicitHoldout = budget.holdoutScenarios\n const { train, holdout } = explicitHoldout\n ? {\n train: opts.scenarios.filter((s) => !explicitHoldout.some((h) => h.id === s.id)),\n holdout: explicitHoldout as TScenario[],\n }\n : splitTrainHoldout(opts.scenarios, holdoutFraction)\n\n if (train.length === 0) {\n throw new Error(\n 'selfImprove: train split is empty. Reduce holdoutFraction or pass more scenarios.',\n )\n }\n if (holdout.length === 0) {\n throw new Error('selfImprove: holdout split is empty. Pass more scenarios.')\n }\n\n const driver: ImprovementDriver =\n opts.driver ??\n gepaDriver({\n llm: {\n baseUrl: opts.llm?.baseUrl ?? 'https://router.tangle.tools/v1',\n apiKey: opts.llm?.apiKey ?? process.env.OPENAI_API_KEY ?? '',\n },\n model: opts.llm?.model ?? 'anthropic/claude-sonnet-4.6',\n target:\n opts.driverTarget ??\n 'agent surface (system prompt or config) being optimized by selfImprove',\n mutationPrimitives: opts.mutationPrimitives ?? DEFAULT_MUTATION_PRIMITIVES,\n })\n\n const gate: Gate<TArtifact, TScenario> =\n opts.gate ??\n defaultProductionGate<TArtifact, TScenario>({\n holdoutScenarios: holdout,\n deltaThreshold: 0.05,\n })\n\n const storage = opts.storage ?? inMemoryCampaignStorage()\n const runDir = opts.runDir ?? `mem://selfImprove-${startedAt}`\n\n if (opts.onProgress) {\n opts.onProgress({ kind: 'baseline.started', scenarios: opts.scenarios.length })\n }\n\n const result = await runImprovementLoop<TScenario, TArtifact>({\n scenarios: train,\n baselineSurface: opts.baselineSurface,\n dispatchWithSurface: opts.agent,\n driver,\n judges: [opts.judge],\n populationSize,\n maxGenerations: generations,\n holdoutScenarios: holdout,\n gate,\n autoOnPromote: opts.autoOnPromote ?? 'none',\n ghOwner: opts.ghOwner,\n ghRepo: opts.ghRepo,\n storage,\n runDir,\n maxConcurrency,\n cellPlacement: opts.cellPlacement,\n costCeiling,\n })\n\n const baseline = meanComposite(result.baselineOnHoldout.aggregates.byScenario)\n const winnerStats = meanComposite(result.winnerOnHoldout.aggregates.byScenario)\n\n if (opts.onProgress) {\n opts.onProgress({\n kind: 'baseline.completed',\n compositeMean: baseline.compositeMean,\n durationMs: Date.now() - startedAt,\n })\n opts.onProgress({\n kind: 'gate.decided',\n decision: result.gateResult.decision,\n lift: winnerStats.compositeMean - baseline.compositeMean,\n })\n }\n\n const totalCost =\n result.baselineCampaign.aggregates.totalCostUsd +\n result.generations.reduce(\n (sum, gen) =>\n sum + gen.surfaces.reduce((s, sf) => s + sf.campaign.aggregates.totalCostUsd, 0),\n 0,\n )\n\n // Rigor packet: feed baseline + winner cells through analyzeRuns().\n // The two candidates (`baseline` / `winner`) give the lift section a\n // clean paired comparison; per-judge / per-dimension / cost-quality\n // sections populate from the cells' judgeScores.\n const insight = await analyzeRuns({\n runs: [\n ...cellsToRunRecords(result.baselineCampaign.cells, 'baseline', runDir),\n ...cellsToRunRecords(result.winnerOnHoldout.cells, 'winner', runDir),\n ],\n baselineCandidateId: 'baseline',\n candidateCandidateId: 'winner',\n })\n\n const summary: SelfImproveResult<TScenario, TArtifact> = {\n baseline,\n winner: {\n ...winnerStats,\n surface: result.winnerSurface,\n },\n lift: winnerStats.compositeMean - baseline.compositeMean,\n gateDecision: result.gateResult.decision,\n generationsExplored: result.generations.length,\n durationMs: Date.now() - startedAt,\n totalCostUsd: totalCost,\n insight,\n raw: result,\n }\n\n // Opt-in hosted ingest. Failures logged but never fail the loop — the\n // local result is always returned. This matches the wedge-doc invariant\n // that LAND-tier never blocks on EXPAND-tier infra.\n if (opts.hostedTenant) {\n try {\n await shipEvalRunToHosted(opts.hostedTenant, opts, summary, result, runDir)\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n // eslint-disable-next-line no-console -- intentional: hosted-ingest is best-effort\n console.warn(`[agent-eval] hosted ingest failed (continuing): ${msg}`)\n }\n }\n\n return summary\n}\n\nasync function shipEvalRunToHosted<TScenario extends Scenario, TArtifact>(\n tenant: HostedTenant,\n opts: SelfImproveOptions<TScenario, TArtifact>,\n summary: SelfImproveResult<TScenario, TArtifact>,\n raw: RunImprovementLoopResult<TArtifact, TScenario>,\n runDir: string,\n): Promise<void> {\n const client = createHostedClient(tenant)\n\n function snapshotFromCampaign(\n index: number,\n surface: MutableSurface | undefined,\n campaign: RunImprovementLoopResult<TArtifact, TScenario>['baselineCampaign'],\n durationMs: number,\n ): EvalRunGenerationSnapshot {\n const cells: EvalRunCellScore[] = campaign.cells.map((cell) => {\n const judgeScores = Object.values(cell.judgeScores)\n const composite =\n judgeScores.length === 0\n ? 0\n : judgeScores.reduce((s, j) => s + j.composite, 0) / judgeScores.length\n return {\n scenarioId: cell.scenarioId,\n rep: cell.rep,\n compositeMean: composite,\n dimensions: Object.fromEntries(\n Object.entries(cell.judgeScores).map(([name, score]) => [name, score.dimensions]),\n ),\n errorMessage: cell.error ?? undefined,\n }\n })\n const compositeMean =\n cells.length === 0 ? 0 : cells.reduce((s, c) => s + c.compositeMean, 0) / cells.length\n return {\n index,\n surfaceHash:\n typeof surface === 'string'\n ? hashString(surface)\n : hashString(JSON.stringify(surface ?? '')),\n surface,\n cells,\n compositeMean,\n costUsd: campaign.aggregates.totalCostUsd,\n durationMs,\n }\n }\n\n const generations: EvalRunGenerationSnapshot[] = []\n // Baseline as generation 0.\n generations.push(snapshotFromCampaign(0, opts.baselineSurface, raw.baselineCampaign, 0))\n // Improvement generations as 1..N. Substrate stores per-surface campaigns\n // per generation — we summarize the WINNING surface per generation here.\n for (const gen of raw.generations) {\n const winner = gen.surfaces.reduce(\n (best, s) =>\n s.campaign.aggregates.cellsExecuted > 0 &&\n (best === undefined || averageComposite(s.campaign) > averageComposite(best.campaign))\n ? s\n : best,\n gen.surfaces[0],\n )\n if (!winner) continue\n generations.push(\n snapshotFromCampaign(gen.record.generationIndex + 1, winner.surface, winner.campaign, 0),\n )\n }\n\n const event: EvalRunEvent = {\n runId: `${runDir}#${Date.now()}`,\n runDir,\n timestamp: new Date().toISOString(),\n status: 'finished',\n labels: opts.hostedLabels ?? {},\n baseline: generations[0],\n generations,\n gateDecision: summary.gateDecision,\n holdoutLift: summary.lift,\n totalCostUsd: summary.totalCostUsd,\n totalDurationMs: summary.durationMs,\n insightReport: summary.insight,\n }\n\n await client.ingestEvalRun(event)\n}\n\nfunction averageComposite(\n campaign: RunImprovementLoopResult<unknown, Scenario>['baselineCampaign'],\n): number {\n const aggs = Object.values(campaign.aggregates.byScenario)\n return aggs.length === 0 ? 0 : aggs.reduce((s, a) => s + a.meanComposite, 0) / aggs.length\n}\n\nfunction hashString(s: string): string {\n let h = 2166136261 >>> 0\n for (let i = 0; i < s.length; i++) {\n h ^= s.charCodeAt(i)\n h = Math.imul(h, 16777619) >>> 0\n }\n return h.toString(16).padStart(8, '0')\n}\n\n/**\n * Adapt campaign cells into the `RunRecord` shape `analyzeRuns()` consumes.\n * Each cell becomes one run; `candidateId` is the caller-supplied label so\n * baseline + winner pair cleanly on `(experimentId, seed)`.\n */\nfunction cellsToRunRecords<TArtifact>(\n cells: ReadonlyArray<CampaignCellResult<TArtifact>>,\n candidateId: 'baseline' | 'winner',\n runId: string,\n): RunRecord[] {\n return cells.map((cell) => {\n const perJudge: Record<string, Record<string, number>> = {}\n const perDimMeanAccum: Record<string, { sum: number; n: number }> = {}\n let compositeSum = 0\n let compositeCount = 0\n for (const [judgeId, score] of Object.entries(cell.judgeScores)) {\n perJudge[judgeId] = { ...score.dimensions }\n for (const [dim, value] of Object.entries(score.dimensions)) {\n if (!Number.isFinite(value)) continue\n const accum = perDimMeanAccum[dim] ?? { sum: 0, n: 0 }\n accum.sum += value\n accum.n += 1\n perDimMeanAccum[dim] = accum\n }\n if (Number.isFinite(score.composite)) {\n compositeSum += score.composite\n compositeCount += 1\n }\n }\n const perDimMean: Record<string, number> = {}\n for (const [dim, { sum, n }] of Object.entries(perDimMeanAccum)) {\n perDimMean[dim] = n === 0 ? 0 : sum / n\n }\n const composite = compositeCount === 0 ? 0 : compositeSum / compositeCount\n const judgeScores: JudgeScoresRecord = {\n perJudge,\n perDimMean,\n composite,\n }\n return {\n runId: `${runId}::${candidateId}::${cell.cellId}`,\n experimentId: runId,\n candidateId,\n // Pair on (scenarioId, rep) — analyzeRuns pairs on (experimentId, seed).\n // Synthesize a stable seed for that pairing.\n seed:\n cell.rep * 1_000_000 +\n hashString(cell.scenarioId)\n .slice(0, 6)\n .split('')\n .reduce((a, c) => (a * 31 + c.charCodeAt(0)) >>> 0, 0),\n model: 'campaign-cell',\n promptHash: 'sha256:cell',\n configHash: 'sha256:cell',\n commitSha: 'cell',\n wallMs: cell.durationMs,\n costUsd: cell.costUsd,\n tokenUsage: { input: 0, output: 0 },\n outcome: {\n holdoutScore: composite,\n raw: {},\n judgeScores,\n },\n splitTag: 'holdout',\n ...(cell.error ? { failureMode: cell.error } : {}),\n } satisfies RunRecord\n })\n}\n","/**\n * # `intake/feedback-table` — multi-rater approve/reject corpus → `RunRecord[]`.\n *\n * The generic shape behind Obsidian's `#approved` / `#rejected` tags, a\n * Google Sheet, a Postgres `feedback` table, or any CSV with ratings.\n *\n * Caller supplies one row per (run, rater) tuple plus per-run metadata; the\n * adapter rolls them up into the substrate-canonical `RunRecord` shape so\n * `analyzeRuns({ runs, raterScores })` can produce inter-rater agreement,\n * disagreement triage, and downstream recommendations.\n *\n * Per-run `RunRecord.outcome.searchScore` is the rater-mean rating\n * (normalised to 0..1 when scale is supplied); `outcome.raw` carries the\n * per-rater scores keyed by rater id for downstream attribution.\n */\n\nimport type { JudgeScoresRecord, RunOutcome, RunRecord, RunSplitTag } from '../../run-record'\n\nexport interface FeedbackTableRow {\n /** Stable id for this run — the unit a rater scored. Drives pairing\n * across analysis primitives. */\n runId: string\n /** Identifier of the rater that produced this rating. */\n rater: string\n /** The rating itself. Accepts boolean (approve/reject), 0..1 scalar,\n * or any numeric scale — see `scale`. */\n rating: number | boolean\n /** Optional metadata carried through to `RunRecord.outcome.raw` and the\n * custom-shape metadata bag. */\n metadata?: Record<string, unknown>\n}\n\nexport interface FeedbackTableMeta {\n runId: string\n /** When omitted, defaults to `'feedback-corpus'`. Used to group related\n * runs in `analyzeRuns()` lift analysis. */\n experimentId?: string\n /** When omitted, defaults to `runId` — each run is its own candidate. */\n candidateId?: string\n /** Cost in USD, when available. Set to 0 when unknown — the consumer's\n * cost analysis sections will collapse gracefully. */\n costUsd?: number\n /** Wall-clock ms, when available. Defaults to 0. */\n wallMs?: number\n /** Model identifier including snapshot. Default `unknown@unknown`. */\n model?: string\n /** Optional sha256 of the prompt; default `'sha256:unknown'`. */\n promptHash?: string\n /** Default `'sha256:unknown'`. */\n configHash?: string\n /** Default `'unknown'`. */\n commitSha?: string\n /** Default `'holdout'` — feedback corpora are by nature the holdout\n * signal a closed-loop improvement aims at. */\n splitTag?: RunSplitTag\n /** Free-form metadata available to consumers via the cast-out path on\n * the resulting RunRecord. */\n extras?: Record<string, unknown>\n}\n\nexport interface FromFeedbackTableOptions {\n /** Per-(run, rater) ratings. */\n ratings: FeedbackTableRow[]\n /** Per-run metadata. When a runId appears in `ratings` but not here, the\n * adapter synthesises minimal metadata with defaults documented above. */\n meta?: FeedbackTableMeta[]\n /** Rating scale. Provide `{ min, max }` for non-0..1 numeric scales.\n * Booleans are normalised: true → 1, false → 0. Default: assumes\n * ratings are already 0..1. */\n scale?: { min: number; max: number }\n /** When true, the rater scores are emitted into `raterScores` (a sibling\n * array `analyzeRuns()` accepts) instead of being averaged into the\n * run's `outcome.searchScore`. Default `true` — preserves rater-level\n * signal for inter-rater analysis. */\n emitRaterScores?: boolean\n}\n\nexport interface FromFeedbackTableResult {\n runs: RunRecord[]\n /** Rater-level scores ready to pass into `analyzeRuns({ raterScores })`\n * for inter-rater agreement + disagreement triage. */\n raterScores: Array<{ runId: string; rater: string; score: number }>\n}\n\nexport function fromFeedbackTable(opts: FromFeedbackTableOptions): FromFeedbackTableResult {\n const { ratings, meta = [], scale, emitRaterScores = true } = opts\n const metaByRun = new Map(meta.map((m) => [m.runId, m]))\n\n // Normalise per-rating to a 0..1 score.\n const normalise = (rating: number | boolean): number => {\n if (typeof rating === 'boolean') return rating ? 1 : 0\n if (!Number.isFinite(rating)) return Number.NaN\n if (!scale) return rating\n const { min, max } = scale\n if (max === min) return rating\n return (rating - min) / (max - min)\n }\n\n // Group ratings by runId.\n const byRun = new Map<string, FeedbackTableRow[]>()\n for (const row of ratings) {\n const list = byRun.get(row.runId) ?? []\n list.push(row)\n byRun.set(row.runId, list)\n }\n\n const runs: RunRecord[] = []\n const raterScores: FromFeedbackTableResult['raterScores'] = []\n\n for (const [runId, rowsForRun] of byRun) {\n const normalised = rowsForRun\n .map((r) => ({ rater: r.rater, score: normalise(r.rating) }))\n .filter((r) => Number.isFinite(r.score))\n if (normalised.length === 0) continue\n\n const meanScore = normalised.reduce((s, r) => s + r.score, 0) / normalised.length\n\n const runMeta = metaByRun.get(runId) ?? ({ runId } as FeedbackTableMeta)\n\n const judgeScores: JudgeScoresRecord = {\n perJudge: Object.fromEntries(normalised.map((r) => [r.rater, { rating: r.score }])),\n perDimMean: { rating: meanScore },\n composite: meanScore,\n }\n\n const outcome: RunOutcome = {\n // Feedback corpora ARE the holdout signal — score lands on\n // `holdoutScore` so downstream substrate primitives (`paretoChart`,\n // promotion gates) read it correctly by default.\n holdoutScore: meanScore,\n raw: Object.fromEntries(normalised.map((r) => [`rater:${r.rater}`, r.score])),\n judgeScores,\n }\n\n runs.push({\n runId,\n experimentId: runMeta.experimentId ?? 'feedback-corpus',\n candidateId: runMeta.candidateId ?? runId,\n seed: 0,\n model: runMeta.model ?? 'unknown@unknown',\n promptHash: runMeta.promptHash ?? 'sha256:unknown',\n configHash: runMeta.configHash ?? 'sha256:unknown',\n commitSha: runMeta.commitSha ?? 'unknown',\n wallMs: runMeta.wallMs ?? 0,\n costUsd: runMeta.costUsd ?? 0,\n tokenUsage: { input: 0, output: 0 },\n outcome,\n splitTag: runMeta.splitTag ?? 'holdout',\n } as RunRecord)\n\n if (emitRaterScores) {\n for (const r of normalised) raterScores.push({ runId, rater: r.rater, score: r.score })\n }\n }\n\n return { runs, raterScores }\n}\n","/**\n * # `intake/otel-spans` — OTel `TraceSpanEvent[]` → `RunRecord[]`.\n *\n * Turns an existing observability stream into the substrate-canonical\n * `RunRecord` shape so consumers with logs but no eval discipline can\n * call `analyzeRuns()` against their production traffic immediately.\n *\n * Pivot rule: spans are grouped by `tangle.runId` (the same attribute the\n * hosted-tier wire format uses) or, when absent, by `traceId`. One group\n * becomes one `RunRecord`. The root span (no `parentSpanId`) supplies:\n *\n * - `runId` (the group key)\n * - `wallMs` from `endTimeUnixNano - startTimeUnixNano`\n * - `model` from `gen_ai.request.model` / `llm.model` / `tangle.model`\n * - cost from `cost.usd` / `gen_ai.usage.cost_usd` / `tangle.cost.usd`\n * - token usage from `gen_ai.usage.{input,output}_tokens`\n * - `outcome.searchScore` from `tangle.score` / `eval.score` when\n * present; `outcome.raw` collects every numeric attribute.\n *\n * Spans that ERRORed (`status.code === 'ERROR'`) populate `failureMode`\n * with their `name` so `analyzeRuns()`'s failure clustering sees them.\n */\n\nimport type { TraceSpanEvent } from '../../hosted/types'\nimport type {\n JudgeScoresRecord,\n RunOutcome,\n RunRecord,\n RunSplitTag,\n RunTokenUsage,\n} from '../../run-record'\n\nconst SCORE_KEYS = ['tangle.score', 'eval.score', 'score']\nconst MODEL_KEYS = ['tangle.model', 'gen_ai.request.model', 'llm.model', 'model']\nconst COST_KEYS = ['tangle.cost.usd', 'gen_ai.usage.cost_usd', 'cost.usd', 'cost']\nconst INPUT_TOKEN_KEYS = ['gen_ai.usage.input_tokens', 'tangle.tokens.in', 'tokens.in']\nconst OUTPUT_TOKEN_KEYS = ['gen_ai.usage.output_tokens', 'tangle.tokens.out', 'tokens.out']\nconst PROMPT_HASH_KEYS = ['tangle.prompt_hash', 'prompt.hash']\nconst CONFIG_HASH_KEYS = ['tangle.config_hash', 'config.hash']\n\nexport interface FromOtelSpansOptions {\n spans: TraceSpanEvent[]\n /** Default split tag for synthesized records. Defaults to `'holdout'`. */\n defaultSplit?: RunSplitTag\n /** Default `experimentId` when not present on any span. */\n experimentId?: string\n}\n\nexport function fromOtelSpans(opts: FromOtelSpansOptions): RunRecord[] {\n const { spans, defaultSplit = 'holdout', experimentId = 'otel-corpus' } = opts\n const grouped = groupSpans(spans)\n\n const runs: RunRecord[] = []\n for (const [groupKey, groupSpans] of grouped) {\n const root = findRoot(groupSpans)\n if (!root) continue\n\n const wallMs = Math.max(0, (root.endTimeUnixNano - root.startTimeUnixNano) / 1_000_000)\n const model = readAttrString(groupSpans, MODEL_KEYS) ?? 'unknown@unknown'\n const costUsd = readAttrNumber(groupSpans, COST_KEYS) ?? 0\n const inputTokens = readAttrNumber(groupSpans, INPUT_TOKEN_KEYS) ?? 0\n const outputTokens = readAttrNumber(groupSpans, OUTPUT_TOKEN_KEYS) ?? 0\n const promptHash = readAttrString(groupSpans, PROMPT_HASH_KEYS) ?? 'sha256:unknown'\n const configHash = readAttrString(groupSpans, CONFIG_HASH_KEYS) ?? 'sha256:unknown'\n const score = readAttrNumber(groupSpans, SCORE_KEYS)\n\n const rawNumeric = collectNumericAttrs(groupSpans)\n const tokenUsage: RunTokenUsage = {\n input: inputTokens,\n output: outputTokens,\n }\n\n const judgeScores: JudgeScoresRecord | undefined =\n score !== undefined\n ? {\n perJudge: { 'otel-derived': { score } },\n perDimMean: { score },\n composite: score,\n }\n : undefined\n\n const errorSpan = groupSpans.find((s) => s.status?.code === 'ERROR')\n const outcome: RunOutcome = {\n ...(opts.defaultSplit === 'search' ? { searchScore: score } : { holdoutScore: score }),\n raw: rawNumeric,\n ...(judgeScores ? { judgeScores } : {}),\n }\n\n runs.push({\n runId: groupKey,\n experimentId,\n candidateId: (root.attributes['tangle.candidateId'] as string | undefined) ?? 'otel-default',\n seed: 0,\n model,\n promptHash,\n configHash,\n commitSha: (root.attributes['tangle.commit_sha'] as string | undefined) ?? 'unknown',\n wallMs,\n costUsd,\n tokenUsage,\n outcome,\n splitTag: defaultSplit,\n ...(errorSpan ? { failureMode: errorSpan.name } : {}),\n } as RunRecord)\n }\n return runs\n}\n\n// ── Internal helpers ────────────────────────────────────────────────\n\nfunction groupSpans(spans: TraceSpanEvent[]): Map<string, TraceSpanEvent[]> {\n const m = new Map<string, TraceSpanEvent[]>()\n for (const span of spans) {\n const key = (span['tangle.runId'] as string | undefined) ?? span.traceId\n const list = m.get(key) ?? []\n list.push(span)\n m.set(key, list)\n }\n return m\n}\n\nfunction findRoot(group: TraceSpanEvent[]): TraceSpanEvent | undefined {\n return group.find((s) => !s.parentSpanId) ?? group[0]\n}\n\nfunction readAttrString(spans: TraceSpanEvent[], keys: string[]): string | undefined {\n for (const span of spans) {\n for (const key of keys) {\n const v = span.attributes[key]\n if (typeof v === 'string' && v.length > 0) return v\n }\n }\n return undefined\n}\n\nfunction readAttrNumber(spans: TraceSpanEvent[], keys: string[]): number | undefined {\n for (const span of spans) {\n for (const key of keys) {\n const v = span.attributes[key]\n if (typeof v === 'number' && Number.isFinite(v)) return v\n if (typeof v === 'string') {\n const parsed = Number(v)\n if (Number.isFinite(parsed)) return parsed\n }\n }\n }\n return undefined\n}\n\nfunction collectNumericAttrs(spans: TraceSpanEvent[]): Record<string, number> {\n const raw: Record<string, number> = {}\n for (const span of spans) {\n for (const [k, v] of Object.entries(span.attributes)) {\n if (typeof v === 'number' && Number.isFinite(v)) raw[k] = v\n }\n }\n return raw\n}\n"],"mappings":"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","names":["mean","i","groupSpans"]}
1
+ {"version":3,"sources":["../../src/contract/analyze-runs.ts","../../src/contract/self-improve.ts","../../src/contract/intake/feedback-table.ts","../../src/contract/intake/otel-spans.ts"],"sourcesContent":["/**\n * # `analyzeRuns()` — turn a set of agent runs into an actionable decision packet.\n *\n * Wires the substrate's statistical, calibration, clustering, Pareto, and\n * release-confidence primitives into one `InsightReport`. Two top-level\n * entry points use this function:\n *\n * - `selfImprove()` calls it on the campaign output to attach a packet\n * to every run.\n * - Consumers with observed `RunRecord[]` (production traces, gold\n * corpora, approve/reject tables) call it directly via `analyzeRuns()`\n * for analysis without a closed loop.\n *\n * Every section is opt-in based on what the input data supports — the\n * function never invents signal. If runs carry no judge scores, `judges`\n * is empty. If there's no baseline/candidate split, `lift` is undefined.\n * If no `analyst` is wired, `failureClusters` is undefined.\n *\n * The `recommendations` array is the human-readable layer; everything\n * else is the evidence backing each recommendation.\n */\n\nimport type { AnalystRegistry } from '../analyst/registry'\nimport type { AnalystFinding } from '../analyst/types'\nimport { checkCanaries } from '../contamination-guard'\nimport type { DatasetScenario } from '../dataset'\nimport type { RunRecord } from '../run-record'\nimport { cohensD, pairedBootstrap, pairedMde, pairedTTest, requiredSampleSize } from '../statistics'\nimport { type ParetoFigureSpec, paretoChart } from '../summary-report'\n\nimport type {\n FailureClusterInsight,\n InsightReport,\n InterRaterInsight,\n JudgeInsight,\n LiftInsight,\n OutcomeCorrelationInsight,\n Recommendation,\n ScalarDistribution,\n} from './insight-report'\n\n// ── Public API ───────────────────────────────────────────────────────\n\nexport interface AnalyzeRunsOptions {\n /** The runs to analyze. */\n runs: RunRecord[]\n /** Which split to score against when reading composite from RunOutcome.\n * Default: holdout when ANY run has a `holdoutScore`, else search. */\n split?: 'search' | 'holdout' | 'auto'\n /** Pairwise analysis configuration. When both `baselineCandidateId` and\n * `candidateCandidateId` are present, lift is computed on paired\n * (experimentId, seed) tuples shared between the two sides. */\n baselineCandidateId?: string\n candidateCandidateId?: string\n /** Canary scenarios — checked against every run's raw output for\n * holdout contamination. */\n canaryScenarios?: DatasetScenario[]\n /** Analyst registry for failure clustering. When omitted, the\n * `failureClusters` section is left undefined. */\n analyst?: AnalystRegistry\n /** Downstream outcome metric per run (e.g. engagement rate, approval\n * rate, downstream pass rate). When present, the report includes\n * `outcomeCorrelation` + a simple linear reward model fit. */\n outcomeSignal?: {\n metric: string\n valueByRunId: Record<string, number>\n }\n /** Multi-rater feedback for inter-rater agreement. Each entry is one\n * rater's score for one run. Two or more raters → kappa + disagreement\n * triage list. */\n raterScores?: Array<{ runId: string; rater: string; score: number }>\n /** Number of histogram bins for distributional summaries. Default 12. */\n histogramBins?: number\n /** Decision threshold — the smallest composite lift the caller cares\n * about. Used by the recommendations engine to call ship vs hold.\n * Default 0.02. */\n decisionThreshold?: number\n}\n\nexport async function analyzeRuns(opts: AnalyzeRunsOptions): Promise<InsightReport> {\n const runs = opts.runs\n const bins = opts.histogramBins ?? 12\n const threshold = opts.decisionThreshold ?? 0.02\n const split = resolveSplit(runs, opts.split ?? 'auto')\n\n const compositeWithIds = runs\n .map((r) => ({ runId: r.runId, score: compositeOf(r, split) }))\n .filter((p) => Number.isFinite(p.score))\n const composite = distributionOf(\n compositeWithIds.map((p) => p.score),\n bins,\n compositeWithIds,\n )\n\n const perDimension = computePerDimension(runs, bins)\n\n const costs = runs.map((r) => r.costUsd).filter(Number.isFinite)\n const costDist = distributionOf(costs, bins)\n const pareto = paretoChart(runs, { split })\n const degraded: { cost?: string; pareto?: string } = {}\n if (costs.length === 0 || costs.every((c) => c === 0)) {\n degraded.cost = 'no costUsd values recorded — cost axis carries no signal'\n }\n if (pareto.points.length < 2) {\n degraded.pareto =\n pareto.points.length === 0\n ? 'no candidates — Pareto unavailable'\n : 'single candidate — Pareto is a single point, not a frontier'\n }\n const costQuality = {\n cost: costDist,\n pareto,\n ...(degraded.cost || degraded.pareto ? { degraded } : {}),\n }\n\n const judges = computeJudgeInsights(runs)\n\n const interRater = opts.raterScores ? computeInterRater(opts.raterScores) : undefined\n\n const lift = computeLift(runs, opts.baselineCandidateId, opts.candidateCandidateId, split)\n\n const failureClusters = opts.analyst\n ? await computeFailureClusters(runs, opts.analyst, split)\n : undefined\n\n const contamination = opts.canaryScenarios\n ? computeContamination(runs, opts.canaryScenarios)\n : undefined\n\n const outcomeCorrelation = opts.outcomeSignal\n ? computeOutcomeCorrelation(runs, opts.outcomeSignal, split)\n : undefined\n\n const release = buildReleaseScorecard(composite, lift, contamination)\n\n const recommendations = buildRecommendations({\n composite,\n judges,\n interRater,\n lift,\n failureClusters,\n contamination,\n outcomeCorrelation,\n threshold,\n })\n\n return {\n n: runs.length,\n composite,\n perDimension,\n costQuality,\n judges,\n interRater,\n lift,\n failureClusters,\n contamination,\n outcomeCorrelation,\n release,\n recommendations,\n }\n}\n\n// ── Composite + split selection ─────────────────────────────────────\n\nfunction resolveSplit(\n runs: RunRecord[],\n pref: 'search' | 'holdout' | 'auto',\n): 'search' | 'holdout' {\n if (pref !== 'auto') return pref\n const hasHoldout = runs.some((r) => Number.isFinite(r.outcome.holdoutScore))\n return hasHoldout ? 'holdout' : 'search'\n}\n\nfunction compositeOf(run: RunRecord, split: 'search' | 'holdout'): number {\n const primary = split === 'holdout' ? run.outcome.holdoutScore : run.outcome.searchScore\n if (Number.isFinite(primary)) return primary as number\n // Fall through to the other split if the preferred one is missing —\n // analyzeRuns shouldn't refuse to summarise a run just because the\n // caller asked for the split that wasn't recorded.\n const alt = split === 'holdout' ? run.outcome.searchScore : run.outcome.holdoutScore\n return Number.isFinite(alt) ? (alt as number) : Number.NaN\n}\n\n// ── Distribution helpers ────────────────────────────────────────────\n\nfunction distributionOf(\n values: number[],\n bins: number,\n withIds?: Array<{ runId: string; score: number }>,\n): ScalarDistribution {\n if (values.length === 0) {\n return {\n n: 0,\n mean: 0,\n p50: 0,\n p95: 0,\n stddev: 0,\n min: 0,\n max: 0,\n histogram: [],\n }\n }\n const sorted = [...values].sort((a, b) => a - b)\n const n = sorted.length\n const mean = sorted.reduce((s, v) => s + v, 0) / n\n const variance = sorted.reduce((s, v) => s + (v - mean) ** 2, 0) / n\n const stddev = Math.sqrt(variance)\n const tailRuns = withIds\n ? [...withIds].sort((a, b) => a.score - b.score).slice(0, Math.min(5, withIds.length))\n : undefined\n return {\n n,\n mean,\n p50: percentile(sorted, 0.5),\n p95: percentile(sorted, 0.95),\n stddev,\n min: sorted[0]!,\n max: sorted[n - 1]!,\n histogram: histogram(sorted, bins),\n ...(tailRuns ? { tailRuns } : {}),\n }\n}\n\nfunction percentile(sorted: number[], q: number): number {\n if (sorted.length === 0) return 0\n if (sorted.length === 1) return sorted[0]!\n const idx = (sorted.length - 1) * q\n const lo = Math.floor(idx)\n const hi = Math.ceil(idx)\n if (lo === hi) return sorted[lo]!\n const w = idx - lo\n return sorted[lo]! * (1 - w) + sorted[hi]! * w\n}\n\n/** Even-width histogram over the value range. Returns inclusive-lo /\n * exclusive-hi bins (closed on right for the last bin) compatible with\n * the substrate's `GainDistributionBin` shape. */\nfunction histogram(sorted: number[], bins: number): ScalarDistribution['histogram'] {\n if (sorted.length === 0 || bins < 1) return []\n const min = sorted[0]!\n const max = sorted[sorted.length - 1]!\n if (min === max) return [{ lo: min, hi: max, count: sorted.length }]\n const width = (max - min) / bins\n const out: ScalarDistribution['histogram'] = []\n for (let i = 0; i < bins; i++) {\n const lo = min + i * width\n const hi = i === bins - 1 ? max : lo + width\n out.push({ lo, hi, count: 0 })\n }\n for (const v of sorted) {\n const idx = Math.min(bins - 1, Math.floor((v - min) / width))\n out[idx]!.count++\n }\n return out\n}\n\nfunction computePerDimension(runs: RunRecord[], bins: number): Record<string, ScalarDistribution> {\n // JudgeScoresRecord pre-aggregates `perDimMean` (mean across judges per\n // dimension). We collect those means across runs to produce a per-dim\n // distribution at the corpus level. Consumers who want per-judge\n // dimension values reach into `perJudge[judgeId][dim]` themselves.\n const byDim = new Map<string, number[]>()\n for (const run of runs) {\n const scores = run.outcome.judgeScores\n if (!scores) continue\n for (const [dim, value] of Object.entries(scores.perDimMean ?? {})) {\n if (!Number.isFinite(value)) continue\n const arr = byDim.get(dim) ?? []\n arr.push(value)\n byDim.set(dim, arr)\n }\n }\n const out: Record<string, ScalarDistribution> = {}\n for (const [dim, values] of byDim) out[dim] = distributionOf(values, bins)\n return out\n}\n\n// ── Judge insights ──────────────────────────────────────────────────\n\nfunction computeJudgeInsights(runs: RunRecord[]): Record<string, JudgeInsight> {\n // Each judge's per-run mean is the average of its per-dimension scores\n // for that run. We aggregate those means across all runs each judge\n // scored — giving consumers a \"this judge's typical verdict\" reading.\n const out: Record<string, JudgeInsight> = {}\n const byJudge = new Map<string, number[]>()\n for (const run of runs) {\n const scores = run.outcome.judgeScores\n if (!scores?.perJudge) continue\n for (const [judgeId, dims] of Object.entries(scores.perJudge)) {\n const dimValues = Object.values(dims).filter(Number.isFinite) as number[]\n if (dimValues.length === 0) continue\n const judgeMean = dimValues.reduce((s, v) => s + v, 0) / dimValues.length\n const arr = byJudge.get(judgeId) ?? []\n arr.push(judgeMean)\n byJudge.set(judgeId, arr)\n }\n }\n for (const [judgeId, values] of byJudge) {\n out[judgeId] = {\n n: values.length,\n meanScore: values.reduce((s, v) => s + v, 0) / values.length,\n }\n }\n return out\n}\n\n// ── Inter-rater agreement ───────────────────────────────────────────\n\nfunction computeInterRater(\n ratings: Array<{ runId: string; rater: string; score: number }>,\n): InterRaterInsight | undefined {\n const byRun = new Map<string, Array<{ rater: string; score: number }>>()\n for (const r of ratings) {\n if (!Number.isFinite(r.score)) continue\n const list = byRun.get(r.runId) ?? []\n list.push({ rater: r.rater, score: r.score })\n byRun.set(r.runId, list)\n }\n const raters = new Set(ratings.map((r) => r.rater))\n const jointlyRated: string[] = []\n for (const [runId, ratersForRun] of byRun) {\n const seen = new Set(ratersForRun.map((r) => r.rater))\n let all = true\n for (const r of raters) if (!seen.has(r)) all = false\n if (all) jointlyRated.push(runId)\n }\n if (raters.size < 2 || jointlyRated.length === 0) return undefined\n\n const raterList = [...raters].sort()\n const perPair: Record<string, number> = {}\n for (let i = 0; i < raterList.length; i++) {\n for (let j = i + 1; j < raterList.length; j++) {\n const a = raterList[i]!\n const b = raterList[j]!\n const aScores: number[] = []\n const bScores: number[] = []\n for (const runId of jointlyRated) {\n const ratersForRun = byRun.get(runId)!\n const sa = ratersForRun.find((r) => r.rater === a)?.score\n const sb = ratersForRun.find((r) => r.rater === b)?.score\n if (sa !== undefined && sb !== undefined) {\n aScores.push(sa)\n bScores.push(sb)\n }\n }\n perPair[`${a}::${b}`] = pearson(aScores, bScores)\n }\n }\n const pairKappas = Object.values(perPair)\n const kappa =\n pairKappas.length === 0 ? 0 : pairKappas.reduce((s, v) => s + v, 0) / pairKappas.length\n\n const disagreementCases = jointlyRated\n .map((runId) => {\n const ratersForRun = byRun.get(runId)!\n const scores = ratersForRun.map((r) => r.score)\n const range = Math.max(...scores) - Math.min(...scores)\n return { runId, ratings: ratersForRun, range }\n })\n .sort((a, b) => b.range - a.range)\n .slice(0, 20)\n\n return {\n raters: raters.size,\n jointlyRated: jointlyRated.length,\n kappa,\n perPair,\n disagreementCases,\n }\n}\n\nfunction pearson(a: number[], b: number[]): number {\n if (a.length !== b.length || a.length === 0) return 0\n const n = a.length\n const meanA = a.reduce((s, v) => s + v, 0) / n\n const meanB = b.reduce((s, v) => s + v, 0) / n\n let num = 0\n let denomA = 0\n let denomB = 0\n for (let i = 0; i < n; i++) {\n const da = a[i]! - meanA\n const db = b[i]! - meanB\n num += da * db\n denomA += da * da\n denomB += db * db\n }\n const denom = Math.sqrt(denomA * denomB)\n return denom === 0 ? 0 : num / denom\n}\n\n// ── Lift ────────────────────────────────────────────────────────────\n\nfunction computeLift(\n runs: RunRecord[],\n baselineId: string | undefined,\n candidateId: string | undefined,\n split: 'search' | 'holdout',\n): LiftInsight | undefined {\n let bId = baselineId\n let cId = candidateId\n if (!bId || !cId) {\n // Auto-detect: when exactly two distinct candidateIds appear, treat the\n // lower-mean side as baseline.\n const ids = [...new Set(runs.map((r) => r.candidateId))]\n if (ids.length !== 2) return undefined\n const [idA, idB] = ids as [string, string]\n const meanA = mean(runs.filter((r) => r.candidateId === idA).map((r) => compositeOf(r, split)))\n const meanB = mean(runs.filter((r) => r.candidateId === idB).map((r) => compositeOf(r, split)))\n bId = meanA <= meanB ? idA : idB\n cId = meanA <= meanB ? idB : idA\n }\n\n const baseline = runs.filter((r) => r.candidateId === bId)\n const candidate = runs.filter((r) => r.candidateId === cId)\n if (baseline.length === 0 || candidate.length === 0) return undefined\n\n // Pair on (experimentId, seed). When that key doesn't match, fall back\n // to ordinal pairing — common for fresh runs from the same scenario list.\n const baselineByKey = new Map(baseline.map((r) => [pairingKey(r), r]))\n const pairedBaseline: number[] = []\n const pairedCandidate: number[] = []\n let usedKeyPairing = false\n for (const cand of candidate) {\n const b = baselineByKey.get(pairingKey(cand))\n if (b) {\n const bC = compositeOf(b, split)\n const cC = compositeOf(cand, split)\n if (Number.isFinite(bC) && Number.isFinite(cC)) {\n pairedBaseline.push(bC)\n pairedCandidate.push(cC)\n usedKeyPairing = true\n }\n }\n }\n if (!usedKeyPairing) {\n const n = Math.min(baseline.length, candidate.length)\n for (let i = 0; i < n; i++) {\n const bC = compositeOf(baseline[i]!, split)\n const cC = compositeOf(candidate[i]!, split)\n if (Number.isFinite(bC) && Number.isFinite(cC)) {\n pairedBaseline.push(bC)\n pairedCandidate.push(cC)\n }\n }\n }\n if (pairedBaseline.length === 0) return undefined\n\n const baselineMean = mean(pairedBaseline)\n const candidateMean = mean(pairedCandidate)\n const delta = candidateMean - baselineMean\n\n const bootstrap = pairedBootstrap(pairedBaseline, pairedCandidate, {\n confidence: 0.95,\n resamples: 2000,\n statistic: 'mean',\n })\n const tTest = pairedTTest(pairedBaseline, pairedCandidate)\n const d = cohensD(pairedBaseline, pairedCandidate)\n const mde = pairedMde({ nPaired: pairedBaseline.length, power: 0.8, alpha: 0.05 })\n const requiredN = requiredSampleSize({\n effect: Math.max(Math.abs(delta), 1e-6),\n power: 0.8,\n alpha: 0.05,\n })\n\n return {\n baselineMean,\n candidateMean,\n delta,\n ci95: [bootstrap.low, bootstrap.high],\n pValue: tTest.p,\n n: pairedBaseline.length,\n cohensD: d,\n mde,\n requiredN,\n }\n}\n\nfunction pairingKey(r: RunRecord): string {\n return `${r.experimentId}::${r.seed}`\n}\n\nfunction mean(arr: number[]): number {\n return arr.length === 0 ? 0 : arr.reduce((s, v) => s + v, 0) / arr.length\n}\n\n// ── Failure clustering ──────────────────────────────────────────────\n\nasync function computeFailureClusters(\n runs: RunRecord[],\n analyst: AnalystRegistry,\n split: 'search' | 'holdout',\n): Promise<FailureClusterInsight | undefined> {\n const failed = runs.filter((r) => compositeOf(r, split) < 0.5 || r.failureMode !== undefined)\n if (failed.length === 0) return { clusters: [], totalFailures: 0 }\n\n const clusters = new Map<string, { exemplars: string[]; share: number }>()\n for (const run of failed) {\n try {\n const result = await analyst.run(run.runId, {\n kind: 'run-record',\n run,\n } as Parameters<typeof analyst.run>[1])\n for (const finding of result.findings as AnalystFinding[]) {\n const key = finding.area || finding.analyst_id || 'unclassified'\n const c = clusters.get(key) ?? { exemplars: [], share: 0 }\n if (c.exemplars.length < 5) c.exemplars.push(run.runId)\n clusters.set(key, c)\n }\n } catch {\n const c = clusters.get('analyst-error') ?? { exemplars: [], share: 0 }\n if (c.exemplars.length < 5) c.exemplars.push(run.runId)\n clusters.set('analyst-error', c)\n }\n }\n const clusterList = [...clusters.entries()].map(([id, c]) => ({\n id,\n name: id,\n share: c.exemplars.length / failed.length,\n exemplars: c.exemplars,\n }))\n clusterList.sort((a, b) => b.share - a.share)\n return { clusters: clusterList, totalFailures: failed.length }\n}\n\n// ── Contamination ──────────────────────────────────────────────────\n\nfunction computeContamination(\n runs: RunRecord[],\n canaries: DatasetScenario[],\n): InsightReport['contamination'] {\n let leaks = 0\n const details: Array<{ runId: string; canary: string; matched: string }> = []\n for (const run of runs) {\n const output = stringifyOutput(run)\n if (!output) continue\n const leaksHere = checkCanaries(output, canaries)\n for (const leak of leaksHere) {\n leaks++\n details.push({ runId: run.runId, canary: leak.canary, matched: leak.evidence })\n }\n }\n return { leaks, holdoutAuditPassed: leaks === 0, details }\n}\n\nfunction stringifyOutput(run: RunRecord): string | undefined {\n // RunRecord doesn't fix where \"the agent's output\" lives — different\n // consumers stash it differently. We probe the common shapes: the\n // outcome.raw map (numeric only by design — unlikely to contain text),\n // and any string-valued fields tucked under metadata via type casting.\n // Consumers with bespoke shapes pass canaryScenarios only when they\n // know their runs carry a stringifiable surface.\n const metadata = (run as unknown as { metadata?: Record<string, unknown> }).metadata\n if (typeof metadata?.output === 'string') return metadata.output\n if (typeof metadata?.text === 'string') return metadata.text\n return undefined\n}\n\n// ── Outcome correlation + linear reward model ──────────────────────\n\nfunction computeOutcomeCorrelation(\n runs: RunRecord[],\n outcome: { metric: string; valueByRunId: Record<string, number> },\n split: 'search' | 'holdout',\n): OutcomeCorrelationInsight | undefined {\n const xs: number[] = []\n const ys: number[] = []\n for (const run of runs) {\n const y = outcome.valueByRunId[run.runId]\n if (y === undefined || !Number.isFinite(y)) continue\n const x = compositeOf(run, split)\n if (!Number.isFinite(x)) continue\n xs.push(x)\n ys.push(y)\n }\n if (xs.length < 3) return undefined\n\n const p = pearson(xs, ys)\n const s = spearman(xs, ys)\n const meanX = mean(xs)\n const meanY = mean(ys)\n let num = 0\n let denom = 0\n for (let i = 0; i < xs.length; i++) {\n num += (xs[i]! - meanX) * (ys[i]! - meanY)\n denom += (xs[i]! - meanX) ** 2\n }\n const slope = denom === 0 ? 0 : num / denom\n const intercept = meanY - slope * meanX\n const ssTot = ys.reduce((a, y) => a + (y - meanY) ** 2, 0)\n const ssRes = ys.reduce((a, y, i) => a + (y - (intercept + slope * xs[i]!)) ** 2, 0)\n const r2 = ssTot === 0 ? 0 : 1 - ssRes / ssTot\n\n return {\n metric: outcome.metric,\n n: xs.length,\n pearson: p,\n spearman: s,\n rewardModel: { intercept, slope, r2 },\n }\n}\n\nfunction spearman(a: number[], b: number[]): number {\n if (a.length !== b.length || a.length === 0) return 0\n return pearson(rank(a), rank(b))\n}\n\nfunction rank(arr: number[]): number[] {\n const indexed = arr.map((v, i) => ({ v, i }))\n indexed.sort((x, y) => x.v - y.v)\n const ranks = new Array(arr.length).fill(0)\n let i = 0\n while (i < indexed.length) {\n let j = i\n while (j + 1 < indexed.length && indexed[j + 1]!.v === indexed[i]!.v) j++\n const avg = (i + j + 2) / 2\n for (let k = i; k <= j; k++) ranks[indexed[k]!.i] = avg\n i = j + 1\n }\n return ranks\n}\n\n// ── Release confidence scorecard ───────────────────────────────────\n\nfunction buildReleaseScorecard(\n composite: ScalarDistribution,\n lift: LiftInsight | undefined,\n contamination: InsightReport['contamination'],\n): InsightReport['release'] {\n // Synthesise a minimal scorecard from the rolled-up signal. The\n // substrate's `evaluateReleaseConfidence` primitive consumes a richer\n // input shape that callers can produce by wiring SLO definitions; the\n // shape here is the contract `selfImprove`/`analyzeRuns` consumers\n // receive automatically. They can call `evaluateReleaseConfidence`\n // directly when they want SLO-based axis evaluation.\n const axes: InsightReport['release']['axes'] = []\n const liftPass =\n lift === undefined || lift.ci95[0] > 0\n ? ('pass' as const)\n : lift.delta > 0\n ? ('warn' as const)\n : ('fail' as const)\n axes.push({\n name: 'quality-lift',\n status: liftPass,\n detail: lift\n ? `delta=${lift.delta.toFixed(3)}, CI95=[${lift.ci95[0].toFixed(3)}, ${lift.ci95[1].toFixed(3)}], n=${lift.n}`\n : 'no baseline/candidate pair available',\n })\n const contamPass =\n contamination === undefined || contamination.leaks === 0 ? ('pass' as const) : ('fail' as const)\n axes.push({\n name: 'contamination',\n status: contamPass,\n detail: contamination ? `${contamination.leaks} canary leak(s)` : 'no canaries supplied',\n })\n axes.push({\n name: 'composite-distribution',\n status: composite.mean >= 0.5 ? 'pass' : composite.mean >= 0.3 ? 'warn' : 'fail',\n detail: `mean=${composite.mean.toFixed(3)}, p50=${composite.p50.toFixed(3)}, p95=${composite.p95.toFixed(3)} over n=${composite.n}`,\n })\n const status = axes.some((a) => a.status === 'fail')\n ? 'fail'\n : axes.some((a) => a.status === 'warn')\n ? 'warn'\n : 'pass'\n return {\n status,\n axes,\n issues: [],\n }\n}\n\n// ── Recommendations engine ─────────────────────────────────────────\n\ninterface RecommendationContext {\n composite: ScalarDistribution\n judges: Record<string, JudgeInsight>\n interRater?: InterRaterInsight\n lift?: LiftInsight\n failureClusters?: FailureClusterInsight\n contamination?: InsightReport['contamination']\n outcomeCorrelation?: OutcomeCorrelationInsight\n threshold: number\n}\n\nfunction buildRecommendations(ctx: RecommendationContext): Recommendation[] {\n const out: Recommendation[] = []\n\n // Composite-distribution branch. Fires when the overall quality signal is\n // poor regardless of lift / contamination / clusters — the customer needs\n // to know they have a problem AND which specific runs to inspect.\n if (ctx.composite.n > 0) {\n if (ctx.composite.mean < 0.3) {\n const tail = ctx.composite.tailRuns ?? []\n const names = tail\n .slice(0, 5)\n .map((t) => `${t.runId}=${t.score.toFixed(3)}`)\n .join(', ')\n out.push({\n priority: 'critical',\n kind: 'investigate',\n title: `Composite mean ${ctx.composite.mean.toFixed(3)} is below the 0.3 floor — the agent is broken on this corpus`,\n detail:\n tail.length > 0\n ? `Worst ${tail.length} run${tail.length === 1 ? '' : 's'} to inspect first: ${names}. Histogram p50=${ctx.composite.p50.toFixed(3)}, p95=${ctx.composite.p95.toFixed(3)}.`\n : `Histogram p50=${ctx.composite.p50.toFixed(3)}, p95=${ctx.composite.p95.toFixed(3)}.`,\n evidencePath: 'composite.tailRuns',\n })\n } else if (ctx.composite.mean < 0.5) {\n const tail = ctx.composite.tailRuns ?? []\n const names = tail\n .slice(0, 3)\n .map((t) => `${t.runId}=${t.score.toFixed(3)}`)\n .join(', ')\n out.push({\n priority: 'high',\n kind: 'investigate',\n title: `Composite mean ${ctx.composite.mean.toFixed(3)} is below 0.5 — investigate the lower tail before claiming the agent is healthy`,\n detail:\n tail.length > 0\n ? `Worst ${tail.length} run${tail.length === 1 ? '' : 's'}: ${names}. Histogram p50=${ctx.composite.p50.toFixed(3)}, p95=${ctx.composite.p95.toFixed(3)}.`\n : `Histogram p50=${ctx.composite.p50.toFixed(3)}, p95=${ctx.composite.p95.toFixed(3)}.`,\n evidencePath: 'composite.tailRuns',\n })\n }\n }\n\n // Missing-judges branch. The report can't surface per-dimension or\n // calibration signal when `outcome.judgeScores` is empty across the\n // corpus. Tell the customer how to enrich.\n if (Object.keys(ctx.judges).length === 0 && ctx.composite.n > 0) {\n out.push({\n priority: 'medium',\n kind: 'expand-corpus',\n title: 'No judge scores recorded — per-dimension + calibration insights unavailable',\n detail:\n 'Records have no `outcome.judgeScores`. To unlock perDimension, judges, and calibration, attach a Judge run during your eval pass and populate `outcome.judgeScores.perJudge[judgeName][dimension] = score`. See `docs/insight-report.md` for the expected shape.',\n evidencePath: 'judges',\n })\n }\n\n if (ctx.lift) {\n const decisive = ctx.lift.ci95[0] > ctx.threshold\n const inconclusive = ctx.lift.ci95[0] <= ctx.threshold && ctx.lift.ci95[1] > ctx.threshold\n if (decisive) {\n out.push({\n priority: 'critical',\n kind: 'ship',\n title: `Ship — lift ${ctx.lift.delta.toFixed(3)} (95% CI ${ctx.lift.ci95[0].toFixed(3)}..${ctx.lift.ci95[1].toFixed(3)})`,\n detail: `Holdout lift exceeds threshold ${ctx.threshold} with 95% bootstrap confidence (n=${ctx.lift.n}, p=${ctx.lift.pValue.toFixed(4)}, d=${ctx.lift.cohensD.toFixed(2)}).`,\n evidencePath: 'lift',\n })\n } else if (inconclusive) {\n out.push({\n priority: 'high',\n kind: 'expand-corpus',\n title: `Inconclusive — need ~${ctx.lift.requiredN} paired runs (have ${ctx.lift.n}) at current effect size`,\n detail: `CI straddles threshold. Current MDE at 80% power is ${ctx.lift.mde.toFixed(3)}; observed delta is ${ctx.lift.delta.toFixed(3)}.`,\n evidencePath: 'lift',\n })\n } else {\n out.push({\n priority: 'critical',\n kind: 'hold',\n title: `Hold — lift CI lower bound ${ctx.lift.ci95[0].toFixed(3)} is at or below threshold ${ctx.threshold}`,\n detail: `Bootstrap CI provides no statistical evidence the candidate is better. Consider tightening the mutation or expanding the holdout.`,\n evidencePath: 'lift',\n })\n }\n }\n\n if (ctx.contamination && ctx.contamination.leaks > 0) {\n out.push({\n priority: 'critical',\n kind: 'fix',\n title: `${ctx.contamination.leaks} canary leak${ctx.contamination.leaks === 1 ? '' : 's'} detected`,\n detail: `Holdout integrity is compromised. The lift number is unreliable until you investigate.`,\n evidencePath: 'contamination',\n })\n }\n\n if (ctx.interRater && ctx.interRater.kappa < 0.5) {\n out.push({\n priority: 'high',\n kind: 'recalibrate',\n title: `Inter-rater agreement κ=${ctx.interRater.kappa.toFixed(2)} is below 0.5`,\n detail: `Raters disagree on what 'good' looks like. Top disagreement cases listed in interRater.disagreementCases — consider a triage meeting or refining the rubric.`,\n evidencePath: 'interRater',\n })\n }\n\n if (ctx.failureClusters && ctx.failureClusters.clusters.length > 0) {\n const top = ctx.failureClusters.clusters[0]!\n out.push({\n priority: 'high',\n kind: 'investigate',\n title: `Top failure cluster: ${top.name} (${(top.share * 100).toFixed(0)}% of failures)`,\n detail: `${ctx.failureClusters.totalFailures} runs failed. The largest cluster groups ${top.exemplars.length} exemplars under '${top.name}'.`,\n evidencePath: 'failureClusters.clusters[0]',\n })\n }\n\n if (ctx.outcomeCorrelation && Math.abs(ctx.outcomeCorrelation.spearman) < 0.3) {\n out.push({\n priority: 'medium',\n kind: 'recalibrate',\n title: `Judge scores decoupled from ${ctx.outcomeCorrelation.metric} (Spearman ρ=${ctx.outcomeCorrelation.spearman.toFixed(2)})`,\n detail: `Your judges score what they were trained to score, but it isn't predicting downstream ${ctx.outcomeCorrelation.metric}. Consider retraining the judge against ${ctx.outcomeCorrelation.metric} as the gold signal.`,\n evidencePath: 'outcomeCorrelation',\n })\n }\n\n return out\n}\n\n// ── Re-export pareto figure spec for hosted-side rendering ─────────\n\nexport type { ParetoFigureSpec }\n","/**\n * # `selfImprove()` — the LAND-tier one-shot.\n *\n * The cheapest possible call site to run a real closed-loop self-\n * improvement over your agent. Wraps `runImprovementLoop` with smart\n * defaults and a budget-shaped options API; every escape hatch the\n * substrate exposes is reachable from here without losing the\n * one-function feel.\n *\n * Defaults picked to match the LAND-tier story:\n * - In-memory storage (no filesystem touch).\n * - `gepaDriver` reflective mutation with copywriting-flavored primitives\n * (override `driver` or `mutationPrimitives` for any domain).\n * - `defaultProductionGate` with `deltaThreshold: 0.05`.\n * - Held-out split = 25% of scenarios, deterministic by id hash.\n * - 3 generations × population 2 (raise via `budget` for more search).\n * - `autoOnPromote: 'none'` (we don't open PRs unless you ask).\n *\n * Want one-click? Provide `agent` + `scenarios` + `judge`. Done.\n * Want distributed? Pass `cellPlacement` + an `httpDispatch`-backed\n * agent. Want a code-tier surface? Pass a `MutableSurface` + your own\n * `driver`. Same function.\n */\n\nimport { gepaDriver } from '../campaign/drivers/gepa'\nimport { defaultProductionGate } from '../campaign/gates/default-production-gate'\nimport {\n type RunImprovementLoopResult,\n runImprovementLoop,\n} from '../campaign/presets/run-improvement-loop'\nimport { type CampaignStorage, inMemoryCampaignStorage } from '../campaign/storage'\nimport type {\n CampaignCellResult,\n DispatchContext,\n Gate,\n ImprovementDriver,\n JudgeConfig,\n MutableSurface,\n Scenario,\n} from '../campaign/types'\nimport { createHostedClient, type HostedTenant } from '../hosted/client'\nimport type { EvalRunCellScore, EvalRunEvent, EvalRunGenerationSnapshot } from '../hosted/types'\nimport type { JudgeScoresRecord, RunRecord } from '../run-record'\nimport { analyzeRuns } from './analyze-runs'\nimport type { InsightReport } from './insight-report'\n\nexport interface SelfImproveBudget {\n /** Hard $ ceiling across all cells in baseline + every generation. Cells\n * beyond the ceiling are skipped (cost-aware, not aborted). */\n dollars?: number\n /** How many improvement generations to explore. Default 3. Set 0 to\n * skip improvement entirely (selfImprove becomes a baseline-only run). */\n generations?: number\n /** Candidates the driver proposes per generation. Default 2. */\n populationSize?: number\n /** Max concurrent cells across the loop. Default 2. */\n maxConcurrency?: number\n /** Fraction of `scenarios` held out from training, used for the gate.\n * Default 0.25. Ignored when `holdoutScenarios` is set explicitly. */\n holdoutFraction?: number\n /** Explicit held-out scenarios; overrides `holdoutFraction`. */\n holdoutScenarios?: Scenario[]\n}\n\nexport interface SelfImproveLlm {\n /** Endpoint base URL. Default Tangle Router. */\n baseUrl?: string\n /** Bearer token. Default `process.env.OPENAI_API_KEY`. */\n apiKey?: string\n /** Model id used by `gepaDriver` reflection. Default\n * `anthropic/claude-sonnet-4.6`. */\n model?: string\n}\n\nexport type SelfImproveProgressEvent =\n | { kind: 'baseline.started'; scenarios: number }\n | { kind: 'baseline.completed'; compositeMean: number; durationMs: number }\n | { kind: 'generation.started'; index: number; populationSize: number }\n | { kind: 'generation.completed'; index: number; bestComposite: number; durationMs: number }\n | { kind: 'gate.decided'; decision: string; lift: number }\n\nexport interface SelfImproveOptions<TScenario extends Scenario, TArtifact> {\n /**\n * Your agent — a function that takes the current `MutableSurface`\n * (typically a system prompt the loop is optimizing) plus the\n * scenario + cell ctx, and returns the artifact your judge scores.\n *\n * Same shape as `RunOptimizationOptions.dispatchWithSurface`. Wrap a\n * plain `Dispatch` if you don't have a surface seam:\n *\n * agent: (_surface, scenario, ctx) => yourPlainDispatch(scenario, ctx)\n *\n * That mode evaluates without mutating any surface — useful as a\n * baseline-only run (set `budget.generations = 0`).\n */\n agent: (surface: MutableSurface, scenario: TScenario, ctx: DispatchContext) => Promise<TArtifact>\n\n /** Scenarios to evaluate against. Train/holdout split is computed from\n * these unless `budget.holdoutScenarios` is set explicitly. */\n scenarios: TScenario[]\n\n /** Judge that scores artifacts. Bring your own; use `langchainJudge`\n * from `/adapters/langchain` for a Runnable-shaped one. */\n judge: JudgeConfig<TArtifact, TScenario>\n\n /** Starting surface — system prompt, JSON config, anything `MutableSurface`\n * accepts. The driver mutates this each generation. */\n baselineSurface: MutableSurface\n\n /** Budget + loop shape. All fields optional; defaults pick the LAND-tier\n * story. */\n budget?: SelfImproveBudget\n\n /** Custom driver. Default is `gepaDriver` configured from `llm` +\n * `mutationPrimitives`. */\n driver?: ImprovementDriver\n\n /** Default-driver overrides — used when `driver` is unset. */\n mutationPrimitives?: string[]\n driverTarget?: string\n\n /** Custom gate. Default is `defaultProductionGate` with\n * `deltaThreshold: 0.05` on the held-out split. */\n gate?: Gate<TArtifact, TScenario>\n\n /** LLM config consumed by the default `gepaDriver`. Ignored if you pass\n * your own `driver`. */\n llm?: SelfImproveLlm\n\n /** Storage backend. Default `inMemoryCampaignStorage()` — nothing\n * persists past the call. Pass `fsCampaignStorage()` to write to disk. */\n storage?: CampaignStorage\n\n /** Run directory (logical for in-memory storage, real path for fs).\n * Default `mem://selfImprove-<timestamp>`. */\n runDir?: string\n\n /** Distributed-driver seam — same as `RunCampaignOptions.cellPlacement`.\n * Returns an opaque placement key the substrate forwards to your agent\n * as `ctx.placement`. Combined with `httpDispatch` from\n * `/adapters/http`, fans cells across regions. */\n cellPlacement?: (input: {\n scenario: TScenario\n rep: number\n generation?: number\n }) => string | undefined\n\n /** Streaming hook — fires on baseline + each generation + gate decision.\n * Consumer routes events wherever (UI, dashboard, logs). */\n onProgress?: (event: SelfImproveProgressEvent) => void\n\n /** Auto-promotion behavior on a ship decision. Default `'none'` — we\n * return the winner; you ship it however you ship. `'pr'` opens a\n * GitHub PR via `openAutoPr`; requires `ghOwner` + `ghRepo`. */\n autoOnPromote?: 'pr' | 'none'\n ghOwner?: string\n ghRepo?: string\n\n /**\n * Opt-in: ship eval-run events to a hosted orchestrator (ours, your\n * self-hosted one, or any compatible implementation of the\n * `docs/hosted-ingest-spec.md` wire format). When set, the substrate\n * POSTs the final `EvalRunEvent` to `${endpoint}/v1/ingest/eval-runs`\n * after the loop completes. Failures are logged but do not fail the\n * loop — local result is always returned.\n *\n * For our orchestrator: `{ endpoint: 'https://orchestrator.tangle.tools/v1', apiKey, tenantId }`.\n *\n * For your self-hosted: any URL serving the wire format. See\n * `examples/hosted-ingest-server/` for the reference receiver.\n */\n hostedTenant?: HostedTenant\n\n /** Free-form labels attached to the hosted event (env, branch, model id,\n * etc.). Ignored when `hostedTenant` is unset. */\n hostedLabels?: Record<string, string>\n}\n\nexport interface SelfImproveResult<TScenario extends Scenario, TArtifact> {\n /** Composite mean across all scenarios, baseline run. */\n baseline: {\n compositeMean: number\n perScenario: Record<string, number>\n }\n /** Composite mean on the held-out set, winner run. */\n winner: {\n compositeMean: number\n perScenario: Record<string, number>\n surface: MutableSurface\n }\n /** `winner.compositeMean - baselineOnHoldout.compositeMean`. Positive\n * means the gate observed improvement. */\n lift: number\n /** `defaultProductionGate.decide()` result. */\n gateDecision: 'ship' | 'hold' | 'need_more_work' | 'model_ceiling' | 'arch_ceiling'\n /** Number of generations actually explored (may be less than the\n * budget if the driver gave up early). */\n generationsExplored: number\n /** Wall-clock total. */\n durationMs: number\n /** Total cost across baseline + every generation. */\n totalCostUsd: number\n /**\n * Rigor packet: distributional summary, paired-bootstrap lift CI,\n * judge stats, contamination check, recommendations. Wired through\n * `analyzeRuns()` on the baseline + winner cells of the campaign.\n * Hosted-tier dashboards render this as the v3-vs-v4 decision view.\n */\n insight: InsightReport\n /**\n * Raw substrate result for advanced inspection — full per-generation\n * candidates, full campaign artifacts, all judge scores. Useful for\n * debugging or reporting beyond the summary.\n */\n raw: RunImprovementLoopResult<TArtifact, TScenario>\n}\n\n/**\n * Deterministic train/holdout split by a stable hash of `scenario.id`,\n * so the same scenario set always splits the same way across runs.\n */\nfunction splitTrainHoldout<TScenario extends Scenario>(\n scenarios: TScenario[],\n fraction: number,\n): { train: TScenario[]; holdout: TScenario[] } {\n // Stable fnv-1a-ish hash of the id for ordering.\n function hash(s: string): number {\n let h = 2166136261 >>> 0\n for (let i = 0; i < s.length; i++) {\n h ^= s.charCodeAt(i)\n h = Math.imul(h, 16777619) >>> 0\n }\n return h\n }\n const sorted = [...scenarios].sort((a, b) => hash(a.id) - hash(b.id))\n const nHoldout = Math.max(1, Math.min(sorted.length - 1, Math.round(sorted.length * fraction)))\n return {\n holdout: sorted.slice(0, nHoldout),\n train: sorted.slice(nHoldout),\n }\n}\n\nfunction meanComposite(byScenario: Record<string, { meanComposite: number }>): {\n compositeMean: number\n perScenario: Record<string, number>\n} {\n const perScenario: Record<string, number> = {}\n const values: number[] = []\n for (const [id, agg] of Object.entries(byScenario)) {\n perScenario[id] = agg.meanComposite\n values.push(agg.meanComposite)\n }\n return {\n compositeMean: values.length === 0 ? 0 : values.reduce((s, v) => s + v, 0) / values.length,\n perScenario,\n }\n}\n\nconst DEFAULT_MUTATION_PRIMITIVES = [\n 'Tighten the hook: lead with the specific user outcome.',\n 'Replace generic adjectives with specific verbs or proof numbers.',\n \"Anchor every claim in something the scenario's brief literally supports.\",\n 'Honor the surface-shape constraint (length, register, audience vocabulary).',\n]\n\n/**\n * One-shot self-improvement loop. See module docstring for defaults +\n * extension points.\n *\n * @example Minimum (LAND tier):\n *\n * const result = await selfImprove({\n * agent: (surface, scenario, ctx) => myAgent(surface, scenario, ctx.signal),\n * scenarios,\n * judge,\n * baselineSurface: DEFAULT_PROMPT,\n * })\n * console.log(`lift: ${result.lift.toFixed(3)} (${result.gateDecision})`)\n *\n * @example Distributed (workers in three regions):\n *\n * await selfImprove({\n * agent: httpDispatch({ resolveUrl: ({ placement }) => REGION_URLS[placement!] }),\n * scenarios,\n * judge,\n * baselineSurface: DEFAULT_PROMPT,\n * cellPlacement: ({ scenario }) => scenario.region,\n * budget: { maxConcurrency: 12 },\n * })\n */\nexport async function selfImprove<TScenario extends Scenario, TArtifact>(\n opts: SelfImproveOptions<TScenario, TArtifact>,\n): Promise<SelfImproveResult<TScenario, TArtifact>> {\n const startedAt = Date.now()\n\n const budget = opts.budget ?? {}\n const generations = budget.generations ?? 3\n const populationSize = budget.populationSize ?? 2\n const maxConcurrency = budget.maxConcurrency ?? 2\n const holdoutFraction = budget.holdoutFraction ?? 0.25\n const costCeiling = budget.dollars\n\n const explicitHoldout = budget.holdoutScenarios\n const { train, holdout } = explicitHoldout\n ? {\n train: opts.scenarios.filter((s) => !explicitHoldout.some((h) => h.id === s.id)),\n holdout: explicitHoldout as TScenario[],\n }\n : splitTrainHoldout(opts.scenarios, holdoutFraction)\n\n if (train.length === 0) {\n throw new Error(\n 'selfImprove: train split is empty. Reduce holdoutFraction or pass more scenarios.',\n )\n }\n if (holdout.length === 0) {\n throw new Error('selfImprove: holdout split is empty. Pass more scenarios.')\n }\n\n const driver: ImprovementDriver =\n opts.driver ??\n gepaDriver({\n llm: {\n baseUrl: opts.llm?.baseUrl ?? 'https://router.tangle.tools/v1',\n apiKey: opts.llm?.apiKey ?? process.env.OPENAI_API_KEY ?? '',\n },\n model: opts.llm?.model ?? 'anthropic/claude-sonnet-4.6',\n target:\n opts.driverTarget ??\n 'agent surface (system prompt or config) being optimized by selfImprove',\n mutationPrimitives: opts.mutationPrimitives ?? DEFAULT_MUTATION_PRIMITIVES,\n })\n\n const gate: Gate<TArtifact, TScenario> =\n opts.gate ??\n defaultProductionGate<TArtifact, TScenario>({\n holdoutScenarios: holdout,\n deltaThreshold: 0.05,\n })\n\n const storage = opts.storage ?? inMemoryCampaignStorage()\n const runDir = opts.runDir ?? `mem://selfImprove-${startedAt}`\n\n if (opts.onProgress) {\n opts.onProgress({ kind: 'baseline.started', scenarios: opts.scenarios.length })\n }\n\n const result = await runImprovementLoop<TScenario, TArtifact>({\n scenarios: train,\n baselineSurface: opts.baselineSurface,\n dispatchWithSurface: opts.agent,\n driver,\n judges: [opts.judge],\n populationSize,\n maxGenerations: generations,\n holdoutScenarios: holdout,\n gate,\n autoOnPromote: opts.autoOnPromote ?? 'none',\n ghOwner: opts.ghOwner,\n ghRepo: opts.ghRepo,\n storage,\n runDir,\n maxConcurrency,\n cellPlacement: opts.cellPlacement,\n costCeiling,\n })\n\n const baseline = meanComposite(result.baselineOnHoldout.aggregates.byScenario)\n const winnerStats = meanComposite(result.winnerOnHoldout.aggregates.byScenario)\n\n if (opts.onProgress) {\n opts.onProgress({\n kind: 'baseline.completed',\n compositeMean: baseline.compositeMean,\n durationMs: Date.now() - startedAt,\n })\n opts.onProgress({\n kind: 'gate.decided',\n decision: result.gateResult.decision,\n lift: winnerStats.compositeMean - baseline.compositeMean,\n })\n }\n\n const totalCost =\n result.baselineCampaign.aggregates.totalCostUsd +\n result.generations.reduce(\n (sum, gen) =>\n sum + gen.surfaces.reduce((s, sf) => s + sf.campaign.aggregates.totalCostUsd, 0),\n 0,\n )\n\n // Rigor packet: feed baseline + winner cells through analyzeRuns().\n // The two candidates (`baseline` / `winner`) give the lift section a\n // clean paired comparison; per-judge / per-dimension / cost-quality\n // sections populate from the cells' judgeScores.\n const insight = await analyzeRuns({\n runs: [\n ...cellsToRunRecords(result.baselineCampaign.cells, 'baseline', runDir),\n ...cellsToRunRecords(result.winnerOnHoldout.cells, 'winner', runDir),\n ],\n baselineCandidateId: 'baseline',\n candidateCandidateId: 'winner',\n })\n\n const summary: SelfImproveResult<TScenario, TArtifact> = {\n baseline,\n winner: {\n ...winnerStats,\n surface: result.winnerSurface,\n },\n lift: winnerStats.compositeMean - baseline.compositeMean,\n gateDecision: result.gateResult.decision,\n generationsExplored: result.generations.length,\n durationMs: Date.now() - startedAt,\n totalCostUsd: totalCost,\n insight,\n raw: result,\n }\n\n // Opt-in hosted ingest. Failures logged but never fail the loop — the\n // local result is always returned. This matches the wedge-doc invariant\n // that LAND-tier never blocks on EXPAND-tier infra.\n if (opts.hostedTenant) {\n try {\n await shipEvalRunToHosted(opts.hostedTenant, opts, summary, result, runDir)\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n // eslint-disable-next-line no-console -- intentional: hosted-ingest is best-effort\n console.warn(`[agent-eval] hosted ingest failed (continuing): ${msg}`)\n }\n }\n\n return summary\n}\n\nasync function shipEvalRunToHosted<TScenario extends Scenario, TArtifact>(\n tenant: HostedTenant,\n opts: SelfImproveOptions<TScenario, TArtifact>,\n summary: SelfImproveResult<TScenario, TArtifact>,\n raw: RunImprovementLoopResult<TArtifact, TScenario>,\n runDir: string,\n): Promise<void> {\n const client = createHostedClient(tenant)\n\n function snapshotFromCampaign(\n index: number,\n surface: MutableSurface | undefined,\n campaign: RunImprovementLoopResult<TArtifact, TScenario>['baselineCampaign'],\n durationMs: number,\n ): EvalRunGenerationSnapshot {\n const cells: EvalRunCellScore[] = campaign.cells.map((cell) => {\n const judgeScores = Object.values(cell.judgeScores)\n const composite =\n judgeScores.length === 0\n ? 0\n : judgeScores.reduce((s, j) => s + j.composite, 0) / judgeScores.length\n return {\n scenarioId: cell.scenarioId,\n rep: cell.rep,\n compositeMean: composite,\n dimensions: Object.fromEntries(\n Object.entries(cell.judgeScores).map(([name, score]) => [name, score.dimensions]),\n ),\n errorMessage: cell.error ?? undefined,\n }\n })\n const compositeMean =\n cells.length === 0 ? 0 : cells.reduce((s, c) => s + c.compositeMean, 0) / cells.length\n return {\n index,\n surfaceHash:\n typeof surface === 'string'\n ? hashString(surface)\n : hashString(JSON.stringify(surface ?? '')),\n surface,\n cells,\n compositeMean,\n costUsd: campaign.aggregates.totalCostUsd,\n durationMs,\n }\n }\n\n const generations: EvalRunGenerationSnapshot[] = []\n // Baseline as generation 0.\n generations.push(snapshotFromCampaign(0, opts.baselineSurface, raw.baselineCampaign, 0))\n // Improvement generations as 1..N. Substrate stores per-surface campaigns\n // per generation — we summarize the WINNING surface per generation here.\n for (const gen of raw.generations) {\n const winner = gen.surfaces.reduce(\n (best, s) =>\n s.campaign.aggregates.cellsExecuted > 0 &&\n (best === undefined || averageComposite(s.campaign) > averageComposite(best.campaign))\n ? s\n : best,\n gen.surfaces[0],\n )\n if (!winner) continue\n generations.push(\n snapshotFromCampaign(gen.record.generationIndex + 1, winner.surface, winner.campaign, 0),\n )\n }\n\n const event: EvalRunEvent = {\n runId: `${runDir}#${Date.now()}`,\n runDir,\n timestamp: new Date().toISOString(),\n status: 'finished',\n labels: opts.hostedLabels ?? {},\n baseline: generations[0],\n generations,\n gateDecision: summary.gateDecision,\n holdoutLift: summary.lift,\n totalCostUsd: summary.totalCostUsd,\n totalDurationMs: summary.durationMs,\n insightReport: summary.insight,\n }\n\n await client.ingestEvalRun(event)\n}\n\nfunction averageComposite(\n campaign: RunImprovementLoopResult<unknown, Scenario>['baselineCampaign'],\n): number {\n const aggs = Object.values(campaign.aggregates.byScenario)\n return aggs.length === 0 ? 0 : aggs.reduce((s, a) => s + a.meanComposite, 0) / aggs.length\n}\n\nfunction hashString(s: string): string {\n let h = 2166136261 >>> 0\n for (let i = 0; i < s.length; i++) {\n h ^= s.charCodeAt(i)\n h = Math.imul(h, 16777619) >>> 0\n }\n return h.toString(16).padStart(8, '0')\n}\n\n/**\n * Adapt campaign cells into the `RunRecord` shape `analyzeRuns()` consumes.\n * Each cell becomes one run; `candidateId` is the caller-supplied label so\n * baseline + winner pair cleanly on `(experimentId, seed)`.\n */\nfunction cellsToRunRecords<TArtifact>(\n cells: ReadonlyArray<CampaignCellResult<TArtifact>>,\n candidateId: 'baseline' | 'winner',\n runId: string,\n): RunRecord[] {\n return cells.map((cell) => {\n const perJudge: Record<string, Record<string, number>> = {}\n const perDimMeanAccum: Record<string, { sum: number; n: number }> = {}\n let compositeSum = 0\n let compositeCount = 0\n for (const [judgeId, score] of Object.entries(cell.judgeScores)) {\n perJudge[judgeId] = { ...score.dimensions }\n for (const [dim, value] of Object.entries(score.dimensions)) {\n if (!Number.isFinite(value)) continue\n const accum = perDimMeanAccum[dim] ?? { sum: 0, n: 0 }\n accum.sum += value\n accum.n += 1\n perDimMeanAccum[dim] = accum\n }\n if (Number.isFinite(score.composite)) {\n compositeSum += score.composite\n compositeCount += 1\n }\n }\n const perDimMean: Record<string, number> = {}\n for (const [dim, { sum, n }] of Object.entries(perDimMeanAccum)) {\n perDimMean[dim] = n === 0 ? 0 : sum / n\n }\n const composite = compositeCount === 0 ? 0 : compositeSum / compositeCount\n const judgeScores: JudgeScoresRecord = {\n perJudge,\n perDimMean,\n composite,\n }\n return {\n runId: `${runId}::${candidateId}::${cell.cellId}`,\n experimentId: runId,\n candidateId,\n // Pair on (scenarioId, rep) — analyzeRuns pairs on (experimentId, seed).\n // Synthesize a stable seed for that pairing.\n seed:\n cell.rep * 1_000_000 +\n hashString(cell.scenarioId)\n .slice(0, 6)\n .split('')\n .reduce((a, c) => (a * 31 + c.charCodeAt(0)) >>> 0, 0),\n model: 'campaign-cell',\n promptHash: 'sha256:cell',\n configHash: 'sha256:cell',\n commitSha: 'cell',\n wallMs: cell.durationMs,\n costUsd: cell.costUsd,\n tokenUsage: { input: 0, output: 0 },\n outcome: {\n holdoutScore: composite,\n raw: {},\n judgeScores,\n },\n splitTag: 'holdout',\n ...(cell.error ? { failureMode: cell.error } : {}),\n } satisfies RunRecord\n })\n}\n","/**\n * # `intake/feedback-table` — multi-rater approve/reject corpus → `RunRecord[]`.\n *\n * The generic shape behind Obsidian's `#approved` / `#rejected` tags, a\n * Google Sheet, a Postgres `feedback` table, or any CSV with ratings.\n *\n * Caller supplies one row per (run, rater) tuple plus per-run metadata; the\n * adapter rolls them up into the substrate-canonical `RunRecord` shape so\n * `analyzeRuns({ runs, raterScores })` can produce inter-rater agreement,\n * disagreement triage, and downstream recommendations.\n *\n * Per-run `RunRecord.outcome.searchScore` is the rater-mean rating\n * (normalised to 0..1 when scale is supplied); `outcome.raw` carries the\n * per-rater scores keyed by rater id for downstream attribution.\n */\n\nimport type { JudgeScoresRecord, RunOutcome, RunRecord, RunSplitTag } from '../../run-record'\n\nexport interface FeedbackTableRow {\n /** Stable id for this run — the unit a rater scored. Drives pairing\n * across analysis primitives. */\n runId: string\n /** Identifier of the rater that produced this rating. */\n rater: string\n /** The rating itself. Accepts boolean (approve/reject), 0..1 scalar,\n * or any numeric scale — see `scale`. */\n rating: number | boolean\n /** Optional metadata carried through to `RunRecord.outcome.raw` and the\n * custom-shape metadata bag. */\n metadata?: Record<string, unknown>\n}\n\nexport interface FeedbackTableMeta {\n runId: string\n /** When omitted, defaults to `'feedback-corpus'`. Used to group related\n * runs in `analyzeRuns()` lift analysis. */\n experimentId?: string\n /** When omitted, defaults to `runId` — each run is its own candidate. */\n candidateId?: string\n /** Cost in USD, when available. Set to 0 when unknown — the consumer's\n * cost analysis sections will collapse gracefully. */\n costUsd?: number\n /** Wall-clock ms, when available. Defaults to 0. */\n wallMs?: number\n /** Model identifier including snapshot. Default `unknown@unknown`. */\n model?: string\n /** Optional sha256 of the prompt; default `'sha256:unknown'`. */\n promptHash?: string\n /** Default `'sha256:unknown'`. */\n configHash?: string\n /** Default `'unknown'`. */\n commitSha?: string\n /** Default `'holdout'` — feedback corpora are by nature the holdout\n * signal a closed-loop improvement aims at. */\n splitTag?: RunSplitTag\n /** Free-form metadata available to consumers via the cast-out path on\n * the resulting RunRecord. */\n extras?: Record<string, unknown>\n}\n\nexport interface FromFeedbackTableOptions {\n /** Per-(run, rater) ratings. */\n ratings: FeedbackTableRow[]\n /** Per-run metadata. When a runId appears in `ratings` but not here, the\n * adapter synthesises minimal metadata with defaults documented above. */\n meta?: FeedbackTableMeta[]\n /** Rating scale. Provide `{ min, max }` for non-0..1 numeric scales.\n * Booleans are normalised: true → 1, false → 0. Default: assumes\n * ratings are already 0..1. */\n scale?: { min: number; max: number }\n /** When true, the rater scores are emitted into `raterScores` (a sibling\n * array `analyzeRuns()` accepts) instead of being averaged into the\n * run's `outcome.searchScore`. Default `true` — preserves rater-level\n * signal for inter-rater analysis. */\n emitRaterScores?: boolean\n}\n\nexport interface FromFeedbackTableResult {\n runs: RunRecord[]\n /** Rater-level scores ready to pass into `analyzeRuns({ raterScores })`\n * for inter-rater agreement + disagreement triage. */\n raterScores: Array<{ runId: string; rater: string; score: number }>\n}\n\nexport function fromFeedbackTable(opts: FromFeedbackTableOptions): FromFeedbackTableResult {\n const { ratings, meta = [], scale, emitRaterScores = true } = opts\n const metaByRun = new Map(meta.map((m) => [m.runId, m]))\n\n // Normalise per-rating to a 0..1 score.\n const normalise = (rating: number | boolean): number => {\n if (typeof rating === 'boolean') return rating ? 1 : 0\n if (!Number.isFinite(rating)) return Number.NaN\n if (!scale) return rating\n const { min, max } = scale\n if (max === min) return rating\n return (rating - min) / (max - min)\n }\n\n // Group ratings by runId.\n const byRun = new Map<string, FeedbackTableRow[]>()\n for (const row of ratings) {\n const list = byRun.get(row.runId) ?? []\n list.push(row)\n byRun.set(row.runId, list)\n }\n\n const runs: RunRecord[] = []\n const raterScores: FromFeedbackTableResult['raterScores'] = []\n\n for (const [runId, rowsForRun] of byRun) {\n const normalised = rowsForRun\n .map((r) => ({ rater: r.rater, score: normalise(r.rating) }))\n .filter((r) => Number.isFinite(r.score))\n if (normalised.length === 0) continue\n\n const meanScore = normalised.reduce((s, r) => s + r.score, 0) / normalised.length\n\n const runMeta = metaByRun.get(runId) ?? ({ runId } as FeedbackTableMeta)\n\n const judgeScores: JudgeScoresRecord = {\n perJudge: Object.fromEntries(normalised.map((r) => [r.rater, { rating: r.score }])),\n perDimMean: { rating: meanScore },\n composite: meanScore,\n }\n\n const outcome: RunOutcome = {\n // Feedback corpora ARE the holdout signal — score lands on\n // `holdoutScore` so downstream substrate primitives (`paretoChart`,\n // promotion gates) read it correctly by default.\n holdoutScore: meanScore,\n raw: Object.fromEntries(normalised.map((r) => [`rater:${r.rater}`, r.score])),\n judgeScores,\n }\n\n runs.push({\n runId,\n experimentId: runMeta.experimentId ?? 'feedback-corpus',\n candidateId: runMeta.candidateId ?? runId,\n seed: 0,\n model: runMeta.model ?? 'unknown@unknown',\n promptHash: runMeta.promptHash ?? 'sha256:unknown',\n configHash: runMeta.configHash ?? 'sha256:unknown',\n commitSha: runMeta.commitSha ?? 'unknown',\n wallMs: runMeta.wallMs ?? 0,\n costUsd: runMeta.costUsd ?? 0,\n tokenUsage: { input: 0, output: 0 },\n outcome,\n splitTag: runMeta.splitTag ?? 'holdout',\n } as RunRecord)\n\n if (emitRaterScores) {\n for (const r of normalised) raterScores.push({ runId, rater: r.rater, score: r.score })\n }\n }\n\n return { runs, raterScores }\n}\n","/**\n * # `intake/otel-spans` — OTel `TraceSpanEvent[]` → `RunRecord[]`.\n *\n * Turns an existing observability stream into the substrate-canonical\n * `RunRecord` shape so consumers with logs but no eval discipline can\n * call `analyzeRuns()` against their production traffic immediately.\n *\n * Pivot rule: spans are grouped by `tangle.runId` (the same attribute the\n * hosted-tier wire format uses) or, when absent, by `traceId`. One group\n * becomes one `RunRecord`. The root span (no `parentSpanId`) supplies:\n *\n * - `runId` (the group key)\n * - `wallMs` from `endTimeUnixNano - startTimeUnixNano`\n * - `model` from `gen_ai.request.model` / `llm.model` / `tangle.model`\n * - cost from `cost.usd` / `gen_ai.usage.cost_usd` / `tangle.cost.usd`\n * - token usage from `gen_ai.usage.{input,output}_tokens`\n * - `outcome.searchScore` from `tangle.score` / `eval.score` when\n * present; `outcome.raw` collects every numeric attribute.\n *\n * Spans that ERRORed (`status.code === 'ERROR'`) populate `failureMode`\n * with their `name` so `analyzeRuns()`'s failure clustering sees them.\n */\n\nimport type { TraceSpanEvent } from '../../hosted/types'\nimport type {\n JudgeScoresRecord,\n RunOutcome,\n RunRecord,\n RunSplitTag,\n RunTokenUsage,\n} from '../../run-record'\n\nconst SCORE_KEYS = ['tangle.score', 'eval.score', 'score']\nconst MODEL_KEYS = ['tangle.model', 'gen_ai.request.model', 'llm.model', 'model']\nconst COST_KEYS = ['tangle.cost.usd', 'gen_ai.usage.cost_usd', 'cost.usd', 'cost']\nconst INPUT_TOKEN_KEYS = ['gen_ai.usage.input_tokens', 'tangle.tokens.in', 'tokens.in']\nconst OUTPUT_TOKEN_KEYS = ['gen_ai.usage.output_tokens', 'tangle.tokens.out', 'tokens.out']\nconst PROMPT_HASH_KEYS = ['tangle.prompt_hash', 'prompt.hash']\nconst CONFIG_HASH_KEYS = ['tangle.config_hash', 'config.hash']\n\nexport interface FromOtelSpansOptions {\n spans: TraceSpanEvent[]\n /** Default split tag for synthesized records. Defaults to `'holdout'`. */\n defaultSplit?: RunSplitTag\n /** Default `experimentId` when not present on any span. */\n experimentId?: string\n}\n\nexport function fromOtelSpans(opts: FromOtelSpansOptions): RunRecord[] {\n const { spans, defaultSplit = 'holdout', experimentId = 'otel-corpus' } = opts\n const grouped = groupSpans(spans)\n\n const runs: RunRecord[] = []\n for (const [groupKey, groupSpans] of grouped) {\n const root = findRoot(groupSpans)\n if (!root) continue\n\n const wallMs = Math.max(0, (root.endTimeUnixNano - root.startTimeUnixNano) / 1_000_000)\n const model = readAttrString(groupSpans, MODEL_KEYS) ?? 'unknown@unknown'\n const costUsd = readAttrNumber(groupSpans, COST_KEYS) ?? 0\n const inputTokens = readAttrNumber(groupSpans, INPUT_TOKEN_KEYS) ?? 0\n const outputTokens = readAttrNumber(groupSpans, OUTPUT_TOKEN_KEYS) ?? 0\n const promptHash = readAttrString(groupSpans, PROMPT_HASH_KEYS) ?? 'sha256:unknown'\n const configHash = readAttrString(groupSpans, CONFIG_HASH_KEYS) ?? 'sha256:unknown'\n const score = readAttrNumber(groupSpans, SCORE_KEYS)\n\n const rawNumeric = collectNumericAttrs(groupSpans)\n const tokenUsage: RunTokenUsage = {\n input: inputTokens,\n output: outputTokens,\n }\n\n const judgeScores: JudgeScoresRecord | undefined =\n score !== undefined\n ? {\n perJudge: { 'otel-derived': { score } },\n perDimMean: { score },\n composite: score,\n }\n : undefined\n\n const errorSpan = groupSpans.find((s) => s.status?.code === 'ERROR')\n const outcome: RunOutcome = {\n ...(opts.defaultSplit === 'search' ? { searchScore: score } : { holdoutScore: score }),\n raw: rawNumeric,\n ...(judgeScores ? { judgeScores } : {}),\n }\n\n runs.push({\n runId: groupKey,\n experimentId,\n candidateId: (root.attributes['tangle.candidateId'] as string | undefined) ?? 'otel-default',\n seed: 0,\n model,\n promptHash,\n configHash,\n commitSha: (root.attributes['tangle.commit_sha'] as string | undefined) ?? 'unknown',\n wallMs,\n costUsd,\n tokenUsage,\n outcome,\n splitTag: defaultSplit,\n ...(errorSpan ? { failureMode: errorSpan.name } : {}),\n } as RunRecord)\n }\n return runs\n}\n\n// ── Internal helpers ────────────────────────────────────────────────\n\nfunction groupSpans(spans: TraceSpanEvent[]): Map<string, TraceSpanEvent[]> {\n const m = new Map<string, TraceSpanEvent[]>()\n for (const span of spans) {\n const key = (span['tangle.runId'] as string | undefined) ?? span.traceId\n const list = m.get(key) ?? []\n list.push(span)\n m.set(key, list)\n }\n return m\n}\n\nfunction findRoot(group: TraceSpanEvent[]): TraceSpanEvent | undefined {\n return group.find((s) => !s.parentSpanId) ?? group[0]\n}\n\nfunction readAttrString(spans: TraceSpanEvent[], keys: string[]): string | undefined {\n for (const span of spans) {\n for (const key of keys) {\n const v = span.attributes[key]\n if (typeof v === 'string' && v.length > 0) return v\n }\n }\n return undefined\n}\n\nfunction readAttrNumber(spans: TraceSpanEvent[], keys: string[]): number | undefined {\n for (const span of spans) {\n for (const key of keys) {\n const v = span.attributes[key]\n if (typeof v === 'number' && Number.isFinite(v)) return v\n if (typeof v === 'string') {\n const parsed = Number(v)\n if (Number.isFinite(parsed)) return parsed\n }\n }\n }\n return undefined\n}\n\nfunction collectNumericAttrs(spans: TraceSpanEvent[]): Record<string, number> {\n const raw: Record<string, number> = {}\n for (const span of spans) {\n for (const [k, v] of Object.entries(span.attributes)) {\n if (typeof v === 'number' && Number.isFinite(v)) raw[k] = v\n }\n }\n return raw\n}\n"],"mappings":"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","names":["mean","i","groupSpans"]}
@@ -1,4 +1,4 @@
1
- export { E as EvalRunCellScore, d as EvalRunEvent, e as EvalRunGenerationSnapshot, f as EvalRunStatus, g as HOSTED_WIRE_VERSION, H as HostedClient, h as HostedIngestHeaders, a as HostedTenant, i as HostedWireVersion, j as IngestEvalRunsRequest, k as IngestResponse, l as IngestTracesRequest, T as TraceSpanEvent, m as createHostedClient } from '../index-BRxz6qov.js';
1
+ export { E as EvalRunCellScore, d as EvalRunEvent, e as EvalRunGenerationSnapshot, f as EvalRunStatus, g as HOSTED_WIRE_VERSION, H as HostedClient, h as HostedIngestHeaders, a as HostedTenant, i as HostedWireVersion, j as IngestEvalRunsRequest, k as IngestResponse, l as IngestTracesRequest, T as TraceSpanEvent, m as createHostedClient } from '../index-DQHtWQ57.js';
2
2
  import '../types-Dbj5gu8n.js';
3
3
  import '../summary-report-B7gNRX-r.js';
4
4
  import '../run-record-BGY6bHRh.js';
@@ -44,6 +44,14 @@ interface InsightReport {
44
44
  costQuality: {
45
45
  cost: ScalarDistribution;
46
46
  pareto: ParetoFigureSpec;
47
+ /** Set when the cost/quality view is degraded because the input data
48
+ * doesn't fully support it — e.g. all `costUsd` were zero, or only a
49
+ * single candidate appears (so the Pareto is a single point). The
50
+ * named fields name the degraded sub-view, free-text the reason. */
51
+ degraded?: {
52
+ cost?: string;
53
+ pareto?: string;
54
+ };
47
55
  };
48
56
  /** Per-judge calibration + bias detection. Populated for every judge name
49
57
  * that appears in `outcome.judgeScores`. Bias fields require either a
@@ -89,6 +97,14 @@ interface ScalarDistribution {
89
97
  max: number;
90
98
  /** Histogram bins using `agent-eval`'s `gainHistogram` primitive. */
91
99
  histogram: GainDistributionBin[];
100
+ /** Worst-N runs by score, ascending. Populated for the composite
101
+ * distribution so the report names the runs a customer should
102
+ * inspect first. Undefined when the distribution was computed from a
103
+ * raw value list with no run identity (e.g. cost). */
104
+ tailRuns?: Array<{
105
+ runId: string;
106
+ score: number;
107
+ }>;
92
108
  }
93
109
  interface JudgeInsight {
94
110
  /** Number of times this judge scored a run. */
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.50.1",
5
+ "version": "0.51.0",
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",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-eval",
3
- "version": "0.50.1",
3
+ "version": "0.51.0",
4
4
  "description": "Substrate for self-improving agents: traces, verifiable rewards, preferences, GEPA / reflective mutation, auto-research, replay, sequential anytime-valid stats, and release gates.",
5
5
  "homepage": "https://github.com/tangle-network/agent-eval#readme",
6
6
  "repository": {