@tangle-network/agent-runtime 0.74.0 → 0.75.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lifecycle/apply.ts","../src/lifecycle/compose.ts","../src/lifecycle/marginal-lift.ts","../src/lifecycle/dedupe.ts","../src/lifecycle/drift-watch.ts","../src/lifecycle/gate.ts","../src/lifecycle/prompt-generator.ts","../src/lifecycle/registry.ts","../src/lifecycle/run-lifecycle.ts","../src/lifecycle/skill-generator.ts","../src/lifecycle/tool-build.ts","../src/lifecycle/tool-generator.ts"],"sourcesContent":["/**\n * `applyArtifact` — merge one `ProfileArtifact` onto an `AgentProfile`.\n *\n * This is the deterministic bridge between the artifact catalog and the §1.5\n * profile law: each `ArtifactKind` lands on exactly one profile field. The merge\n * is shallow-immutable — the input profile is never mutated; a new profile with\n * the artifact applied is returned. This is the ONE place that knows how a\n * `kind` maps to a profile field, so both the registry's `compose` and\n * `measureMarginalLift`'s with/without ablation share a single source of truth.\n */\n\nimport type { AgentProfile } from '@tangle-network/agent-interface'\nimport type { ProfileArtifact } from './types'\n\n/**\n * Return a new profile with `artifact` merged onto `base`. Keyed kinds\n * (`tool`/`mcp`/`hook`/`subagent`) land under `artifact.key` (falling back to\n * `artifact.id`). `prompt` appends an instruction line; `skill` appends a\n * resource ref. Existing keys are overwritten by the artifact (the artifact is\n * the candidate being measured/promoted, so it wins on conflict).\n */\nexport function applyArtifact(base: AgentProfile, artifact: ProfileArtifact): AgentProfile {\n switch (artifact.kind) {\n case 'prompt': {\n const payload = artifact.payload as { instruction: string }\n const instructions = [...(base.prompt?.instructions ?? []), payload.instruction]\n return { ...base, prompt: { ...base.prompt, instructions } }\n }\n case 'skill': {\n const payload = artifact.payload as ProfileArtifact<'skill'>['payload']\n const skills = [...(base.resources?.skills ?? []), payload.resource]\n return { ...base, resources: { ...base.resources, skills } }\n }\n case 'tool': {\n const payload = artifact.payload as ProfileArtifact<'tool'>['payload']\n const key = keyOf(artifact)\n return { ...base, tools: { ...base.tools, [key]: payload.enabled } }\n }\n case 'mcp': {\n const payload = artifact.payload as ProfileArtifact<'mcp'>['payload']\n const key = keyOf(artifact)\n return { ...base, mcp: { ...base.mcp, [key]: payload.server } }\n }\n case 'hook': {\n const payload = artifact.payload as ProfileArtifact<'hook'>['payload']\n const existing = base.hooks?.[payload.event] ?? []\n return {\n ...base,\n hooks: { ...base.hooks, [payload.event]: [...existing, ...payload.commands] },\n }\n }\n case 'subagent': {\n const payload = artifact.payload as ProfileArtifact<'subagent'>['payload']\n const key = keyOf(artifact)\n return { ...base, subagents: { ...base.subagents, [key]: payload.profile } }\n }\n }\n}\n\n/** Apply many artifacts left-to-right; later artifacts win on key conflicts. */\nexport function applyArtifacts(\n base: AgentProfile,\n artifacts: readonly ProfileArtifact[],\n): AgentProfile {\n return artifacts.reduce((profile, artifact) => applyArtifact(profile, artifact), base)\n}\n\n/** The profile-field key a keyed artifact lands under: explicit `key` or `id`. */\nfunction keyOf(artifact: ProfileArtifact): string {\n return artifact.key ?? artifact.id\n}\n","/**\n * `composeProfile` — fold the top-k active artifacts back into a profile.\n *\n * This is the \"give it all the passing options at once\" step. After the loop has\n * generated, measured, and promoted artifacts, `composeProfile` selects the\n * highest-lift ACTIVE ones (those promoted WITH a measured held-back lift) and\n * applies them onto a baseline profile via the single `applyArtifact` bridge.\n *\n * It differs from the registry's raw `compose` in two ways the lifecycle needs:\n * 1. It ranks by MEASURED LIFT and takes the top `k`, rather than applying\n * every promoted artifact in registration order. The best pieces go in\n * first; a `k` budget keeps the composed profile from accreting marginal\n * wins without bound.\n * 2. It enforces the lifecycle INVARIANT — only artifacts with a finite\n * `liftOf` are eligible. An artifact flipped to `promoted` without a lift\n * receipt is invisible here, by construction.\n */\n\nimport type { AgentProfile } from '@tangle-network/agent-interface'\nimport { applyArtifacts } from './apply'\nimport type { ArtifactRegistry } from './registry'\nimport type { ArtifactKind, ProfileArtifact } from './types'\n\nexport interface ComposeProfileOptions {\n /** Cap on how many artifacts to fold in. Default: all eligible. */\n k?: number\n /** Restrict to one surface (e.g. only fold in skills). Default: all kinds. */\n kind?: ArtifactKind\n}\n\n/**\n * Return a new profile with the top-`k` active artifacts (highest measured lift\n * first) applied onto `base`.\n *\n * \"Active\" = promoted WITH a finite measured lift (`registry.liftOf` returns a\n * number) — the lifecycle invariant. Ties in lift fall back to registration\n * order (stable). With no `k`, every eligible artifact is folded in.\n *\n * @param registry the catalog the loop populated\n * @param base the baseline profile to fold artifacts onto (the empty profile\n * on a cold start)\n * @param opts `k` (top-k budget) and an optional `kind` filter\n *\n * @example Cold start — fold the single best distilled skill back in:\n * const composed = composeProfile(registry, emptyProfile, { kind: 'skill', k: 1 })\n */\nexport function composeProfile(\n registry: ArtifactRegistry,\n base: AgentProfile,\n opts: ComposeProfileOptions = {},\n): AgentProfile {\n const eligible: Array<{ artifact: ProfileArtifact; lift: number }> = []\n for (const artifact of registry.list({ status: 'active', kind: opts.kind })) {\n const lift = registry.liftOf(artifact.id)\n // Invariant: no measured lift ⇒ not eligible for a lift-ranked compose.\n if (lift === undefined) continue\n eligible.push({ artifact, lift })\n }\n\n // Highest lift first; stable on ties (Array.prototype.sort is stable in V8).\n eligible.sort((a, b) => b.lift - a.lift)\n\n const k = opts.k ?? eligible.length\n const selected = eligible.slice(0, Math.max(0, k)).map((e) => e.artifact)\n return applyArtifacts(base, selected)\n}\n","/**\n * `measureMarginalLift` — the with-vs-without ablation for one artifact.\n *\n * \"What does THIS one piece add?\" is the question a lifecycle has to answer\n * before it can promote anything. The answer is an ablation: score the baseline\n * profile, score the baseline-plus-candidate profile, and report the delta. This\n * is the marginal contribution of the artifact — the same shape the project's\n * `OutcomeMeasurement` reports for an apply pass, but isolated to ONE artifact so\n * a registry can rank candidates by what they individually earn.\n *\n * The measurement is selector-agnostic: it takes an `EvalRunner` the caller\n * supplies (any function that scores a profile and reports cost), runs it twice,\n * and subtracts. It never judges, never picks a winner, never re-scores on a\n * holdout — those are the gate's job. It only quantifies the ablation so the gate\n * has a real number to decide on.\n */\n\nimport type { RunRecord } from '@tangle-network/agent-eval'\nimport type { AgentProfile } from '@tangle-network/agent-interface'\nimport { applyArtifact } from './apply'\nimport type { ProfileArtifact } from './types'\n\n/**\n * The result of running an eval over ONE profile: a composite score and the cost\n * to obtain it. This mirrors the project's score/cost convention (`composite`\n * from `OutcomeMeasurement`, `costUsd` from `LoopResult`), so a caller can pass a\n * thin wrapper over `runLoop` / `runBenchmark` / `runAgentEval` directly.\n */\nexport interface EvalResult {\n /** Composite score in `[0, 1]` (higher is better) for the profile under test. */\n composite: number\n /** USD cost to produce this result. */\n costUsd: number\n /**\n * Per-task records the run produced, when the runner emits them. The marginal\n * lift only needs `composite`, but the held-out promotion gate (`HeldOutGate`)\n * pairs candidate vs baseline per-task holdout records by (experimentId, seed)\n * — so a runner feeding `heldOutPromotionGate` MUST populate this with rows\n * carrying both `search` and `holdout` split scores. Omit it for evals scored\n * to a single composite (then use `thresholdPromotionGate`).\n */\n runs?: RunRecord[]\n /** Optional opaque passthrough (per-task cells, the raw report, …). */\n details?: unknown\n}\n\n/**\n * Scores a profile. The caller wires this to whatever eval they run — a\n * `runLoop` rollout, a `runBenchmark` campaign, a `runAgentEval` cohort — and\n * returns the composite + cost. `signal` is forwarded for cancellation.\n */\nexport type EvalRunner = (profile: AgentProfile, signal?: AbortSignal) => Promise<EvalResult>\n\nexport interface MeasureMarginalLiftOptions {\n /** The profile the artifact is measured ON TOP OF (the \"without\" arm). */\n baseline: AgentProfile\n /** The single artifact whose marginal contribution we want. */\n candidate: ProfileArtifact\n /** The eval that scores a profile. Run once per arm (twice total). */\n evalRunner: EvalRunner\n /**\n * A pre-computed baseline result, to skip the \"without\" run when the caller\n * already scored the baseline (e.g. measuring several candidates against the\n * same baseline). When set, the baseline arm is NOT re-run.\n */\n baselineResult?: EvalResult\n /** Forwarded to both `evalRunner` invocations for cancellation. */\n signal?: AbortSignal\n}\n\n/**\n * The marginal lift of one artifact: the with/without ablation.\n *\n * `scoreDelta = with.composite − without.composite`. A positive `scoreDelta` is\n * the evidence a gate needs to promote; a negative one is the signal to drop the\n * artifact. `costDelta` is the extra USD the artifact costs (often positive — a\n * new tool/MCP adds calls) and lets the gate weigh lift against spend.\n */\nexport interface MarginalLift {\n /** The artifact id this measurement is for (stable, from the registry). */\n artifactId: string\n /** Eval of `applyArtifact(baseline, candidate)`. */\n withArtifact: EvalResult\n /** Eval of `baseline` alone. */\n withoutArtifact: EvalResult\n /** `withArtifact.composite − withoutArtifact.composite`. */\n scoreDelta: number\n /** `withArtifact.costUsd − withoutArtifact.costUsd`. */\n costDelta: number\n}\n\n/**\n * Run the with/without ablation for `candidate` over `baseline` and return its\n * marginal score/cost contribution.\n *\n * The \"without\" arm scores the baseline profile unchanged; the \"with\" arm scores\n * `applyArtifact(baseline, candidate)`. Both use the same `evalRunner`, so the\n * delta isolates the artifact's effect (eval method held constant). The baseline\n * arm is skipped when `baselineResult` is supplied.\n *\n * @example\n * const lift = await measureMarginalLift({\n * baseline,\n * candidate: registry.get(id)!,\n * evalRunner: (profile) => scoreProfileOnCohort(profile),\n * })\n * if (lift.scoreDelta > 0) registry.promote(lift.artifactId)\n */\nexport async function measureMarginalLift(opts: MeasureMarginalLiftOptions): Promise<MarginalLift> {\n const { baseline, candidate, evalRunner, baselineResult, signal } = opts\n\n const withoutArtifact = baselineResult ?? (await evalRunner(baseline, signal))\n const withProfile = applyArtifact(baseline, candidate)\n const withArtifact = await evalRunner(withProfile, signal)\n\n return {\n artifactId: candidate.id,\n withArtifact,\n withoutArtifact,\n scoreDelta: withArtifact.composite - withoutArtifact.composite,\n costDelta: withArtifact.costUsd - withoutArtifact.costUsd,\n }\n}\n","/**\n * `dedupeArtifacts` — retire the redundant half of a non-stacking pair.\n *\n * The lifecycle promotes each artifact on its OWN marginal lift, in isolation.\n * But two artifacts can each earn lift alone yet teach the agent the SAME thing —\n * a distilled \"check state first\" skill and a \"verify before acting\" prompt line,\n * say. Composed together they don't add up: the agent already learned the tactic\n * from the first, so the second buys little. Keeping both is wasted profile\n * surface (more context, more cost, more ways to conflict) for no extra score.\n *\n * `dedupeArtifacts` is the judge that catches this. For each pair of `active`\n * artifacts it measures whether their lifts STACK: it scores the baseline, each\n * artifact alone, and BOTH together, then compares the combined lift against the\n * sum of the individual lifts. When `combined < (a + b) − tolerance`, the pair\n * overlaps — they do not stack — and the weaker member (lower individual lift) is\n * `retire`d. Retirement is terminal: the kept member already delivers the shared\n * value, so the loop should not re-promote the redundant one.\n *\n * The \"judge\" is a MEASUREMENT, not an LLM verdict — it reuses the same ablation\n * machinery (`measureMarginalLift` + the caller's `EvalRunner`) the rest of the\n * lifecycle runs on, so the selector≠judge firewall holds and there is no new\n * execution model. It is surface-agnostic: any two artifacts (skill+prompt,\n * tool+tool, …) are compared identically, because stacking is measured on the\n * composed profile via the one `applyArtifacts` bridge.\n */\n\nimport type { AgentProfile } from '@tangle-network/agent-interface'\nimport { ValidationError } from '../errors'\nimport { applyArtifacts } from './apply'\nimport { type EvalResult, type EvalRunner, measureMarginalLift } from './marginal-lift'\nimport type { ArtifactRegistry } from './registry'\nimport type { ProfileArtifact } from './types'\n\nexport interface DedupeOptions {\n /** The registry whose `active` artifacts are pairwise stack-tested. Mutated in\n * place: the weaker member of each non-stacking pair is retired. */\n registry: ArtifactRegistry\n /** The baseline profile the stacking ablation runs on top of. The \"without\"\n * arm is scored once and shared across every pair. */\n baseline: AgentProfile\n /** Scores a profile on the held-back split. Called for the baseline, each\n * artifact alone, and each candidate pair together. */\n evalRunner: EvalRunner\n /**\n * The stacking tolerance. A pair is judged non-stacking (redundant) when the\n * combined lift falls SHORT of the sum of individual lifts by more than this:\n * `combined < a + b − tolerance`. A small positive tolerance absorbs eval\n * noise so only a real overlap retires an artifact. Default 0 — any shortfall\n * counts as non-stacking (use a positive value on noisy live evals).\n */\n tolerance?: number\n /** Restrict dedupe to one surface (only compare skills against skills, …).\n * Default: every `active` artifact is a candidate, across kinds. */\n kind?: ProfileArtifact['kind']\n /** A pre-computed baseline result, to skip the shared \"without\" run. */\n baselineResult?: EvalResult\n /** Cooperative cancellation, forwarded to every `evalRunner` call. */\n signal?: AbortSignal\n}\n\n/** The stacking verdict for one pair of active artifacts. */\nexport interface PairStackCheck {\n /** Ids of the two artifacts compared, in (a, b) order as examined. */\n pair: [string, string]\n /** Individual held-back lift of the first artifact alone (re-measured). */\n liftA: number\n /** Individual held-back lift of the second artifact alone (re-measured). */\n liftB: number\n /** Held-back lift of BOTH artifacts composed together. */\n combinedLift: number\n /** `combinedLift − (liftA + liftB)`: ≥ −tolerance ⇒ they stack; below ⇒ they\n * overlap (redundant). */\n stackGap: number\n /** Whether the pair was judged non-stacking (redundant). */\n redundant: boolean\n /** The id retired when redundant (the weaker member), else `undefined`. */\n retiredId?: string\n}\n\nexport interface DedupeResult {\n /** One verdict per examined pair, in iteration order. Pairs where one member\n * was already retired by an earlier pair this cycle are skipped. */\n checks: PairStackCheck[]\n /** Ids retired this cycle (the weaker member of each non-stacking pair). */\n retired: string[]\n /** The shared baseline eval (the \"without\" arm, measured once). */\n baselineResult: EvalResult\n}\n\n/**\n * Pairwise stack-test the `active` artifacts and retire the redundant half of\n * each non-stacking pair.\n *\n * For every unordered pair of active artifacts (optionally within one `kind`),\n * the combined lift is compared against the sum of individual lifts. A pair is\n * non-stacking when `combinedLift < liftA + liftB − tolerance`; the lower-lift\n * member is retired (ties retire the second-examined). An artifact retired by one\n * pair is removed from the remaining comparisons that cycle, so a cluster of\n * three mutually-redundant artifacts collapses to its single strongest member.\n *\n * Cost is one shared baseline run, one re-measure per active artifact (cached\n * across the pairs it appears in), and one combined run per still-eligible pair.\n *\n * @example Retire skills that teach the same tactic as a stronger one:\n * const out = await dedupeArtifacts({ registry, baseline, evalRunner, kind: 'skill' })\n * if (out.retired.length) report(`retired ${out.retired.length} redundant skills`)\n */\nexport async function dedupeArtifacts(opts: DedupeOptions): Promise<DedupeResult> {\n const tolerance = opts.tolerance ?? 0\n if (!Number.isFinite(tolerance) || tolerance < 0) {\n throw new ValidationError(\n `dedupeArtifacts: tolerance must be a finite, non-negative number (got ${tolerance})`,\n )\n }\n\n const active = opts.registry.list({ status: 'active', kind: opts.kind })\n const baselineResult = opts.baselineResult ?? (await opts.evalRunner(opts.baseline, opts.signal))\n\n // Re-measure each artifact's individual lift ONCE (cached across every pair it\n // appears in) so the stack comparison is apples-to-apples in this eval cycle.\n const liftCache = new Map<string, number>()\n const liftOf = async (artifact: ProfileArtifact): Promise<number> => {\n const cached = liftCache.get(artifact.id)\n if (cached !== undefined) return cached\n const lift = await measureMarginalLift({\n baseline: opts.baseline,\n candidate: artifact,\n evalRunner: opts.evalRunner,\n baselineResult,\n signal: opts.signal,\n })\n liftCache.set(artifact.id, lift.scoreDelta)\n return lift.scoreDelta\n }\n\n const retired = new Set<string>()\n const checks: PairStackCheck[] = []\n\n for (let i = 0; i < active.length; i += 1) {\n if (opts.signal?.aborted) break\n const a = active[i]!\n if (retired.has(a.id)) continue\n for (let j = i + 1; j < active.length; j += 1) {\n if (opts.signal?.aborted) break\n const b = active[j]!\n if (retired.has(b.id)) continue\n\n const liftA = await liftOf(a)\n const liftB = await liftOf(b)\n const combined = await opts.evalRunner(applyArtifacts(opts.baseline, [a, b]), opts.signal)\n const combinedLift = combined.composite - baselineResult.composite\n const stackGap = combinedLift - (liftA + liftB)\n const redundant = stackGap < -tolerance\n\n const check: PairStackCheck = {\n pair: [a.id, b.id],\n liftA,\n liftB,\n combinedLift,\n stackGap,\n redundant,\n }\n if (redundant) {\n // Retire the weaker member; on a tie, drop the second-examined.\n const weaker = liftA < liftB ? a : b\n const reason =\n `lifts do not stack: combined Δ=${combinedLift.toFixed(4)} < ` +\n `${a.id} (Δ=${liftA.toFixed(4)}) + ${b.id} (Δ=${liftB.toFixed(4)}) − tol ${tolerance} — ` +\n `redundant with ${(weaker.id === a.id ? b : a).id}`\n opts.registry.retire(weaker.id, reason)\n retired.add(weaker.id)\n check.retiredId = weaker.id\n checks.push(check)\n // `a` itself may have been the one retired — stop pairing it further.\n if (weaker.id === a.id) break\n } else {\n checks.push(check)\n }\n }\n }\n\n return { checks, retired: [...retired], baselineResult }\n}\n","/**\n * `driftWatch` — the scheduled re-measure that DEMOTES decayed artifacts.\n *\n * Promotion is a one-time decision on the evidence available THEN; the world\n * moves on. A skill that earned its lift against last month's task mix, a tool\n * grant the underlying model has since internalized, a prompt line another active\n * artifact now subsumes — each can quietly stop pulling its weight. An artifact\n * that no longer earns its keep but stays `active` is dead weight in every\n * composed profile and a lie in the registry's lift receipt.\n *\n * `driftWatch` closes that gap. It re-runs the SAME `measureMarginalLift`\n * ablation the loop used to promote each `active` artifact — over the CURRENT\n * baseline and the CURRENT eval — and demotes any whose re-measured held-back\n * lift fell below the keep-bar. Demotion moves the artifact `active` → `decayed`\n * (reversible: a later re-measure that recovers the lift can re-promote it), so\n * it drops out of `composeProfile` immediately while staying in the registry as\n * an auditable record.\n *\n * This is NOT a new execution model — it is the existing ablation, run on a\n * schedule, against the active set. The schedule itself is the caller's cron;\n * this module is the one re-measure-and-demote step that cron invokes. It is\n * surface-agnostic (skills, tools, prompts, MCP all re-measure identically) — the\n * only per-surface logic lives, as everywhere in the lifecycle, behind the\n * artifact's own `applyArtifact` bridge, which `measureMarginalLift` already uses.\n */\n\nimport type { AgentProfile } from '@tangle-network/agent-interface'\nimport { ValidationError } from '../errors'\nimport { type EvalResult, type EvalRunner, measureMarginalLift } from './marginal-lift'\nimport type { ArtifactRegistry } from './registry'\nimport type { ProfileArtifact } from './types'\n\nexport interface DriftWatchOptions {\n /** The registry whose `active` artifacts are re-measured. Mutated in place:\n * artifacts that fail the keep-bar are demoted to `decayed`. */\n registry: ArtifactRegistry\n /** The baseline profile each artifact is re-measured ON TOP OF — the SAME\n * ablation shape as promotion. Pass the CURRENT baseline (the world the\n * artifact lives in now), which may differ from the one it was promoted on. */\n baseline: AgentProfile\n /** Scores a profile on the held-back split. The shared baseline arm is run\n * once and reused across every artifact's ablation (the \"without\" arm). */\n evalRunner: EvalRunner\n /**\n * The absolute keep-bar: an artifact stays `active` only while its re-measured\n * held-back lift is STRICTLY ABOVE this floor. Default 0 — an artifact that no\n * longer adds anything (or now subtracts) decays. Mirrors `thresholdPromotionGate`'s\n * `minDelta` so the keep-bar and the promote-bar can be set consistently.\n */\n minLift?: number\n /**\n * The relative keep-bar: an artifact also decays if its re-measured lift fell\n * to below this FRACTION of the lift recorded at promotion (`registry.liftOf`).\n * E.g. `0.5` demotes an artifact that lost more than half its original lift,\n * even if it still clears `minLift`. Default: unset (no relative check — only\n * the absolute `minLift` floor applies). Ignored for an artifact with no\n * recorded prior lift (a bare `promote`), which is judged on `minLift` alone.\n */\n maxRelativeDecay?: number\n /** Restrict the watch to one surface (e.g. re-measure only skills). Default:\n * every `active` artifact, regardless of kind. */\n kind?: ProfileArtifact['kind']\n /** A pre-computed baseline result, to skip the shared \"without\" run when the\n * caller already scored the current baseline this cycle. */\n baselineResult?: EvalResult\n /** Cooperative cancellation, forwarded to every `evalRunner` call. */\n signal?: AbortSignal\n}\n\n/** Per-artifact record of what the re-measure found and decided. */\nexport interface DriftCheck {\n /** The re-measured artifact (status reflects the decision: still `active`, or\n * now `decayed`). */\n artifact: ProfileArtifact\n /** The lift recorded at promotion time (`registry.liftOf` before the check),\n * or `undefined` if it was promoted without a lift receipt. */\n priorLift: number | undefined\n /** The freshly re-measured held-back lift (with − without composite). */\n currentLift: number\n /** Whether this re-measure demoted the artifact (`active` → `decayed`). */\n demoted: boolean\n /** Human-readable reason the artifact was kept or demoted (the same string\n * recorded under `lifecycleReasonKey` on a demotion). */\n reason: string\n}\n\nexport interface DriftWatchResult {\n /** One check per `active` artifact examined, in registry order. */\n checks: DriftCheck[]\n /** Ids of the artifacts demoted to `decayed` this cycle. */\n demoted: string[]\n /** The shared baseline eval (the \"without\" arm, measured once). */\n baselineResult: EvalResult\n}\n\n/**\n * Re-measure every `active` artifact and demote those whose held-back lift\n * decayed below the keep-bar.\n *\n * For each `active` artifact (optionally filtered to one `kind`), this re-runs\n * `measureMarginalLift` over the supplied `baseline`, then applies the keep-bar:\n *\n * - ABSOLUTE: `currentLift > minLift` (default `> 0`), and\n * - RELATIVE (when `maxRelativeDecay` is set AND a prior lift exists):\n * `currentLift >= priorLift * (1 − maxRelativeDecay)`.\n *\n * An artifact failing EITHER bar is demoted to `decayed` (recording the\n * re-measured lift + reason) and drops out of `composeProfile`. An artifact that\n * passes both bars stays `active`, with its recorded lift refreshed to the latest\n * measurement so `liftOf` never reports stale evidence.\n *\n * The baseline \"without\" arm is scored ONCE and shared across all artifacts, so\n * the cost is `1 + (number of active artifacts)` eval runs.\n *\n * @example A nightly cron that demotes any active artifact that lost its lift:\n * const out = await driftWatch({ registry, baseline, evalRunner, minLift: 0 })\n * if (out.demoted.length) report(`demoted ${out.demoted.length} decayed artifacts`)\n */\nexport async function driftWatch(opts: DriftWatchOptions): Promise<DriftWatchResult> {\n if (opts.maxRelativeDecay !== undefined) {\n if (!Number.isFinite(opts.maxRelativeDecay) || opts.maxRelativeDecay < 0) {\n throw new ValidationError(\n `driftWatch: maxRelativeDecay must be a finite, non-negative fraction (got ${opts.maxRelativeDecay})`,\n )\n }\n }\n const minLift = opts.minLift ?? 0\n if (!Number.isFinite(minLift)) {\n throw new ValidationError(`driftWatch: minLift must be a finite number (got ${minLift})`)\n }\n\n // The active set is the only thing that can decay. Snapshot it BEFORE mutating,\n // so demotions this cycle don't shift the set mid-iteration.\n const active = opts.registry.list({ status: 'active', kind: opts.kind })\n\n // Share the baseline \"without\" arm across every artifact's ablation.\n const baselineResult = opts.baselineResult ?? (await opts.evalRunner(opts.baseline, opts.signal))\n\n const checks: DriftCheck[] = []\n const demoted: string[] = []\n for (const artifact of active) {\n if (opts.signal?.aborted) break\n const priorLift = opts.registry.liftOf(artifact.id)\n const lift = await measureMarginalLift({\n baseline: opts.baseline,\n candidate: artifact,\n evalRunner: opts.evalRunner,\n baselineResult,\n signal: opts.signal,\n })\n const currentLift = lift.scoreDelta\n\n const decision = keepDecision(currentLift, priorLift, minLift, opts.maxRelativeDecay)\n if (decision.keep) {\n // Still earning its keep — refresh the lift receipt to the latest evidence.\n const refreshed = opts.registry.promoteWithLift(artifact.id, currentLift)\n checks.push({\n artifact: refreshed,\n priorLift,\n currentLift,\n demoted: false,\n reason: decision.reason,\n })\n } else {\n const decayed = opts.registry.demote(artifact.id, decision.reason, currentLift)\n demoted.push(artifact.id)\n checks.push({\n artifact: decayed,\n priorLift,\n currentLift,\n demoted: true,\n reason: decision.reason,\n })\n }\n }\n\n return { checks, demoted, baselineResult }\n}\n\n/** Apply the absolute + relative keep-bars and explain the verdict. */\nfunction keepDecision(\n currentLift: number,\n priorLift: number | undefined,\n minLift: number,\n maxRelativeDecay: number | undefined,\n): { keep: boolean; reason: string } {\n if (!(currentLift > minLift)) {\n return {\n keep: false,\n reason: `re-measured lift Δ=${currentLift.toFixed(4)} ≤ keep-bar ${minLift} — decayed`,\n }\n }\n if (maxRelativeDecay !== undefined && priorLift !== undefined && priorLift > 0) {\n const floor = priorLift * (1 - maxRelativeDecay)\n if (currentLift < floor) {\n return {\n keep: false,\n reason: `re-measured lift Δ=${currentLift.toFixed(4)} fell below ${(maxRelativeDecay * 100).toFixed(0)}%-decay floor ${floor.toFixed(4)} of prior ${priorLift.toFixed(4)} — decayed`,\n }\n }\n }\n return {\n keep: true,\n reason: `re-measured lift Δ=${currentLift.toFixed(4)} clears the keep-bar — still active`,\n }\n}\n","/**\n * `PromotionGate` — the held-back exam that decides whether a measured candidate\n * artifact is promoted into the registry.\n *\n * The lifecycle measures each candidate's marginal lift (`measureMarginalLift`),\n * but a positive ablation delta on the practice problems is NOT enough to ship —\n * that is how you overfit. The gate is the SECOND, stricter test: it decides\n * promotion from a held-back split (fresh problems the candidate never tuned on)\n * with a significance bar, so a promoted artifact earned its place rather than\n * memorized the practice set.\n *\n * The interface is small and pluggable on purpose: production wires\n * `heldOutPromotionGate`, which delegates to agent-eval's paper-grade\n * `HeldOutGate` (paired-bootstrap CI on the held-out delta + an overfit-gap\n * check). A deterministic test can inject a stub gate. The orchestrator never\n * imports `HeldOutGate` directly — it only knows this interface — so the gate\n * policy is a configuration choice, not a hardcode.\n */\n\nimport { HeldOutGate, type RunRecord } from '@tangle-network/agent-eval'\nimport type { MarginalLift } from './marginal-lift'\n\n/** The verdict a gate returns for one candidate. */\nexport interface PromotionVerdict {\n /** Whether to promote the candidate into the registry as `active`. */\n promote: boolean\n /** Human-readable reason (surfaced in provenance + reports). */\n reason: string\n /** Machine-readable rejection code, or `null` on promote. Mirrors the\n * `HeldOutGate` rejection taxonomy when that gate is the backend. */\n rejectionCode: string | null\n}\n\n/**\n * Decides whether ONE measured candidate is promoted. The lifecycle calls this\n * once per candidate, after `measureMarginalLift` has produced the ablation.\n *\n * `lift` carries the with/without ablation (the marginal contribution); the gate\n * MAY use it directly (the simple \"positive lift on the held-back split\" policy)\n * OR ignore the scalar and decide from the paired per-task records the eval\n * produced (`lift.withArtifact.runs` / `lift.withoutArtifact.runs`) when a\n * significance gate like `HeldOutGate` is the backend.\n */\nexport interface PromotionGate {\n /** Stable label for the gate policy (provenance). */\n kind: string\n decide(lift: MarginalLift): PromotionVerdict\n}\n\n/**\n * The simplest honest gate: promote iff the candidate's marginal lift on the\n * held-back split clears `minDelta` (default `> 0`). It reads only the scalar\n * `scoreDelta`, so it works with any `EvalRunner` (no per-task records needed).\n *\n * Use this when the eval is already scored ON the held-back split and you want a\n * threshold, not a significance test — e.g. a deterministic fixture domain, or a\n * cheap first pass before a paired-bootstrap gate. For paper-grade promotion on\n * noisy live evals, prefer `heldOutPromotionGate`.\n */\nexport function thresholdPromotionGate(minDelta = 0): PromotionGate {\n return {\n kind: `threshold:${minDelta}`,\n decide(lift): PromotionVerdict {\n if (lift.scoreDelta > minDelta) {\n return {\n promote: true,\n reason: `held-back lift Δ=${lift.scoreDelta.toFixed(4)} > ${minDelta}`,\n rejectionCode: null,\n }\n }\n return {\n promote: false,\n reason: `held-back lift Δ=${lift.scoreDelta.toFixed(4)} ≤ ${minDelta}`,\n rejectionCode: 'negative_delta',\n }\n },\n }\n}\n\nexport interface HeldOutPromotionGateOptions {\n /** Stable label of the baseline candidate the held-out records pair against. */\n baselineKey: string\n /** Minimum paired (candidate, baseline) holdout observations. Default 3. */\n minProductiveRuns?: number\n /** Lower-bound on the bootstrap-CI of the median paired holdout delta. Default 0. */\n pairedDeltaThreshold?: number\n /** Max allowed worsening of the (search − holdout) overfit gap. Default 0.15. */\n overfitGapThreshold?: number\n /** Deterministic bootstrap seed (reproducible CIs). Default unseeded. */\n seed?: number\n /** Hard ceiling on the candidate's median per-task USD cost. Default none. */\n costPerTaskCeiling?: number\n}\n\n/**\n * The paper-grade promotion gate: delegate to agent-eval's `HeldOutGate`, which\n * pairs the candidate and baseline per-task holdout records by (experimentId,\n * seed), runs a paired-bootstrap CI on the median delta, and checks the\n * overfit gap. Promotes only when the held-out generalization is real, not luck.\n *\n * REQUIRES the eval to surface per-task records: `lift.withArtifact.runs` (the\n * candidate arm) and `lift.withoutArtifact.runs` (the baseline arm), each\n * carrying matched seeds with both `search` and `holdout` split scores. When the\n * records are absent, this gate FAILS LOUD — a held-out significance claim with\n * no per-task data behind it would be a fabricated number, which the\n * no-silent-fallback doctrine forbids. Use `thresholdPromotionGate` for evals\n * that only produce a scalar composite.\n */\nexport function heldOutPromotionGate(opts: HeldOutPromotionGateOptions): PromotionGate {\n const gate = new HeldOutGate({\n baselineKey: opts.baselineKey,\n minProductiveRuns: opts.minProductiveRuns,\n pairedDeltaThreshold: opts.pairedDeltaThreshold,\n overfitGapThreshold: opts.overfitGapThreshold,\n seed: opts.seed,\n costPerTaskCeiling: opts.costPerTaskCeiling,\n })\n return {\n kind: 'heldout',\n decide(lift): PromotionVerdict {\n const candidateRuns = recordsOf(lift.withArtifact.runs, 'withArtifact', lift.artifactId)\n const baselineRuns = recordsOf(lift.withoutArtifact.runs, 'withoutArtifact', lift.artifactId)\n const decision = gate.evaluate(candidateRuns, baselineRuns)\n return {\n promote: decision.promote,\n reason: decision.reason,\n rejectionCode: decision.rejectionCode,\n }\n },\n }\n}\n\n/** Require the per-task records the held-out gate cannot run without. */\nfunction recordsOf(\n runs: RunRecord[] | undefined,\n arm: 'withArtifact' | 'withoutArtifact',\n artifactId: string,\n): RunRecord[] {\n if (runs === undefined || runs.length === 0) {\n throw new Error(\n `heldOutPromotionGate: the EvalRunner produced no per-task records on the '${arm}' arm for artifact ${JSON.stringify(artifactId)}. ` +\n 'The held-out gate runs a paired-bootstrap on per-task holdout records — it cannot decide from a scalar composite. ' +\n 'Either surface EvalResult.runs from your runner, or use thresholdPromotionGate.',\n )\n }\n return runs\n}\n","/**\n * `promptGenerator` — the `CandidateGenerator` for the PROMPT surface.\n *\n * It is to the prompt surface what `skillGenerator` is to skills: the thin\n * per-surface adapter that turns the agent's history into fresh, unmeasured\n * prompt-instruction candidates the surface-agnostic `runLifecycle` loop then\n * measures, gates, and promotes. Each candidate is a `prompt` artifact carrying\n * one `{ instruction }` line that `applyArtifact` appends to\n * `profile.prompt.instructions`.\n *\n * Two candidate sources, run together each generation:\n *\n * 1. REFINE (incumbent-grounded) — drive agent-eval's `gepaProposer`, the\n * reflective prompt-tier proposer. It reflects on the incumbent prompt +\n * the trace findings and proposes TARGETED rewrites. This is the exploit\n * arm: it polishes the framing the agent already has.\n *\n * 2. SEED (basin escape) — author N genuinely DIVERSE fresh instruction\n * lines from the TASK SPEC, NOT from the incumbent. A reflective proposer\n * only ever perturbs the current surface, so on its own it can polish a\n * local minimum forever — it never tries a fundamentally different framing.\n * The seed arm authors several rewrites that each take a DIFFERENT stance\n * (imperative vs. checklist vs. failure-mode-first, …) so the search can\n * JUMP basins instead of only descending the one it starts in. This is the\n * explore arm, and it is the piece `gepaProposer` structurally cannot do.\n *\n * Both seams are INJECTED (per the §1.5 law: the generator AUTHORS profile\n * pieces, it does not embed a specific LLM loop). `refine` wraps `gepaProposer`;\n * `authorDiverseSeeds` wraps an LLM author. A test injects pure stubs for both,\n * keeping the closed loop deterministically testable; production wires the real\n * router-backed engines (see `productionPromptGenerator`).\n */\n\nimport { type AnalystFinding, callLlmJson, type LlmClientOptions } from '@tangle-network/agent-eval'\nimport {\n type GenerationRecord,\n isProposedCandidate,\n type MutableSurface,\n type ProposeContext,\n type ProposedCandidate,\n type SurfaceProposer,\n} from '@tangle-network/agent-eval/campaign'\nimport { gepaProposer } from '@tangle-network/agent-eval/contract'\nimport type { AgentProfile } from '@tangle-network/agent-interface'\nimport type { CandidateGenerator, GenerateContext } from './generator'\nimport type { ArtifactInput } from './types'\n\n/** A proposed prompt instruction line plus the WHY behind it. The `rationale`\n * rides into the artifact metadata so a promotion decision is auditable. */\nexport interface PromptDraft {\n /** The instruction line appended to `profile.prompt.instructions`. */\n instruction: string\n /** Short human label for review surfaces. */\n label: string\n /** Why this line was proposed — which failure / framing it targets. */\n rationale: string\n}\n\n/**\n * REFINE — incumbent-grounded rewrites. Given the lifecycle context, return\n * targeted edits OF the current prompt framing. The production implementation\n * drives `gepaProposer`; a test injects a pure function. Returns zero or more\n * drafts.\n */\nexport type RefinePrompt = (ctx: GenerateContext) => Promise<PromptDraft[]> | PromptDraft[]\n\n/**\n * SEED — author N genuinely DIVERSE fresh instruction lines from the task spec,\n * NOT mutations of the incumbent. This is the local-minimum escape: each seed\n * MUST take a different framing so the population spans multiple basins. The\n * production implementation makes one LLM call at a non-trivial temperature; a\n * test injects a pure function. Returns up to `count` drafts.\n */\nexport type AuthorDiverseSeeds = (\n ctx: GenerateContext,\n count: number,\n) => Promise<PromptDraft[]> | PromptDraft[]\n\nexport interface PromptGeneratorOptions {\n /** OPTIONAL — the exploit arm (incumbent-grounded rewrites via `gepaProposer`).\n * Omit to run seeds-only. */\n refine?: RefinePrompt\n /** OPTIONAL — the explore arm (diverse fresh seeds). Omit to run refine-only. */\n authorDiverseSeeds?: AuthorDiverseSeeds\n /** How many diverse seeds the explore arm authors each generation. Default 3.\n * Zero disables seeding even when `authorDiverseSeeds` is set. */\n diverseSeedCount?: number\n}\n\n/**\n * Build a `CandidateGenerator` for the prompt surface. Each generation it pools\n * the refine arm (incumbent rewrites) and the seed arm (diverse fresh framings),\n * de-duplicates by instruction text, and emits each as a `prompt` artifact.\n *\n * At least one of `refine` / `authorDiverseSeeds` MUST be provided — a generator\n * with neither has no way to produce a candidate and is a wiring bug, so it\n * throws at construction rather than silently returning `[]` every round.\n *\n * @example Production wiring (refine = gepaProposer, seed = LLM author):\n * promptGenerator({\n * refine: gepaRefine(llm, model),\n * authorDiverseSeeds: routerSeedAuthor(llm, model),\n * })\n */\nexport function promptGenerator(opts: PromptGeneratorOptions): CandidateGenerator<'prompt'> {\n if (!opts.refine && !opts.authorDiverseSeeds) {\n throw new TypeError(\n 'promptGenerator: at least one of `refine` or `authorDiverseSeeds` is required — a generator with neither can never produce a candidate',\n )\n }\n const seedCount = opts.diverseSeedCount ?? 3\n\n return {\n kind: 'prompt',\n async generate(ctx): Promise<ArtifactInput<'prompt'>[]> {\n const drafts: PromptDraft[] = []\n // Explore first: the diverse seeds anchor the population in multiple\n // basins before the incumbent-grounded refinements pile onto one.\n if (opts.authorDiverseSeeds && seedCount > 0) {\n drafts.push(...(await opts.authorDiverseSeeds(ctx, seedCount)))\n }\n if (opts.refine) {\n drafts.push(...(await opts.refine(ctx)))\n }\n return dedupeByInstruction(drafts).map(toPromptArtifact)\n },\n }\n}\n\n/** Drop drafts whose instruction text (trimmed) duplicates an earlier one — the\n * refine arm and a seed can independently land on the same line. First wins. */\nfunction dedupeByInstruction(drafts: PromptDraft[]): PromptDraft[] {\n const seen = new Set<string>()\n const out: PromptDraft[] = []\n for (const draft of drafts) {\n const key = draft.instruction.trim()\n if (key.length === 0 || seen.has(key)) continue\n seen.add(key)\n out.push(draft)\n }\n return out\n}\n\n/** Turn a prompt draft into a `prompt` artifact; the rationale rides in\n * metadata so the promotion decision stays auditable. */\nfunction toPromptArtifact(draft: PromptDraft): ArtifactInput<'prompt'> {\n return {\n kind: 'prompt',\n name: draft.label,\n description: draft.rationale,\n payload: { instruction: draft.instruction },\n metadata: { rationale: draft.rationale },\n }\n}\n\n// ---------------------------------------------------------------------------\n// Production wiring — the real router-backed seams. Kept here (not in the test)\n// so consumers wire the prompt surface in one call; the seams above stay pure\n// so the closed loop is deterministically testable.\n// ---------------------------------------------------------------------------\n\n/** Default reflection/authoring model — a model the Tangle router serves.\n * Callers should pass their own `model`; this is the zero-config fallback. */\nconst defaultPromptModel = 'deepseek-v4-flash'\n\nexport interface ProductionPromptGeneratorOptions {\n /** Router transport (baseUrl/apiKey) for both the refine and seed arms. */\n llm: LlmClientOptions\n /** Model that performs reflection + seed authoring. Default `deepseek-v4-flash`. */\n model?: string\n /** Population size handed to `gepaProposer`'s `propose`. Default 3. */\n refinePopulation?: number\n /** Diverse-seed count. Default 3. */\n diverseSeedCount?: number\n /** Seed-authoring temperature — high on purpose so the seeds spread across\n * framings rather than collapsing onto one. Default 1.0. */\n seedTemperature?: number\n}\n\n/**\n * Production `promptGenerator`: refine via `gepaProposer`, seed via a\n * router-backed diverse author. The one call a consumer makes to grow prompt\n * artifacts with the real engines.\n */\nexport function productionPromptGenerator(\n opts: ProductionPromptGeneratorOptions,\n): CandidateGenerator<'prompt'> {\n const model = opts.model ?? defaultPromptModel\n return promptGenerator({\n refine: gepaRefine(opts.llm, model, opts.refinePopulation ?? 3),\n authorDiverseSeeds: routerSeedAuthor(opts.llm, model, opts.seedTemperature ?? 1.0),\n diverseSeedCount: opts.diverseSeedCount ?? 3,\n })\n}\n\n/**\n * Wrap `gepaProposer` as a `RefinePrompt`. The proposer reflects on the\n * incumbent prompt (`ctx.baseline.prompt.systemPrompt`) and the trace findings\n * to propose `population` targeted rewrites. We feed it the generation-0\n * `ProposeContext` (no scored history yet inside one lifecycle round), so it\n * reflects on the current surface against its mutation primitives — exactly the\n * \"polish the framing you already have\" arm.\n */\nexport function gepaRefine(llm: LlmClientOptions, model: string, population: number): RefinePrompt {\n const proposer: SurfaceProposer<AnalystFinding> = gepaProposer({\n llm,\n model,\n target: 'agent system prompt',\n }) as SurfaceProposer<AnalystFinding>\n\n return async (ctx) => {\n const baseline = baselinePromptSurface(ctx.baseline)\n const proposeCtx: ProposeContext<AnalystFinding> = {\n currentSurface: baseline,\n history: [] as GenerationRecord[],\n findings: [...ctx.findings],\n populationSize: population,\n generation: 0,\n signal: ctx.signal ?? new AbortController().signal,\n }\n const proposed = await proposer.propose(proposeCtx)\n return proposed.flatMap((p) => promptDraftFromProposal(p, baseline))\n }\n}\n\n/**\n * A router-backed `AuthorDiverseSeeds`: one structured LLM call that authors\n * `count` instruction lines, each REQUIRED to take a distinct framing. The\n * prompt is grounded in the task spec (the incumbent system prompt as the spec\n * of WHAT the agent must do) plus the trace findings (what it gets wrong) — but\n * the model is told to author FRESH lines, not edits, so the output spans\n * basins the incumbent's neighborhood never reaches.\n */\nexport function routerSeedAuthor(\n llm: LlmClientOptions,\n model: string,\n temperature: number,\n): AuthorDiverseSeeds {\n return async (ctx, count) => {\n const spec = baselinePromptSurface(ctx.baseline)\n const findingLines = ctx.findings\n .map(\n (f) =>\n `- [${f.area}] ${f.claim}${f.recommended_action ? ` → ${f.recommended_action}` : ''}`,\n )\n .join('\\n')\n const system =\n 'You author standing instructions for an autonomous agent. Given the agent task ' +\n 'spec and its observed mistakes, write DIVERSE candidate instruction lines. CRITICAL: ' +\n 'the candidates must NOT be paraphrases of each other or of the existing spec — each must ' +\n 'take a GENUINELY DIFFERENT framing (e.g. one imperative directive, one as a pre-flight ' +\n 'checklist, one written failure-mode-first, one as a principle, …). Different framings let ' +\n 'a search escape a local optimum instead of polishing one. Return JSON only.'\n const user =\n `Agent task spec (what it must do):\\n${spec || '(no explicit spec — infer from the domain)'}\\n\\n` +\n `Domain: ${ctx.domain}\\n\\n` +\n `Observed mistakes from traces:\\n${findingLines || '(none captured this round)'}\\n\\n` +\n `Author exactly ${count} instruction-line candidates, each a single standing instruction, ` +\n 'each with a distinct framing.'\n\n const { value } = await callLlmJson<{ candidates?: PromptDraftWire[] }>(\n {\n model,\n messages: [\n { role: 'system', content: system },\n { role: 'user', content: user },\n ],\n temperature,\n jsonSchema: {\n name: 'diverse_prompt_seeds',\n schema: {\n type: 'object',\n properties: {\n candidates: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n instruction: { type: 'string' },\n framing: { type: 'string' },\n rationale: { type: 'string' },\n },\n required: ['instruction', 'framing'],\n },\n },\n },\n required: ['candidates'],\n },\n },\n },\n llm,\n )\n\n const wire = value.candidates ?? []\n return wire.slice(0, count).map((c, i) => ({\n instruction: c.instruction,\n label: c.framing?.trim() ? `seed:${c.framing.trim()}` : `seed-${i + 1}`,\n rationale:\n c.rationale?.trim() ||\n `diverse seed (${c.framing?.trim() || 'fresh framing'}) authored from the task spec to escape a local minimum`,\n }))\n }\n}\n\n/** Wire shape of one authored seed from the router. */\ninterface PromptDraftWire {\n instruction: string\n framing?: string\n rationale?: string\n}\n\n/** The prompt surface `gepaProposer` mutates is the system prompt string. */\nfunction baselinePromptSurface(profile: AgentProfile): string {\n return profile.prompt?.systemPrompt ?? ''\n}\n\n/** Turn a `gepaProposer` proposal into a `PromptDraft`. The proposer returns a\n * full rewritten surface; we keep the delta against the baseline as the\n * appended instruction (an artifact is ONE additive line, not a whole-surface\n * swap), falling back to the whole surface when it is a pure addition. */\nfunction promptDraftFromProposal(\n proposal: MutableSurface | ProposedCandidate,\n baseline: string,\n): PromptDraft[] {\n if (isProposedCandidate(proposal)) {\n const surface = proposal.surface\n if (typeof surface !== 'string') return []\n const instruction = surfaceDelta(baseline, surface)\n if (!instruction) return []\n return [\n {\n instruction,\n label: proposal.label?.trim() || 'refine',\n rationale: proposal.rationale?.trim() || 'incumbent-grounded rewrite (gepaProposer)',\n },\n ]\n }\n if (typeof proposal !== 'string') return []\n const instruction = surfaceDelta(baseline, proposal)\n if (!instruction) return []\n return [{ instruction, label: 'refine', rationale: 'incumbent-grounded rewrite (gepaProposer)' }]\n}\n\n/** The line(s) a proposed surface ADDS over the baseline — the additive\n * instruction an artifact represents. When the proposal is a wholesale rewrite\n * (no shared prefix), the whole rewrite is the instruction. */\nfunction surfaceDelta(baseline: string, proposed: string): string {\n const trimmed = proposed.trim()\n const base = baseline.trim()\n if (trimmed === base) return ''\n if (base && trimmed.startsWith(base)) {\n return trimmed.slice(base.length).trim()\n }\n return trimmed\n}\n","/**\n * `ArtifactRegistry` — a typed catalog of profile artifacts with stable ids.\n *\n * The registry is the lifecycle's source of truth for \"what pieces could go into\n * this agent's profile\". It holds `register` / `list` / `get` / `promote`, assigns\n * stable ids, and can `compose` a subset of artifacts onto a baseline profile via\n * the single `applyArtifact` bridge. It owns NO measurement, NO gate, NO LLM — it\n * is a pure in-memory store so callers can persist/snapshot it however they like.\n */\n\nimport type { AgentProfile } from '@tangle-network/agent-interface'\nimport { ValidationError } from '../errors'\nimport { applyArtifacts } from './apply'\nimport type { ArtifactInput, ArtifactKind, ArtifactStatus, ProfileArtifact } from './types'\n\n/** Filter for `list`. Omit a field to leave that dimension unconstrained. */\nexport interface ArtifactQuery {\n kind?: ArtifactKind\n status?: ArtifactStatus\n}\n\n/**\n * The metadata key under which the registry stores an artifact's measured held-\n * back lift. This is the registry INVARIANT's anchor: an artifact is `active`\n * IFF this key holds a finite number — see `promoteWithLift` and `liftOf`. The\n * lifecycle never promotes by status flag alone; the lift score is the receipt.\n * `driftWatch` overwrites it with the latest re-measure so `liftOf` always\n * reflects the most recent evidence.\n */\nexport const liftMetadataKey = 'measuredLift'\n\n/**\n * The metadata key under which the registry records WHY an artifact left the\n * active set — the human-readable reason a `demote` (→ decayed) or `retire`\n * (→ retired) carried. Kept so a demotion/retirement is auditable, not silent.\n */\nexport const lifecycleReasonKey = 'lifecycleReason'\n\n/**\n * A typed, in-memory registry of `ProfileArtifact`s with stable ids.\n *\n * Ids are stable for the life of the registry: `register` assigns one (or honors\n * a caller-supplied id idempotently), and no later operation reassigns it.\n * Re-registering the same id REPLACES the artifact's mutable fields but preserves\n * the id, so a re-proposed candidate keeps its identity across generations.\n */\nexport class ArtifactRegistry {\n private readonly artifacts = new Map<string, ProfileArtifact>()\n private counter = 0\n\n /**\n * Register an artifact, returning the stored record (with its assigned id).\n * When `input.id` is set it is honored (idempotent re-registration replaces\n * the record under the same id); otherwise a stable id is minted as\n * `<kind>-<n>`. `status` defaults to `'candidate'`.\n */\n register<K extends ArtifactKind>(input: ArtifactInput<K>): ProfileArtifact<K> {\n const id = input.id ?? this.mintId(input.kind)\n if (input.id !== undefined && (input.id.length === 0 || input.id.trim() !== input.id)) {\n throw new ValidationError(\n `ArtifactRegistry.register: explicit id must be a non-empty, untrimmed-free string (got ${JSON.stringify(input.id)})`,\n )\n }\n const record: ProfileArtifact<K> = {\n id,\n kind: input.kind,\n key: input.key,\n name: input.name,\n description: input.description,\n payload: input.payload,\n status: input.status ?? 'candidate',\n metadata: input.metadata,\n }\n this.artifacts.set(id, record as ProfileArtifact)\n return record\n }\n\n /** Get an artifact by id, or `undefined` if it was never registered. */\n get(id: string): ProfileArtifact | undefined {\n return this.artifacts.get(id)\n }\n\n /**\n * List artifacts, optionally filtered by `kind` and/or `status`. Returns a new\n * array in registration order; callers may safely sort/mutate the result.\n */\n list(query: ArtifactQuery = {}): ProfileArtifact[] {\n const out: ProfileArtifact[] = []\n for (const artifact of this.artifacts.values()) {\n if (query.kind !== undefined && artifact.kind !== query.kind) continue\n if (query.status !== undefined && artifact.status !== query.status) continue\n out.push(artifact)\n }\n return out\n }\n\n /**\n * Mark an artifact `active`. Fails loud on an unknown id — promoting a\n * non-existent artifact is a caller bug, not a no-op. Returns the updated\n * record. Idempotent: promoting an already-active artifact is a no-op return.\n *\n * NOTE: the artifact-lifecycle INVARIANT (no measured lift ⇒ not active) is\n * enforced by `promoteWithLift`, the path the closed loop uses. This bare\n * `promote` exists for callers that gate elsewhere and just flip the flag; it\n * does NOT record a lift score, so `liftOf` returns `undefined` and a\n * lift-ranked `composeProfile` will skip it. Prefer `promoteWithLift`.\n */\n promote(id: string): ProfileArtifact {\n const artifact = this.artifacts.get(id)\n if (!artifact) {\n throw new ValidationError(\n `ArtifactRegistry.promote: no artifact with id ${JSON.stringify(id)} is registered`,\n )\n }\n if (artifact.status === 'active') return artifact\n const promoted: ProfileArtifact = { ...artifact, status: 'active' }\n this.artifacts.set(id, promoted)\n return promoted\n }\n\n /**\n * Promote an artifact AND record the measured held-back lift that earned it.\n * This is the closed loop's promotion path and the enforcement point of the\n * lifecycle invariant: an artifact becomes `active` only WITH a finite lift\n * number stamped under `liftMetadataKey`. A non-finite `lift` (NaN/Infinity)\n * fails loud — promoting on a broken measurement is exactly the silent-zero the\n * doctrine forbids. Re-promotes a `decayed` artifact whose lift recovered.\n * Returns the updated record.\n */\n promoteWithLift(id: string, lift: number): ProfileArtifact {\n if (!Number.isFinite(lift)) {\n throw new ValidationError(\n `ArtifactRegistry.promoteWithLift: lift for ${JSON.stringify(id)} must be a finite number (got ${lift})`,\n )\n }\n const artifact = this.artifacts.get(id)\n if (!artifact) {\n throw new ValidationError(\n `ArtifactRegistry.promoteWithLift: no artifact with id ${JSON.stringify(id)} is registered`,\n )\n }\n const promoted: ProfileArtifact = {\n ...artifact,\n status: 'active',\n metadata: { ...artifact.metadata, [liftMetadataKey]: lift },\n }\n this.artifacts.set(id, promoted)\n return promoted\n }\n\n /**\n * Demote an `active` artifact to `decayed`: it was promoted, but a later\n * re-measure (`driftWatch`) found its held-back lift fell below the keep-bar.\n * Records the latest re-measured `lift` (so `liftOf` reflects current evidence)\n * and the `reason` (so the demotion is auditable). The artifact stays in the\n * registry — `decayed`, not deleted — so it can be re-promoted if a future\n * re-measure recovers the lift. Fails loud on an unknown id. Demoting a\n * non-`active` artifact fails loud too: only the active set decays.\n */\n demote(id: string, reason: string, lift?: number): ProfileArtifact {\n const artifact = this.artifacts.get(id)\n if (!artifact) {\n throw new ValidationError(\n `ArtifactRegistry.demote: no artifact with id ${JSON.stringify(id)} is registered`,\n )\n }\n if (artifact.status !== 'active') {\n throw new ValidationError(\n `ArtifactRegistry.demote: artifact ${JSON.stringify(id)} is '${artifact.status}', not 'active' — only an active artifact can decay`,\n )\n }\n if (lift !== undefined && !Number.isFinite(lift)) {\n throw new ValidationError(\n `ArtifactRegistry.demote: re-measured lift for ${JSON.stringify(id)} must be a finite number (got ${lift})`,\n )\n }\n const demoted: ProfileArtifact = {\n ...artifact,\n status: 'decayed',\n metadata: {\n ...artifact.metadata,\n [lifecycleReasonKey]: reason,\n ...(lift !== undefined ? { [liftMetadataKey]: lift } : {}),\n },\n }\n this.artifacts.set(id, demoted)\n return demoted\n }\n\n /**\n * Retire an artifact to the terminal `retired` state: it is permanently out of\n * the active set (`dedupeArtifacts` retires the weaker half of a non-stacking\n * pair). Records the `reason` for the audit trail. Unlike `demote`, this is\n * terminal — a retired artifact is never re-promoted by the loop. Idempotent on\n * an already-retired artifact; fails loud on an unknown id.\n */\n retire(id: string, reason: string): ProfileArtifact {\n const artifact = this.artifacts.get(id)\n if (!artifact) {\n throw new ValidationError(\n `ArtifactRegistry.retire: no artifact with id ${JSON.stringify(id)} is registered`,\n )\n }\n if (artifact.status === 'retired') return artifact\n const retired: ProfileArtifact = {\n ...artifact,\n status: 'retired',\n metadata: { ...artifact.metadata, [lifecycleReasonKey]: reason },\n }\n this.artifacts.set(id, retired)\n return retired\n }\n\n /**\n * The measured held-back lift recorded at promotion time (and overwritten by\n * the latest `driftWatch` re-measure), or `undefined` when the artifact was\n * never promoted WITH a lift (a fresh candidate, or one promoted via the bare\n * `promote`). The lifecycle invariant in one accessor: `liftOf(id) ===\n * undefined` ⇒ the artifact has no measured lift ⇒ it is not eligible for a\n * lift-ranked compose. Note this returns the recorded lift regardless of status\n * — `composeProfile` separately filters to `active`, so a `decayed` artifact's\n * stale lift is visible for audit but never folded into a profile.\n */\n liftOf(id: string): number | undefined {\n const value = this.artifacts.get(id)?.metadata?.[liftMetadataKey]\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined\n }\n\n /**\n * Compose a set of registered artifacts onto a baseline profile. With no ids\n * given, composes every `active` artifact (the \"ship the passing set\"\n * default). With explicit ids, composes exactly those (in id order given),\n * failing loud on any unknown id. The applied order is the order passed (or\n * registration order for the active-default), and later artifacts win on key\n * conflicts — same semantics as `applyArtifacts`.\n */\n compose(base: AgentProfile, ids?: readonly string[]): AgentProfile {\n const selected =\n ids === undefined\n ? this.list({ status: 'active' })\n : ids.map((id) => {\n const artifact = this.artifacts.get(id)\n if (!artifact) {\n throw new ValidationError(\n `ArtifactRegistry.compose: no artifact with id ${JSON.stringify(id)} is registered`,\n )\n }\n return artifact\n })\n return applyArtifacts(base, selected)\n }\n\n /** Number of registered artifacts (any status). */\n get size(): number {\n return this.artifacts.size\n }\n\n private mintId(kind: ArtifactKind): string {\n let id: string\n do {\n this.counter += 1\n id = `${kind}-${this.counter}`\n } while (this.artifacts.has(id))\n return id\n }\n}\n\n/** Construct an empty `ArtifactRegistry`. */\nexport function createArtifactRegistry(): ArtifactRegistry {\n return new ArtifactRegistry()\n}\n","/**\n * `runLifecycle` — the ONE closed-loop orchestrator: generate → measure →\n * promote → store.\n *\n * It is surface-agnostic: it knows nothing about skills vs tools vs prompts. All\n * per-surface logic lives behind the `CandidateGenerator` seam. The loop:\n *\n * 1. GENERATE — ask each generator for candidate artifacts from the agent's\n * history (traces/findings). Register them as `candidate`s.\n * 2. MEASURE — for each candidate, run `measureMarginalLift` on the held-\n * back split (the with/without ablation, baseline shared across\n * candidates so the \"without\" arm runs once).\n * 3. PROMOTE — run the `PromotionGate` (default: the held-back exam). On a\n * pass, `promoteWithLift` records the measured lift — the\n * registry invariant: no measured lift ⇒ never active.\n * 4. STORE — every candidate (promoted or not) lands in the registry with\n * provenance (domain, generation, generator kind, gate verdict)\n * + its lift in metadata, so the decision is auditable.\n *\n * Compose is intentionally NOT part of this loop — folding the promoted set back\n * into a profile is a separate, explicit `composeProfile` call the caller makes\n * when it wants a deployable profile. Keeping them separate means a caller can\n * run the loop to grow the catalog without committing a profile change.\n */\n\nimport type { AnalystFinding } from '@tangle-network/agent-eval'\nimport type { AgentProfile } from '@tangle-network/agent-interface'\nimport { ValidationError } from '../errors'\nimport type { PromotionGate, PromotionVerdict } from './gate'\nimport type { CandidateGenerator, GenerateContext } from './generator'\nimport { type EvalResult, type EvalRunner, measureMarginalLift } from './marginal-lift'\nimport { ArtifactRegistry } from './registry'\nimport type { ArtifactKind, ProfileArtifact } from './types'\n\nexport interface RunLifecycleOptions {\n /** The baseline profile candidates are proposed and measured on top of. On a\n * cold start this is the empty (or near-empty) profile. */\n baseline: AgentProfile\n /** The agent/domain id — namespaces provenance + scopes generators. */\n domain: string\n /** The per-surface candidate generators. One per surface the loop grows; the\n * loop runs them in order and pools their candidates. */\n generators: ReadonlyArray<CandidateGenerator>\n /** Scores a profile on the HELD-BACK split. Run by `measureMarginalLift` —\n * once for the shared baseline, once per candidate. */\n evalRunner: EvalRunner\n /** The promotion gate (the held-back exam). Default-free on purpose: the\n * caller chooses the policy (`thresholdPromotionGate` / `heldOutPromotionGate`). */\n gate: PromotionGate\n /** Trace-analyst findings the generators distill/propose from. */\n findings?: ReadonlyArray<AnalystFinding>\n /** Raw captured traces for generators (e.g. a skill `distill`). Opaque. */\n traces?: unknown\n /** Generation counter for provenance. Default 0. */\n generation?: number\n /** An existing registry to grow across generations. Default: a fresh one. */\n registry?: ArtifactRegistry\n /** Cooperative cancellation. */\n signal?: AbortSignal\n}\n\n/** The per-candidate record of what the loop decided and why. */\nexport interface CandidateOutcome {\n /** The stored artifact (status reflects the gate verdict). */\n artifact: ProfileArtifact\n /** The surface this candidate targeted. */\n kind: ArtifactKind\n /** Measured held-back lift (with − without composite). */\n scoreDelta: number\n /** Measured extra USD cost the artifact adds. */\n costDelta: number\n /** The gate's verdict. */\n verdict: PromotionVerdict\n /** Whether the candidate was promoted into the registry as active. */\n promoted: boolean\n}\n\nexport interface RunLifecycleResult {\n /** The registry, grown with this generation's candidates + promotions. */\n registry: ArtifactRegistry\n /** One outcome per candidate the generators produced, in generation order. */\n outcomes: CandidateOutcome[]\n /** Ids of the artifacts promoted this generation. */\n promoted: string[]\n /** The shared baseline eval (the \"without\" arm, measured once). */\n baselineResult: EvalResult\n}\n\n/**\n * Run ONE generation of the artifact lifecycle.\n *\n * @example Cold start on a fixture domain (the closed loop in one call):\n * const out = await runLifecycle({\n * baseline: emptyProfile,\n * domain: 'support-bot',\n * generators: [skillGenerator({ distill, refine })],\n * evalRunner: scoreOnHeldBackSplit,\n * gate: thresholdPromotionGate(),\n * traces: seededTraces,\n * })\n * const composed = composeProfile(out.registry, emptyProfile, { kind: 'skill' })\n */\nexport async function runLifecycle(opts: RunLifecycleOptions): Promise<RunLifecycleResult> {\n if (opts.generators.length === 0) {\n throw new ValidationError('runLifecycle: at least one CandidateGenerator is required')\n }\n\n const registry = opts.registry ?? new ArtifactRegistry()\n const generation = opts.generation ?? 0\n const findings = opts.findings ?? []\n const ctx: GenerateContext = {\n baseline: opts.baseline,\n domain: opts.domain,\n findings,\n traces: opts.traces,\n signal: opts.signal,\n }\n\n // 1. GENERATE — pool candidates from every surface's generator.\n const candidates: ProfileArtifact[] = []\n for (const generator of opts.generators) {\n if (opts.signal?.aborted) break\n const inputs = await generator.generate(ctx)\n for (const input of inputs) {\n const stored = registry.register({\n ...input,\n // Provenance: who proposed this, for which domain, in which generation.\n metadata: {\n ...input.metadata,\n domain: opts.domain,\n generation,\n generatorKind: generator.kind,\n },\n })\n candidates.push(stored)\n }\n }\n\n // 2. MEASURE — share the baseline arm across all candidates (run it once).\n const baselineResult = await opts.evalRunner(opts.baseline, opts.signal)\n\n const outcomes: CandidateOutcome[] = []\n const promoted: string[] = []\n for (const candidate of candidates) {\n if (opts.signal?.aborted) break\n const lift = await measureMarginalLift({\n baseline: opts.baseline,\n candidate,\n evalRunner: opts.evalRunner,\n baselineResult,\n signal: opts.signal,\n })\n\n // 3. PROMOTE — the held-back exam decides; the lift score is the receipt.\n const verdict = opts.gate.decide(lift)\n let artifact = candidate\n if (verdict.promote) {\n artifact = registry.promoteWithLift(candidate.id, lift.scoreDelta)\n promoted.push(candidate.id)\n } else {\n // 4. STORE the verdict on a rejected candidate too (auditable trail).\n artifact = registry.register({\n ...toInput(candidate),\n metadata: {\n ...candidate.metadata,\n gateReason: verdict.reason,\n gateRejectionCode: verdict.rejectionCode,\n scoreDelta: lift.scoreDelta,\n },\n })\n }\n\n outcomes.push({\n artifact,\n kind: candidate.kind,\n scoreDelta: lift.scoreDelta,\n costDelta: lift.costDelta,\n verdict,\n promoted: verdict.promote,\n })\n }\n\n return { registry, outcomes, promoted, baselineResult }\n}\n\n/** Re-derive a registry input from a stored artifact (preserves the stable id so\n * re-registration replaces in place rather than minting a duplicate). */\nfunction toInput(artifact: ProfileArtifact): {\n id: string\n kind: ArtifactKind\n key?: string\n name: string\n description?: string\n payload: ProfileArtifact['payload']\n} {\n return {\n id: artifact.id,\n kind: artifact.kind,\n key: artifact.key,\n name: artifact.name,\n description: artifact.description,\n payload: artifact.payload,\n }\n}\n","/**\n * `skillGenerator` — the reference `CandidateGenerator` for the SKILL surface,\n * and the literal answer to \"an empty profile has no skills, so what creates\n * one?\".\n *\n * It is a two-step pipeline:\n *\n * 1. DISTILL — read the agent's traces/findings and WRITE a new skill document\n * (a reusable how-to note: \"check state before acting\", \"verify after every\n * edit\"). This is the CREATE step. It is the piece `skillOpt` cannot do —\n * an optimizer refines an existing skill, it cannot conjure one from\n * nothing. The production `distill` is `reflectiveGenerator`-style: an LLM\n * reflection over the trace produces the first draft.\n *\n * 2. REFINE — take the distilled draft and improve its wording/structure. The\n * production `refine` wraps agent-eval's skill optimizer (`runSkillOpt`).\n * Refinement is optional: with no `refine`, the distilled draft IS the\n * candidate.\n *\n * Both steps are INJECTED seams, not hardcoded engines — per the §1.5 law, the\n * generator AUTHORS a profile piece; it does not embed a specific LLM loop. That\n * keeps the closed loop deterministically testable (inject pure stubs) while\n * production wires the real distill + skillOpt. The generator emits a `skill`\n * artifact (an inline `SKILL.md` resource ref) the orchestrator measures + gates.\n */\n\nimport type { AgentProfileResourceRef } from '@tangle-network/agent-interface'\nimport type { CandidateGenerator, GenerateContext } from './generator'\nimport type { ArtifactInput } from './types'\n\n/** A distilled skill draft: a name + the `SKILL.md` body. */\nexport interface SkillDraft {\n /** Skill name — becomes the inline resource ref name + the artifact name. */\n name: string\n /** The `SKILL.md` document body (markdown). */\n content: string\n /** Optional one-line description for review surfaces. */\n description?: string\n}\n\n/**\n * DISTILL — create new skill drafts from the agent's history. Returns zero or\n * more drafts (zero is valid: nothing worth distilling this round). The\n * production implementation reflects over `ctx.traces` / `ctx.findings` with an\n * LLM; a test injects a pure function.\n */\nexport type DistillSkills = (ctx: GenerateContext) => Promise<SkillDraft[]> | SkillDraft[]\n\n/**\n * REFINE — improve ONE distilled draft (wording, structure, examples). The\n * production implementation wraps `runSkillOpt`. Returns the refined draft; when\n * omitted from `skillGenerator`, the distilled draft is used as-is.\n */\nexport type RefineSkill = (draft: SkillDraft) => Promise<SkillDraft> | SkillDraft\n\nexport interface SkillGeneratorOptions {\n /** REQUIRED — the create step. Without it there is no skill to optimize. */\n distill: DistillSkills\n /** OPTIONAL — the optimize step. Omit to ship distilled drafts unrefined. */\n refine?: RefineSkill\n}\n\n/**\n * Build a `CandidateGenerator` for the skill surface that distills new skills\n * from history, then (optionally) refines them, and emits each as a `skill`\n * artifact carrying an inline `SKILL.md` resource ref.\n *\n * @example Production wiring (distill = LLM reflection, refine = skillOpt):\n * skillGenerator({\n * distill: reflectiveDistill, // creates the draft from traces\n * refine: skillOptRefine, // optimizes the draft\n * })\n */\nexport function skillGenerator(opts: SkillGeneratorOptions): CandidateGenerator<'skill'> {\n return {\n kind: 'skill',\n async generate(ctx): Promise<ArtifactInput<'skill'>[]> {\n const drafts = await opts.distill(ctx)\n const out: ArtifactInput<'skill'>[] = []\n for (const draft of drafts) {\n const refined = opts.refine ? await opts.refine(draft) : draft\n out.push(toSkillArtifact(refined))\n }\n return out\n },\n }\n}\n\n/** Turn a (refined) draft into a `skill` artifact with an inline resource ref. */\nfunction toSkillArtifact(draft: SkillDraft): ArtifactInput<'skill'> {\n const resource: AgentProfileResourceRef = {\n kind: 'inline',\n name: draft.name,\n content: draft.content,\n }\n return {\n kind: 'skill',\n name: draft.name,\n description: draft.description,\n payload: { resource },\n }\n}\n","/**\n * `worktreeBuildCandidate` — the PRODUCTION `BuildCandidate`: one fan-out leaf\n * that builds a real tool / MCP server in a fresh git worktree with a real\n * coding harness, and verifies it by the surface's intrinsic check.\n *\n * It is the wiring that makes `buildableGenerator`'s dispatch real, composed\n * entirely from shipped engines — NO new execution model:\n *\n * - `gitWorktreeAdapter` (agent-eval/campaign) cuts the isolated worktree.\n * - `improvementDriver` (this repo) owns the worktree lifecycle (create →\n * generate → finalize/discard) and drives ONE candidate.\n * - `agenticGenerator` (this repo) runs the coding harness IN the worktree\n * with the surface's build-prompt (`toolBuildPrompt` / `mcpBuildPrompt`):\n * research → implement → test, multi-shot resume-on-failure.\n * - the surface's verifier proves the result: `commandVerifier('pnpm', ['test'])`\n * for a tool (compiles + tests pass), `mcpServeVerifier(serve)` for an MCP\n * (boots over stdio + answers `tools/list`). A build that never verifies is\n * dropped by `agenticGenerator` (no `CodeSurface` finalized) → `verified:false`.\n *\n * Kept out of `tool-generator.ts` so the dispatch core stays process-free and\n * unit-testable; this file is the one call a consumer makes to wire the real\n * harness fan-out.\n */\n\nimport type { AnalystFinding } from '@tangle-network/agent-eval'\nimport {\n type CodeSurface,\n gitWorktreeAdapter,\n resolveWorktreePath,\n} from '@tangle-network/agent-eval/campaign'\nimport { ValidationError } from '../errors'\nimport { agenticGenerator, commandVerifier } from '../improvement/agentic-generator'\nimport { mcpBuildPrompt, toolBuildPrompt } from '../improvement/build-prompts'\nimport { type McpServeSpec, mcpServeVerifier } from '../improvement/mcp-serve-verifier'\nimport type { LocalHarness } from '../mcp/local-harness'\nimport type { GenerateContext } from './generator'\nimport type { BuildableKind, BuildCandidate, BuiltCandidate } from './tool-generator'\n\nexport interface WorktreeBuildOptions {\n /** The buildable surface to build (`tool` / `mcp`). */\n kind: BuildableKind\n /** Absolute path to the git checkout each candidate worktree is cut from. */\n repoRoot: string\n /** Which local coding harness drives the build. Default `claude`. */\n harness?: LocalHarness\n /** Base ref each worktree forks from. Default `main`. */\n baseRef?: string\n /** Max harness shots per candidate (the depth dial — resume-on-failure). Default 3. */\n maxShots?: number\n /** Per-shot wall-clock timeout (ms). Forwarded to `agenticGenerator`. */\n timeoutMs?: number\n /**\n * For a `tool` build: the verify command (run in the worktree, exit 0 = pass)\n * and the tool name the resulting grant lands under. Default command\n * `pnpm test`.\n */\n tool?: { verifyCommand?: string; verifyArgs?: string[]; toolName: string }\n /**\n * For an `mcp` build: how to BOOT the built server (the boot-and-probe spec)\n * — used both to verify it serves AND as the artifact's start command. The\n * `cwd` defaults to the candidate worktree.\n */\n mcp?: McpServeSpec\n}\n\n/**\n * Build the production per-candidate seam for `buildableGenerator`. Each call to\n * the returned `BuildCandidate` cuts a fresh worktree, drives the harness to\n * implement + verify the surface, and reports the verified worktree (or\n * `verified:false` with the reason) back to the dispatch for ranking.\n */\nexport function worktreeBuildCandidate(opts: WorktreeBuildOptions): BuildCandidate {\n const harness = opts.harness ?? 'claude'\n const baseRef = opts.baseRef ?? 'main'\n const maxShots = opts.maxShots ?? 3\n const worktree = gitWorktreeAdapter({\n repoRoot: opts.repoRoot,\n branchPrefix: `build-${opts.kind}`,\n })\n\n // The harness generator: surface-specific build-prompt + surface-specific\n // verifier, everything else shared. Identical to the documented composition\n // in build-prompts.ts — no per-kind wrapper, just the pieces wired by data.\n const generator = agenticGenerator({\n harness,\n ...(opts.timeoutMs !== undefined ? { timeoutMs: opts.timeoutMs } : {}),\n buildPrompt: opts.kind === 'mcp' ? mcpBuildPrompt : toolBuildPrompt,\n verify: buildVerifier(opts),\n })\n\n return async (\n ctx: GenerateContext,\n index: number,\n signal: AbortSignal,\n ): Promise<BuiltCandidate> => {\n const label = `${opts.kind}-cand${index}`\n const wt = await worktree.create({ baseRef, label })\n try {\n const { applied, summary } = await generator.generate({\n worktreePath: wt.path,\n report: undefined,\n findings: [...ctx.findings] as AnalystFinding[],\n maxShots,\n signal,\n })\n if (!applied) {\n // The harness never produced a verified change — drop the worktree, and\n // report an unverified build so the dispatch ranks the survivors only.\n await worktree.discard(wt).catch(() => {})\n return {\n label,\n verified: false,\n worktreeRef: wt.path,\n failureReason: 'no verified candidate within maxShots',\n }\n }\n const surface = await worktree.finalize(wt, summary)\n return verifiedBuild(opts, label, surface)\n } catch (err) {\n await worktree.discard(wt).catch(() => {})\n throw err\n }\n }\n}\n\n/** The intrinsic verifier for the surface: `pnpm test` (tool) or boot-and-probe\n * the MCP server (mcp). The verifier is what makes \"verified\" mean compiled +\n * serves, not \"the harness said it was done\". */\nfunction buildVerifier(opts: WorktreeBuildOptions) {\n if (opts.kind === 'mcp') {\n if (!opts.mcp) {\n throw new ValidationError(\n 'worktreeBuildCandidate: an mcp build requires opts.mcp (the boot-and-probe serve spec)',\n )\n }\n return mcpServeVerifier(opts.mcp)\n }\n const command = opts.tool?.verifyCommand ?? 'pnpm'\n const args = opts.tool?.verifyArgs ?? (opts.tool?.verifyCommand ? [] : ['test'])\n return commandVerifier(command, args)\n}\n\n/** Turn a finalized `CodeSurface` into a verified `BuiltCandidate`, carrying the\n * per-surface payload (tool name, or the MCP serve command) the artifact needs. */\nfunction verifiedBuild(\n opts: WorktreeBuildOptions,\n label: string,\n surface: CodeSurface,\n): BuiltCandidate {\n const worktreeRef = resolveWorktreePath(surface)\n if (opts.kind === 'tool') {\n const toolName = opts.tool?.toolName\n if (!toolName || toolName.trim().length === 0) {\n throw new ValidationError(\n 'worktreeBuildCandidate: a tool build requires opts.tool.toolName for the grant key',\n )\n }\n return {\n label,\n verified: true,\n worktreeRef,\n toolName,\n metadata: { summary: surface.summary, baseRef: surface.baseRef },\n }\n }\n const mcp = opts.mcp!\n return {\n label,\n verified: true,\n worktreeRef,\n serve: {\n command: mcp.command,\n ...(mcp.args ? { args: mcp.args } : {}),\n cwd: worktreeRef,\n ...(mcp.env ? { env: mcp.env } : {}),\n },\n metadata: { summary: surface.summary, baseRef: surface.baseRef },\n }\n}\n","/**\n * `buildableGenerator` — the `CandidateGenerator` for the BUILDABLE surfaces\n * (`tool` and `mcp`), and the answer to \"a profile can't *write* a new tool from\n * a prompt rewrite — who builds it?\".\n *\n * Unlike the prompt and skill surfaces, where a candidate is a piece of TEXT an\n * LLM authors in one call, a tool or an MCP server is CODE that must compile,\n * pass its tests, and — for an MCP server — actually boot and serve. You cannot\n * one-shot that reliably. So this generator is a SUPERVISOR DISPATCH, not a\n * single author:\n *\n * 1. FAN OUT — spawn N parallel candidate implementations, each built in its\n * OWN git worktree by a real coding harness (research → implement\n * → test → prove it compiles / actually serves). The per-candidate\n * build is the `buildCandidate` seam; production wires it to the\n * shipped `improvementDriver` + `agenticGenerator` + a verifier\n * (`commandVerifier` for a tool, `mcpServeVerifier` for an MCP).\n * 2. FILTER — keep only the VERIFIED builds. An unverified worktree is never a\n * candidate (the verifier is the gate — same valid-only discipline\n * as `worktreeFanout`'s `selectValidWinner`). A build that never\n * compiles/serves is discarded, never ranked.\n * 3. RANK — score each verified survivor by `measureMarginalLift` against\n * `ctx.baseline` (the with/without held-back ablation — the SAME\n * selector the rest of the lifecycle uses; never a judge).\n * 4. EMIT — return the single best survivor as ONE `tool` / `mcp` artifact,\n * carrying the measured lift + the winning worktree ref as\n * auditable provenance in metadata.\n *\n * Steps 2–4 (filter, lift-rank, emit) are surface-agnostic and live here; only\n * `buildCandidate` (the per-candidate worktree build) varies, and it is INJECTED\n * — production wires the real harness fan-out, a test injects pure stubs so the\n * dispatch is deterministically exercisable without spawning processes or models.\n *\n * NB the lifecycle orchestrator (`runLifecycle`) STILL measures + gates whatever\n * this returns. The rank here is an INTERNAL selection — \"which of the N parallel\n * builds is the best candidate to put forward\" — not the promotion gate. A\n * generator that puts forward a measured-best candidate and an orchestrator that\n * re-measures it on the held-back exam are not redundant: the first picks among\n * siblings, the second decides whether the winner clears the bar to ship.\n */\n\nimport type { AgentProfileMcpServer } from '@tangle-network/agent-interface'\nimport { ValidationError } from '../errors'\nimport type { CandidateGenerator, GenerateContext } from './generator'\nimport { type EvalRunner, measureMarginalLift } from './marginal-lift'\nimport { ArtifactRegistry } from './registry'\nimport type { ArtifactInput, ProfileArtifact } from './types'\n\n/** The buildable surfaces — the kinds whose candidate IS code that must compile\n * / serve, so building one is a fan-out-and-verify dispatch, not a one-shot. */\nexport type BuildableKind = 'tool' | 'mcp'\n\n/**\n * The result of building ONE candidate in its own worktree. A build either\n * verified (compiled + tests passed, or — for an MCP — booted and served) or it\n * did not; an unverified build is dropped before ranking, so `verified:false`\n * carries the reason for the audit trail but never becomes a candidate.\n */\nexport interface BuiltCandidate {\n /** A short label for this candidate (worktree branch / trace node). */\n label: string\n /** Did the build compile + pass its verifier? Only verified builds rank. */\n verified: boolean\n /** The worktree path / git ref holding the built change (provenance). */\n worktreeRef: string\n /**\n * For an `mcp` candidate: how to START the built server (stdio transport).\n * Becomes the `AgentProfileMcpServer` the artifact carries. REQUIRED for an\n * `mcp` build; ignored for a `tool` build.\n */\n serve?: { command: string; args?: string[]; cwd?: string; env?: Record<string, string> }\n /**\n * For a `tool` candidate: the tool name the grant lands under (the profile\n * `tools` key). REQUIRED for a `tool` build; ignored for an `mcp` build.\n */\n toolName?: string\n /** Why the build failed verification, when `verified` is false (audit only). */\n failureReason?: string\n /** Free-form provenance to ride into the artifact metadata (build summary, …). */\n metadata?: Record<string, unknown>\n}\n\n/**\n * BUILD ONE candidate. Given the lifecycle context and the index in the fan-out,\n * produce a fresh worktree implementation and report whether it verified. The\n * production implementation drives a real coding harness in a fresh worktree\n * (`worktreeBuildCandidate`); a test injects a pure function.\n *\n * MUST NOT measure lift, gate, or register — that is the dispatch's / the\n * orchestrator's job. It only builds + verifies ONE sibling.\n */\nexport type BuildCandidate = (\n ctx: GenerateContext,\n index: number,\n signal: AbortSignal,\n) => Promise<BuiltCandidate>\n\nexport interface BuildableGeneratorOptions {\n /** The buildable surface this generator targets (`tool` or `mcp`). */\n kind: BuildableKind\n /** The per-candidate build seam (the fan-out leaf). REQUIRED. */\n buildCandidate: BuildCandidate\n /** How many candidate implementations to build in parallel each generation.\n * Default 3. Must be >= 1. The conserved-budget / live-worker caps that bound\n * the real fan-out live in the production `buildCandidate` wiring. */\n fanout?: number\n /** Scores a profile on the held-back split — used to RANK the verified\n * siblings by `measureMarginalLift`. REQUIRED: without it there is no way to\n * pick the best of N, and \"best\" is the whole point of a fan-out. */\n evalRunner: EvalRunner\n}\n\n/**\n * Build a `CandidateGenerator` for a buildable surface (`tool` / `mcp`). Each\n * generation it fans out `fanout` parallel worktree builds, keeps the verified\n * ones, ranks them by held-back marginal lift, and emits the single best as one\n * artifact. Returns `[]` when no build verifies — the surface had nothing\n * shippable to contribute this round (a valid, common outcome for hard builds).\n *\n * @example Production wiring (build = real harness fan-out, rank = held-back eval):\n * buildableGenerator({\n * kind: 'mcp',\n * buildCandidate: worktreeBuildCandidate({ repoRoot, harness: 'claude' }),\n * evalRunner: scoreOnHeldBackSplit,\n * fanout: 4,\n * })\n */\nexport function buildableGenerator(\n opts: BuildableGeneratorOptions,\n): CandidateGenerator<BuildableKind> {\n const fanout = opts.fanout ?? 3\n if (fanout < 1) {\n throw new ValidationError(\n `buildableGenerator: fanout must be >= 1 (got ${fanout}) — a dispatch with no candidates can never build anything`,\n )\n }\n\n return {\n kind: opts.kind,\n async generate(ctx): Promise<ArtifactInput<BuildableKind>[]> {\n const signal = ctx.signal ?? new AbortController().signal\n\n // 1. FAN OUT — N parallel candidate builds, each in its own worktree.\n const settled = await Promise.all(\n Array.from({ length: fanout }, (_unused, i) => opts.buildCandidate(ctx, i, signal)),\n )\n\n // 2. FILTER — only VERIFIED builds are candidates. An unverified worktree\n // (failed to compile / never served) is discarded, never ranked.\n const verified = settled.filter((b) => b.verified)\n if (verified.length === 0) return []\n\n // 3. RANK — score each survivor by the held-back ablation against the\n // shared baseline (measured once, reused across siblings).\n const baselineResult = await opts.evalRunner(ctx.baseline, signal)\n const ranked: Array<{ built: BuiltCandidate; scoreDelta: number; costDelta: number }> = []\n for (const built of verified) {\n if (signal.aborted) break\n // Stage each survivor as a throwaway artifact so the SAME `applyArtifact`\n // bridge `measureMarginalLift` uses elsewhere applies the built surface.\n const probe = stageProbeArtifact(opts.kind, built)\n const lift = await measureMarginalLift({\n baseline: ctx.baseline,\n candidate: probe,\n evalRunner: opts.evalRunner,\n baselineResult,\n signal,\n })\n ranked.push({ built, scoreDelta: lift.scoreDelta, costDelta: lift.costDelta })\n }\n if (ranked.length === 0) return []\n\n // 4. EMIT the single best survivor as one artifact. Ties → the\n // earliest-built (the fan-out order) wins, matching the kernel's\n // `defaultSelectWinner` tie-break.\n ranked.sort((a, b) => b.scoreDelta - a.scoreDelta)\n const winner = ranked[0]!\n return [toArtifact(opts.kind, winner.built, winner.scoreDelta, winner.costDelta, fanout)]\n },\n }\n}\n\n/**\n * A throwaway artifact used ONLY to drive `applyArtifact` inside the internal\n * rank — it is registered into a private registry (never the lifecycle's), so it\n * gets a real id for `measureMarginalLift` without polluting the caller's\n * catalog. The orchestrator re-registers the EMITTED winner with provenance.\n */\nconst probeRegistry = new ArtifactRegistry()\nfunction stageProbeArtifact(kind: BuildableKind, built: BuiltCandidate): ProfileArtifact {\n return probeRegistry.register(bareArtifact(kind, built))\n}\n\n/** The artifact shape for a built candidate, WITHOUT the lift/provenance the\n * emit step stamps — shared by the probe (internal rank) and the emit. */\nfunction bareArtifact(kind: BuildableKind, built: BuiltCandidate): ArtifactInput<BuildableKind> {\n if (kind === 'tool') {\n const toolName = built.toolName\n if (!toolName || toolName.trim().length === 0) {\n throw new ValidationError(\n `buildableGenerator: a verified 'tool' build must report a toolName (label=${built.label})`,\n )\n }\n return {\n kind: 'tool',\n key: toolName,\n name: toolName,\n payload: { enabled: true },\n } as ArtifactInput<BuildableKind>\n }\n const serve = built.serve\n if (!serve?.command || serve.command.trim().length === 0) {\n throw new ValidationError(\n `buildableGenerator: a verified 'mcp' build must report a serve command (label=${built.label})`,\n )\n }\n const server: AgentProfileMcpServer = {\n transport: 'stdio',\n command: serve.command,\n ...(serve.args ? { args: serve.args } : {}),\n ...(serve.cwd ? { cwd: serve.cwd } : { cwd: built.worktreeRef }),\n ...(serve.env ? { env: serve.env } : {}),\n enabled: true,\n }\n return {\n kind: 'mcp',\n key: built.label,\n name: built.label,\n payload: { server },\n } as ArtifactInput<BuildableKind>\n}\n\n/** The EMITTED winner: the bare artifact plus the measured lift + worktree\n * provenance in metadata, so the promotion decision is auditable. The\n * orchestrator stamps domain/generation provenance on top when it registers. */\nfunction toArtifact(\n kind: BuildableKind,\n built: BuiltCandidate,\n scoreDelta: number,\n costDelta: number,\n fanout: number,\n): ArtifactInput<BuildableKind> {\n const bare = bareArtifact(kind, built)\n return {\n ...bare,\n description: `built via ${fanout}-way fan-out; won on +${scoreDelta.toFixed(4)} held-back lift`,\n metadata: {\n ...built.metadata,\n worktreeRef: built.worktreeRef,\n buildLabel: built.label,\n fanout,\n // The INTERNAL rank lift (best-of-N selection), distinct from the\n // orchestrator's promotion-gate measurement.\n siblingScoreDelta: scoreDelta,\n siblingCostDelta: costDelta,\n },\n }\n}\n"],"mappings":"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","names":["instruction"]}
1
+ {"version":3,"sources":["../src/lifecycle/apply.ts","../src/lifecycle/compose.ts","../src/lifecycle/marginal-lift.ts","../src/lifecycle/dedupe.ts","../src/lifecycle/drift-watch.ts","../src/lifecycle/gate.ts","../src/lifecycle/prompt-generator.ts","../src/lifecycle/registry.ts","../src/lifecycle/run-lifecycle.ts","../src/lifecycle/skill-generator.ts","../src/lifecycle/tool-build.ts","../src/lifecycle/tool-generator.ts"],"sourcesContent":["/**\n * `applyArtifact` — merge one `ProfileArtifact` onto an `AgentProfile`.\n *\n * This is the deterministic bridge between the artifact catalog and the §1.5\n * profile law: each `ArtifactKind` lands on exactly one profile field. The merge\n * is shallow-immutable — the input profile is never mutated; a new profile with\n * the artifact applied is returned. This is the ONE place that knows how a\n * `kind` maps to a profile field, so both the registry's `compose` and\n * `measureMarginalLift`'s with/without ablation share a single source of truth.\n */\n\nimport type { AgentProfile } from '@tangle-network/agent-interface'\nimport type { ProfileArtifact } from './types'\n\n/**\n * Return a new profile with `artifact` merged onto `base`. Keyed kinds\n * (`tool`/`mcp`/`hook`/`subagent`) land under `artifact.key` (falling back to\n * `artifact.id`). `prompt` appends an instruction line; `skill` appends a\n * resource ref. Existing keys are overwritten by the artifact (the artifact is\n * the candidate being measured/promoted, so it wins on conflict).\n */\nexport function applyArtifact(base: AgentProfile, artifact: ProfileArtifact): AgentProfile {\n switch (artifact.kind) {\n case 'prompt': {\n const payload = artifact.payload as { instruction: string }\n const instructions = [...(base.prompt?.instructions ?? []), payload.instruction]\n return { ...base, prompt: { ...base.prompt, instructions } }\n }\n case 'skill': {\n const payload = artifact.payload as ProfileArtifact<'skill'>['payload']\n const skills = [...(base.resources?.skills ?? []), payload.resource]\n return { ...base, resources: { ...base.resources, skills } }\n }\n case 'tool': {\n const payload = artifact.payload as ProfileArtifact<'tool'>['payload']\n const key = keyOf(artifact)\n return { ...base, tools: { ...base.tools, [key]: payload.enabled } }\n }\n case 'mcp': {\n const payload = artifact.payload as ProfileArtifact<'mcp'>['payload']\n const key = keyOf(artifact)\n return { ...base, mcp: { ...base.mcp, [key]: payload.server } }\n }\n case 'hook': {\n const payload = artifact.payload as ProfileArtifact<'hook'>['payload']\n const existing = base.hooks?.[payload.event] ?? []\n return {\n ...base,\n hooks: { ...base.hooks, [payload.event]: [...existing, ...payload.commands] },\n }\n }\n case 'subagent': {\n const payload = artifact.payload as ProfileArtifact<'subagent'>['payload']\n const key = keyOf(artifact)\n return { ...base, subagents: { ...base.subagents, [key]: payload.profile } }\n }\n }\n}\n\n/** Apply many artifacts left-to-right; later artifacts win on key conflicts. */\nexport function applyArtifacts(\n base: AgentProfile,\n artifacts: readonly ProfileArtifact[],\n): AgentProfile {\n return artifacts.reduce((profile, artifact) => applyArtifact(profile, artifact), base)\n}\n\n/** The profile-field key a keyed artifact lands under: explicit `key` or `id`. */\nfunction keyOf(artifact: ProfileArtifact): string {\n return artifact.key ?? artifact.id\n}\n","/**\n * `composeProfile` — fold the top-k active artifacts back into a profile.\n *\n * This is the \"give it all the passing options at once\" step. After the loop has\n * generated, measured, and promoted artifacts, `composeProfile` selects the\n * highest-lift ACTIVE ones (those promoted WITH a measured held-back lift) and\n * applies them onto a baseline profile via the single `applyArtifact` bridge.\n *\n * It differs from the registry's raw `compose` in two ways the lifecycle needs:\n * 1. It ranks by MEASURED LIFT and takes the top `k`, rather than applying\n * every promoted artifact in registration order. The best pieces go in\n * first; a `k` budget keeps the composed profile from accreting marginal\n * wins without bound.\n * 2. It enforces the lifecycle INVARIANT — only artifacts with a finite\n * `liftOf` are eligible. An artifact flipped to `promoted` without a lift\n * receipt is invisible here, by construction.\n */\n\nimport type { AgentProfile } from '@tangle-network/agent-interface'\nimport { applyArtifacts } from './apply'\nimport type { ArtifactRegistry } from './registry'\nimport type { ArtifactKind, ProfileArtifact } from './types'\n\nexport interface ComposeProfileOptions {\n /** Cap on how many artifacts to fold in. Default: all eligible. */\n k?: number\n /** Restrict to one surface (e.g. only fold in skills). Default: all kinds. */\n kind?: ArtifactKind\n}\n\n/**\n * Return a new profile with the top-`k` active artifacts (highest measured lift\n * first) applied onto `base`.\n *\n * \"Active\" = promoted WITH a finite measured lift (`registry.liftOf` returns a\n * number) — the lifecycle invariant. Ties in lift fall back to registration\n * order (stable). With no `k`, every eligible artifact is folded in.\n *\n * @param registry the catalog the loop populated\n * @param base the baseline profile to fold artifacts onto (the empty profile\n * on a cold start)\n * @param opts `k` (top-k budget) and an optional `kind` filter\n *\n * @example Cold start — fold the single best distilled skill back in:\n * const composed = composeProfile(registry, emptyProfile, { kind: 'skill', k: 1 })\n */\nexport function composeProfile(\n registry: ArtifactRegistry,\n base: AgentProfile,\n opts: ComposeProfileOptions = {},\n): AgentProfile {\n const eligible: Array<{ artifact: ProfileArtifact; lift: number }> = []\n for (const artifact of registry.list({ status: 'active', kind: opts.kind })) {\n const lift = registry.liftOf(artifact.id)\n // Invariant: no measured lift ⇒ not eligible for a lift-ranked compose.\n if (lift === undefined) continue\n eligible.push({ artifact, lift })\n }\n\n // Highest lift first; stable on ties (Array.prototype.sort is stable in V8).\n eligible.sort((a, b) => b.lift - a.lift)\n\n const k = opts.k ?? eligible.length\n const selected = eligible.slice(0, Math.max(0, k)).map((e) => e.artifact)\n return applyArtifacts(base, selected)\n}\n","/**\n * `measureMarginalLift` — the with-vs-without ablation for one artifact.\n *\n * \"What does THIS one piece add?\" is the question a lifecycle has to answer\n * before it can promote anything. The answer is an ablation: score the baseline\n * profile, score the baseline-plus-candidate profile, and report the delta. This\n * is the marginal contribution of the artifact — the same shape the project's\n * `OutcomeMeasurement` reports for an apply pass, but isolated to ONE artifact so\n * a registry can rank candidates by what they individually earn.\n *\n * The measurement is selector-agnostic: it takes an `EvalRunner` the caller\n * supplies (any function that scores a profile and reports cost), runs it twice,\n * and subtracts. It never judges, never picks a winner, never re-scores on a\n * holdout — those are the gate's job. It only quantifies the ablation so the gate\n * has a real number to decide on.\n */\n\nimport type { RunRecord } from '@tangle-network/agent-eval'\nimport type { AgentProfile } from '@tangle-network/agent-interface'\nimport { applyArtifact } from './apply'\nimport type { ProfileArtifact } from './types'\n\n/**\n * The result of running an eval over ONE profile: a composite score and the cost\n * to obtain it. This mirrors the project's score/cost convention (`composite`\n * from `OutcomeMeasurement`, `costUsd` from `LoopResult`), so a caller can pass a\n * thin wrapper over `runLoop` / `runBenchmark` / `runAgentEval` directly.\n */\nexport interface EvalResult {\n /** Composite score in `[0, 1]` (higher is better) for the profile under test. */\n composite: number\n /** USD cost to produce this result. */\n costUsd: number\n /**\n * Per-task records the run produced, when the runner emits them. The marginal\n * lift only needs `composite`, but the held-out promotion gate (`HeldOutGate`)\n * pairs candidate vs baseline per-task holdout records by (experimentId, seed)\n * — so a runner feeding `heldOutPromotionGate` MUST populate this with rows\n * carrying both `search` and `holdout` split scores. Omit it for evals scored\n * to a single composite (then use `thresholdPromotionGate`).\n */\n runs?: RunRecord[]\n /** Optional opaque passthrough (per-task cells, the raw report, …). */\n details?: unknown\n}\n\n/**\n * Scores a profile. The caller wires this to whatever eval they run — a\n * `runLoop` rollout, a `runBenchmark` campaign, a `runAgentEval` cohort — and\n * returns the composite + cost. `signal` is forwarded for cancellation.\n */\nexport type EvalRunner = (profile: AgentProfile, signal?: AbortSignal) => Promise<EvalResult>\n\nexport interface MeasureMarginalLiftOptions {\n /** The profile the artifact is measured ON TOP OF (the \"without\" arm). */\n baseline: AgentProfile\n /** The single artifact whose marginal contribution we want. */\n candidate: ProfileArtifact\n /** The eval that scores a profile. Run once per arm (twice total). */\n evalRunner: EvalRunner\n /**\n * A pre-computed baseline result, to skip the \"without\" run when the caller\n * already scored the baseline (e.g. measuring several candidates against the\n * same baseline). When set, the baseline arm is NOT re-run.\n */\n baselineResult?: EvalResult\n /** Forwarded to both `evalRunner` invocations for cancellation. */\n signal?: AbortSignal\n}\n\n/**\n * The marginal lift of one artifact: the with/without ablation.\n *\n * `scoreDelta = with.composite − without.composite`. A positive `scoreDelta` is\n * the evidence a gate needs to promote; a negative one is the signal to drop the\n * artifact. `costDelta` is the extra USD the artifact costs (often positive — a\n * new tool/MCP adds calls) and lets the gate weigh lift against spend.\n */\nexport interface MarginalLift {\n /** The artifact id this measurement is for (stable, from the registry). */\n artifactId: string\n /** Eval of `applyArtifact(baseline, candidate)`. */\n withArtifact: EvalResult\n /** Eval of `baseline` alone. */\n withoutArtifact: EvalResult\n /** `withArtifact.composite − withoutArtifact.composite`. */\n scoreDelta: number\n /** `withArtifact.costUsd − withoutArtifact.costUsd`. */\n costDelta: number\n}\n\n/**\n * Run the with/without ablation for `candidate` over `baseline` and return its\n * marginal score/cost contribution.\n *\n * The \"without\" arm scores the baseline profile unchanged; the \"with\" arm scores\n * `applyArtifact(baseline, candidate)`. Both use the same `evalRunner`, so the\n * delta isolates the artifact's effect (eval method held constant). The baseline\n * arm is skipped when `baselineResult` is supplied.\n *\n * @example\n * const lift = await measureMarginalLift({\n * baseline,\n * candidate: registry.get(id)!,\n * evalRunner: (profile) => scoreProfileOnCohort(profile),\n * })\n * if (lift.scoreDelta > 0) registry.promote(lift.artifactId)\n */\nexport async function measureMarginalLift(opts: MeasureMarginalLiftOptions): Promise<MarginalLift> {\n const { baseline, candidate, evalRunner, baselineResult, signal } = opts\n\n const withoutArtifact = baselineResult ?? (await evalRunner(baseline, signal))\n const withProfile = applyArtifact(baseline, candidate)\n const withArtifact = await evalRunner(withProfile, signal)\n\n return {\n artifactId: candidate.id,\n withArtifact,\n withoutArtifact,\n scoreDelta: withArtifact.composite - withoutArtifact.composite,\n costDelta: withArtifact.costUsd - withoutArtifact.costUsd,\n }\n}\n","/**\n * `dedupeArtifacts` — retire the redundant half of a non-stacking pair.\n *\n * The lifecycle promotes each artifact on its OWN marginal lift, in isolation.\n * But two artifacts can each earn lift alone yet teach the agent the SAME thing —\n * a distilled \"check state first\" skill and a \"verify before acting\" prompt line,\n * say. Composed together they don't add up: the agent already learned the tactic\n * from the first, so the second buys little. Keeping both is wasted profile\n * surface (more context, more cost, more ways to conflict) for no extra score.\n *\n * `dedupeArtifacts` is the judge that catches this. For each pair of `active`\n * artifacts it measures whether their lifts STACK: it scores the baseline, each\n * artifact alone, and BOTH together, then compares the combined lift against the\n * sum of the individual lifts. When `combined < (a + b) − tolerance`, the pair\n * overlaps — they do not stack — and the weaker member (lower individual lift) is\n * `retire`d. Retirement is terminal: the kept member already delivers the shared\n * value, so the loop should not re-promote the redundant one.\n *\n * The \"judge\" is a MEASUREMENT, not an LLM verdict — it reuses the same ablation\n * machinery (`measureMarginalLift` + the caller's `EvalRunner`) the rest of the\n * lifecycle runs on, so the selector≠judge firewall holds and there is no new\n * execution model. It is surface-agnostic: any two artifacts (skill+prompt,\n * tool+tool, …) are compared identically, because stacking is measured on the\n * composed profile via the one `applyArtifacts` bridge.\n */\n\nimport type { AgentProfile } from '@tangle-network/agent-interface'\nimport { ValidationError } from '../errors'\nimport { applyArtifacts } from './apply'\nimport { type EvalResult, type EvalRunner, measureMarginalLift } from './marginal-lift'\nimport type { ArtifactRegistry } from './registry'\nimport type { ProfileArtifact } from './types'\n\nexport interface DedupeOptions {\n /** The registry whose `active` artifacts are pairwise stack-tested. Mutated in\n * place: the weaker member of each non-stacking pair is retired. */\n registry: ArtifactRegistry\n /** The baseline profile the stacking ablation runs on top of. The \"without\"\n * arm is scored once and shared across every pair. */\n baseline: AgentProfile\n /** Scores a profile on the held-back split. Called for the baseline, each\n * artifact alone, and each candidate pair together. */\n evalRunner: EvalRunner\n /**\n * The stacking tolerance. A pair is judged non-stacking (redundant) when the\n * combined lift falls SHORT of the sum of individual lifts by more than this:\n * `combined < a + b − tolerance`. A small positive tolerance absorbs eval\n * noise so only a real overlap retires an artifact. Default 0 — any shortfall\n * counts as non-stacking (use a positive value on noisy live evals).\n */\n tolerance?: number\n /** Restrict dedupe to one surface (only compare skills against skills, …).\n * Default: every `active` artifact is a candidate, across kinds. */\n kind?: ProfileArtifact['kind']\n /** A pre-computed baseline result, to skip the shared \"without\" run. */\n baselineResult?: EvalResult\n /** Cooperative cancellation, forwarded to every `evalRunner` call. */\n signal?: AbortSignal\n}\n\n/** The stacking verdict for one pair of active artifacts. */\nexport interface PairStackCheck {\n /** Ids of the two artifacts compared, in (a, b) order as examined. */\n pair: [string, string]\n /** Individual held-back lift of the first artifact alone (re-measured). */\n liftA: number\n /** Individual held-back lift of the second artifact alone (re-measured). */\n liftB: number\n /** Held-back lift of BOTH artifacts composed together. */\n combinedLift: number\n /** `combinedLift − (liftA + liftB)`: ≥ −tolerance ⇒ they stack; below ⇒ they\n * overlap (redundant). */\n stackGap: number\n /** Whether the pair was judged non-stacking (redundant). */\n redundant: boolean\n /** The id retired when redundant (the weaker member), else `undefined`. */\n retiredId?: string\n}\n\nexport interface DedupeResult {\n /** One verdict per examined pair, in iteration order. Pairs where one member\n * was already retired by an earlier pair this cycle are skipped. */\n checks: PairStackCheck[]\n /** Ids retired this cycle (the weaker member of each non-stacking pair). */\n retired: string[]\n /** The shared baseline eval (the \"without\" arm, measured once). */\n baselineResult: EvalResult\n}\n\n/**\n * Pairwise stack-test the `active` artifacts and retire the redundant half of\n * each non-stacking pair.\n *\n * For every unordered pair of active artifacts (optionally within one `kind`),\n * the combined lift is compared against the sum of individual lifts. A pair is\n * non-stacking when `combinedLift < liftA + liftB − tolerance`; the lower-lift\n * member is retired (ties retire the second-examined). An artifact retired by one\n * pair is removed from the remaining comparisons that cycle, so a cluster of\n * three mutually-redundant artifacts collapses to its single strongest member.\n *\n * Cost is one shared baseline run, one re-measure per active artifact (cached\n * across the pairs it appears in), and one combined run per still-eligible pair.\n *\n * @example Retire skills that teach the same tactic as a stronger one:\n * const out = await dedupeArtifacts({ registry, baseline, evalRunner, kind: 'skill' })\n * if (out.retired.length) report(`retired ${out.retired.length} redundant skills`)\n */\nexport async function dedupeArtifacts(opts: DedupeOptions): Promise<DedupeResult> {\n const tolerance = opts.tolerance ?? 0\n if (!Number.isFinite(tolerance) || tolerance < 0) {\n throw new ValidationError(\n `dedupeArtifacts: tolerance must be a finite, non-negative number (got ${tolerance})`,\n )\n }\n\n const active = opts.registry.list({ status: 'active', kind: opts.kind })\n const baselineResult = opts.baselineResult ?? (await opts.evalRunner(opts.baseline, opts.signal))\n\n // Re-measure each artifact's individual lift ONCE (cached across every pair it\n // appears in) so the stack comparison is apples-to-apples in this eval cycle.\n const liftCache = new Map<string, number>()\n const liftOf = async (artifact: ProfileArtifact): Promise<number> => {\n const cached = liftCache.get(artifact.id)\n if (cached !== undefined) return cached\n const lift = await measureMarginalLift({\n baseline: opts.baseline,\n candidate: artifact,\n evalRunner: opts.evalRunner,\n baselineResult,\n signal: opts.signal,\n })\n liftCache.set(artifact.id, lift.scoreDelta)\n return lift.scoreDelta\n }\n\n const retired = new Set<string>()\n const checks: PairStackCheck[] = []\n\n for (let i = 0; i < active.length; i += 1) {\n if (opts.signal?.aborted) break\n const a = active[i]!\n if (retired.has(a.id)) continue\n for (let j = i + 1; j < active.length; j += 1) {\n if (opts.signal?.aborted) break\n const b = active[j]!\n if (retired.has(b.id)) continue\n\n const liftA = await liftOf(a)\n const liftB = await liftOf(b)\n const combined = await opts.evalRunner(applyArtifacts(opts.baseline, [a, b]), opts.signal)\n const combinedLift = combined.composite - baselineResult.composite\n const stackGap = combinedLift - (liftA + liftB)\n const redundant = stackGap < -tolerance\n\n const check: PairStackCheck = {\n pair: [a.id, b.id],\n liftA,\n liftB,\n combinedLift,\n stackGap,\n redundant,\n }\n if (redundant) {\n // Retire the weaker member; on a tie, drop the second-examined.\n const weaker = liftA < liftB ? a : b\n const reason =\n `lifts do not stack: combined Δ=${combinedLift.toFixed(4)} < ` +\n `${a.id} (Δ=${liftA.toFixed(4)}) + ${b.id} (Δ=${liftB.toFixed(4)}) − tol ${tolerance} — ` +\n `redundant with ${(weaker.id === a.id ? b : a).id}`\n opts.registry.retire(weaker.id, reason)\n retired.add(weaker.id)\n check.retiredId = weaker.id\n checks.push(check)\n // `a` itself may have been the one retired — stop pairing it further.\n if (weaker.id === a.id) break\n } else {\n checks.push(check)\n }\n }\n }\n\n return { checks, retired: [...retired], baselineResult }\n}\n","/**\n * `driftWatch` — the scheduled re-measure that DEMOTES decayed artifacts.\n *\n * Promotion is a one-time decision on the evidence available THEN; the world\n * moves on. A skill that earned its lift against last month's task mix, a tool\n * grant the underlying model has since internalized, a prompt line another active\n * artifact now subsumes — each can quietly stop pulling its weight. An artifact\n * that no longer earns its keep but stays `active` is dead weight in every\n * composed profile and a lie in the registry's lift receipt.\n *\n * `driftWatch` closes that gap. It re-runs the SAME `measureMarginalLift`\n * ablation the loop used to promote each `active` artifact — over the CURRENT\n * baseline and the CURRENT eval — and demotes any whose re-measured held-back\n * lift fell below the keep-bar. Demotion moves the artifact `active` → `decayed`\n * (reversible: a later re-measure that recovers the lift can re-promote it), so\n * it drops out of `composeProfile` immediately while staying in the registry as\n * an auditable record.\n *\n * This is NOT a new execution model — it is the existing ablation, run on a\n * schedule, against the active set. The schedule itself is the caller's cron;\n * this module is the one re-measure-and-demote step that cron invokes. It is\n * surface-agnostic (skills, tools, prompts, MCP all re-measure identically) — the\n * only per-surface logic lives, as everywhere in the lifecycle, behind the\n * artifact's own `applyArtifact` bridge, which `measureMarginalLift` already uses.\n */\n\nimport type { AgentProfile } from '@tangle-network/agent-interface'\nimport { ValidationError } from '../errors'\nimport { type EvalResult, type EvalRunner, measureMarginalLift } from './marginal-lift'\nimport type { ArtifactRegistry } from './registry'\nimport type { ProfileArtifact } from './types'\n\nexport interface DriftWatchOptions {\n /** The registry whose `active` artifacts are re-measured. Mutated in place:\n * artifacts that fail the keep-bar are demoted to `decayed`. */\n registry: ArtifactRegistry\n /** The baseline profile each artifact is re-measured ON TOP OF — the SAME\n * ablation shape as promotion. Pass the CURRENT baseline (the world the\n * artifact lives in now), which may differ from the one it was promoted on. */\n baseline: AgentProfile\n /** Scores a profile on the held-back split. The shared baseline arm is run\n * once and reused across every artifact's ablation (the \"without\" arm). */\n evalRunner: EvalRunner\n /**\n * The absolute keep-bar: an artifact stays `active` only while its re-measured\n * held-back lift is STRICTLY ABOVE this floor. Default 0 — an artifact that no\n * longer adds anything (or now subtracts) decays. Mirrors `thresholdPromotionGate`'s\n * `minDelta` so the keep-bar and the promote-bar can be set consistently.\n */\n minLift?: number\n /**\n * The relative keep-bar: an artifact also decays if its re-measured lift fell\n * to below this FRACTION of the lift recorded at promotion (`registry.liftOf`).\n * E.g. `0.5` demotes an artifact that lost more than half its original lift,\n * even if it still clears `minLift`. Default: unset (no relative check — only\n * the absolute `minLift` floor applies). Ignored for an artifact with no\n * recorded prior lift (a bare `promote`), which is judged on `minLift` alone.\n */\n maxRelativeDecay?: number\n /** Restrict the watch to one surface (e.g. re-measure only skills). Default:\n * every `active` artifact, regardless of kind. */\n kind?: ProfileArtifact['kind']\n /** A pre-computed baseline result, to skip the shared \"without\" run when the\n * caller already scored the current baseline this cycle. */\n baselineResult?: EvalResult\n /** Cooperative cancellation, forwarded to every `evalRunner` call. */\n signal?: AbortSignal\n}\n\n/** Per-artifact record of what the re-measure found and decided. */\nexport interface DriftCheck {\n /** The re-measured artifact (status reflects the decision: still `active`, or\n * now `decayed`). */\n artifact: ProfileArtifact\n /** The lift recorded at promotion time (`registry.liftOf` before the check),\n * or `undefined` if it was promoted without a lift receipt. */\n priorLift: number | undefined\n /** The freshly re-measured held-back lift (with − without composite). */\n currentLift: number\n /** Whether this re-measure demoted the artifact (`active` → `decayed`). */\n demoted: boolean\n /** Human-readable reason the artifact was kept or demoted (the same string\n * recorded under `lifecycleReasonKey` on a demotion). */\n reason: string\n}\n\nexport interface DriftWatchResult {\n /** One check per `active` artifact examined, in registry order. */\n checks: DriftCheck[]\n /** Ids of the artifacts demoted to `decayed` this cycle. */\n demoted: string[]\n /** The shared baseline eval (the \"without\" arm, measured once). */\n baselineResult: EvalResult\n}\n\n/**\n * Re-measure every `active` artifact and demote those whose held-back lift\n * decayed below the keep-bar.\n *\n * For each `active` artifact (optionally filtered to one `kind`), this re-runs\n * `measureMarginalLift` over the supplied `baseline`, then applies the keep-bar:\n *\n * - ABSOLUTE: `currentLift > minLift` (default `> 0`), and\n * - RELATIVE (when `maxRelativeDecay` is set AND a prior lift exists):\n * `currentLift >= priorLift * (1 − maxRelativeDecay)`.\n *\n * An artifact failing EITHER bar is demoted to `decayed` (recording the\n * re-measured lift + reason) and drops out of `composeProfile`. An artifact that\n * passes both bars stays `active`, with its recorded lift refreshed to the latest\n * measurement so `liftOf` never reports stale evidence.\n *\n * The baseline \"without\" arm is scored ONCE and shared across all artifacts, so\n * the cost is `1 + (number of active artifacts)` eval runs.\n *\n * @example A nightly cron that demotes any active artifact that lost its lift:\n * const out = await driftWatch({ registry, baseline, evalRunner, minLift: 0 })\n * if (out.demoted.length) report(`demoted ${out.demoted.length} decayed artifacts`)\n */\nexport async function driftWatch(opts: DriftWatchOptions): Promise<DriftWatchResult> {\n if (opts.maxRelativeDecay !== undefined) {\n if (!Number.isFinite(opts.maxRelativeDecay) || opts.maxRelativeDecay < 0) {\n throw new ValidationError(\n `driftWatch: maxRelativeDecay must be a finite, non-negative fraction (got ${opts.maxRelativeDecay})`,\n )\n }\n }\n const minLift = opts.minLift ?? 0\n if (!Number.isFinite(minLift)) {\n throw new ValidationError(`driftWatch: minLift must be a finite number (got ${minLift})`)\n }\n\n // The active set is the only thing that can decay. Snapshot it BEFORE mutating,\n // so demotions this cycle don't shift the set mid-iteration.\n const active = opts.registry.list({ status: 'active', kind: opts.kind })\n\n // Share the baseline \"without\" arm across every artifact's ablation.\n const baselineResult = opts.baselineResult ?? (await opts.evalRunner(opts.baseline, opts.signal))\n\n const checks: DriftCheck[] = []\n const demoted: string[] = []\n for (const artifact of active) {\n if (opts.signal?.aborted) break\n const priorLift = opts.registry.liftOf(artifact.id)\n const lift = await measureMarginalLift({\n baseline: opts.baseline,\n candidate: artifact,\n evalRunner: opts.evalRunner,\n baselineResult,\n signal: opts.signal,\n })\n const currentLift = lift.scoreDelta\n\n const decision = keepDecision(currentLift, priorLift, minLift, opts.maxRelativeDecay)\n if (decision.keep) {\n // Still earning its keep — refresh the lift receipt to the latest evidence.\n const refreshed = opts.registry.promoteWithLift(artifact.id, currentLift)\n checks.push({\n artifact: refreshed,\n priorLift,\n currentLift,\n demoted: false,\n reason: decision.reason,\n })\n } else {\n const decayed = opts.registry.demote(artifact.id, decision.reason, currentLift)\n demoted.push(artifact.id)\n checks.push({\n artifact: decayed,\n priorLift,\n currentLift,\n demoted: true,\n reason: decision.reason,\n })\n }\n }\n\n return { checks, demoted, baselineResult }\n}\n\n/** Apply the absolute + relative keep-bars and explain the verdict. */\nfunction keepDecision(\n currentLift: number,\n priorLift: number | undefined,\n minLift: number,\n maxRelativeDecay: number | undefined,\n): { keep: boolean; reason: string } {\n if (!(currentLift > minLift)) {\n return {\n keep: false,\n reason: `re-measured lift Δ=${currentLift.toFixed(4)} ≤ keep-bar ${minLift} — decayed`,\n }\n }\n if (maxRelativeDecay !== undefined && priorLift !== undefined && priorLift > 0) {\n const floor = priorLift * (1 - maxRelativeDecay)\n if (currentLift < floor) {\n return {\n keep: false,\n reason: `re-measured lift Δ=${currentLift.toFixed(4)} fell below ${(maxRelativeDecay * 100).toFixed(0)}%-decay floor ${floor.toFixed(4)} of prior ${priorLift.toFixed(4)} — decayed`,\n }\n }\n }\n return {\n keep: true,\n reason: `re-measured lift Δ=${currentLift.toFixed(4)} clears the keep-bar — still active`,\n }\n}\n","/**\n * `PromotionGate` — the held-back exam that decides whether a measured candidate\n * artifact is promoted into the registry.\n *\n * The lifecycle measures each candidate's marginal lift (`measureMarginalLift`),\n * but a positive ablation delta on the practice problems is NOT enough to ship —\n * that is how you overfit. The gate is the SECOND, stricter test: it decides\n * promotion from a held-back split (fresh problems the candidate never tuned on)\n * with a significance bar, so a promoted artifact earned its place rather than\n * memorized the practice set.\n *\n * The interface is small and pluggable on purpose: production wires\n * `heldOutPromotionGate`, which delegates to agent-eval's paper-grade\n * `HeldOutGate` (paired-bootstrap CI on the held-out delta + an overfit-gap\n * check). A deterministic test can inject a stub gate. The orchestrator never\n * imports `HeldOutGate` directly — it only knows this interface — so the gate\n * policy is a configuration choice, not a hardcode.\n */\n\nimport { HeldOutGate, type RunRecord } from '@tangle-network/agent-eval'\nimport type { MarginalLift } from './marginal-lift'\n\n/** The verdict a gate returns for one candidate. */\nexport interface PromotionVerdict {\n /** Whether to promote the candidate into the registry as `active`. */\n promote: boolean\n /** Human-readable reason (surfaced in provenance + reports). */\n reason: string\n /** Machine-readable rejection code, or `null` on promote. Mirrors the\n * `HeldOutGate` rejection taxonomy when that gate is the backend. */\n rejectionCode: string | null\n}\n\n/**\n * Decides whether ONE measured candidate is promoted. The lifecycle calls this\n * once per candidate, after `measureMarginalLift` has produced the ablation.\n *\n * `lift` carries the with/without ablation (the marginal contribution); the gate\n * MAY use it directly (the simple \"positive lift on the held-back split\" policy)\n * OR ignore the scalar and decide from the paired per-task records the eval\n * produced (`lift.withArtifact.runs` / `lift.withoutArtifact.runs`) when a\n * significance gate like `HeldOutGate` is the backend.\n */\nexport interface PromotionGate {\n /** Stable label for the gate policy (provenance). */\n kind: string\n decide(lift: MarginalLift): PromotionVerdict\n}\n\n/**\n * The simplest honest gate: promote iff the candidate's marginal lift on the\n * held-back split clears `minDelta` (default `> 0`). It reads only the scalar\n * `scoreDelta`, so it works with any `EvalRunner` (no per-task records needed).\n *\n * Use this when the eval is already scored ON the held-back split and you want a\n * threshold, not a significance test — e.g. a deterministic fixture domain, or a\n * cheap first pass before a paired-bootstrap gate. For paper-grade promotion on\n * noisy live evals, prefer `heldOutPromotionGate`.\n */\nexport function thresholdPromotionGate(minDelta = 0): PromotionGate {\n return {\n kind: `threshold:${minDelta}`,\n decide(lift): PromotionVerdict {\n if (lift.scoreDelta > minDelta) {\n return {\n promote: true,\n reason: `held-back lift Δ=${lift.scoreDelta.toFixed(4)} > ${minDelta}`,\n rejectionCode: null,\n }\n }\n return {\n promote: false,\n reason: `held-back lift Δ=${lift.scoreDelta.toFixed(4)} ≤ ${minDelta}`,\n rejectionCode: 'negative_delta',\n }\n },\n }\n}\n\nexport interface HeldOutPromotionGateOptions {\n /** Stable label of the baseline candidate the held-out records pair against. */\n baselineKey: string\n /** Minimum paired (candidate, baseline) holdout observations. Default 3. */\n minProductiveRuns?: number\n /** Lower-bound on the bootstrap-CI of the median paired holdout delta. Default 0. */\n pairedDeltaThreshold?: number\n /** Max allowed worsening of the (search − holdout) overfit gap. Default 0.15. */\n overfitGapThreshold?: number\n /** Deterministic bootstrap seed (reproducible CIs). Default unseeded. */\n seed?: number\n /** Hard ceiling on the candidate's median per-task USD cost. Default none. */\n costPerTaskCeiling?: number\n}\n\n/**\n * The paper-grade promotion gate: delegate to agent-eval's `HeldOutGate`, which\n * pairs the candidate and baseline per-task holdout records by (experimentId,\n * seed), runs a paired-bootstrap CI on the median delta, and checks the\n * overfit gap. Promotes only when the held-out generalization is real, not luck.\n *\n * REQUIRES the eval to surface per-task records: `lift.withArtifact.runs` (the\n * candidate arm) and `lift.withoutArtifact.runs` (the baseline arm), each\n * carrying matched seeds with both `search` and `holdout` split scores. When the\n * records are absent, this gate FAILS LOUD — a held-out significance claim with\n * no per-task data behind it would be a fabricated number, which the\n * no-silent-fallback doctrine forbids. Use `thresholdPromotionGate` for evals\n * that only produce a scalar composite.\n */\nexport function heldOutPromotionGate(opts: HeldOutPromotionGateOptions): PromotionGate {\n const gate = new HeldOutGate({\n baselineKey: opts.baselineKey,\n minProductiveRuns: opts.minProductiveRuns,\n pairedDeltaThreshold: opts.pairedDeltaThreshold,\n overfitGapThreshold: opts.overfitGapThreshold,\n seed: opts.seed,\n costPerTaskCeiling: opts.costPerTaskCeiling,\n })\n return {\n kind: 'heldout',\n decide(lift): PromotionVerdict {\n const candidateRuns = recordsOf(lift.withArtifact.runs, 'withArtifact', lift.artifactId)\n const baselineRuns = recordsOf(lift.withoutArtifact.runs, 'withoutArtifact', lift.artifactId)\n const decision = gate.evaluate(candidateRuns, baselineRuns)\n return {\n promote: decision.promote,\n reason: decision.reason,\n rejectionCode: decision.rejectionCode,\n }\n },\n }\n}\n\n/** Require the per-task records the held-out gate cannot run without. */\nfunction recordsOf(\n runs: RunRecord[] | undefined,\n arm: 'withArtifact' | 'withoutArtifact',\n artifactId: string,\n): RunRecord[] {\n if (runs === undefined || runs.length === 0) {\n throw new Error(\n `heldOutPromotionGate: the EvalRunner produced no per-task records on the '${arm}' arm for artifact ${JSON.stringify(artifactId)}. ` +\n 'The held-out gate runs a paired-bootstrap on per-task holdout records — it cannot decide from a scalar composite. ' +\n 'Either surface EvalResult.runs from your runner, or use thresholdPromotionGate.',\n )\n }\n return runs\n}\n","/**\n * `promptGenerator` — the `CandidateGenerator` for the PROMPT surface.\n *\n * It is to the prompt surface what `skillGenerator` is to skills: the thin\n * per-surface adapter that turns the agent's history into fresh, unmeasured\n * prompt-instruction candidates the surface-agnostic `runLifecycle` loop then\n * measures, gates, and promotes. Each candidate is a `prompt` artifact carrying\n * one `{ instruction }` line that `applyArtifact` appends to\n * `profile.prompt.instructions`.\n *\n * Two candidate sources, run together each generation:\n *\n * 1. REFINE (incumbent-grounded) — drive agent-eval's `gepaProposer`, the\n * reflective prompt-tier proposer. It reflects on the incumbent prompt +\n * the trace findings and proposes TARGETED rewrites. This is the exploit\n * arm: it polishes the framing the agent already has.\n *\n * 2. SEED (basin escape) — author N genuinely DIVERSE fresh instruction\n * lines from the TASK SPEC, NOT from the incumbent. A reflective proposer\n * only ever perturbs the current surface, so on its own it can polish a\n * local minimum forever — it never tries a fundamentally different framing.\n * The seed arm authors several rewrites that each take a DIFFERENT stance\n * (imperative vs. checklist vs. failure-mode-first, …) so the search can\n * JUMP basins instead of only descending the one it starts in. This is the\n * explore arm, and it is the piece `gepaProposer` structurally cannot do.\n *\n * Both seams are INJECTED (per the §1.5 law: the generator AUTHORS profile\n * pieces, it does not embed a specific LLM loop). `refine` wraps `gepaProposer`;\n * `authorDiverseSeeds` wraps an LLM author. A test injects pure stubs for both,\n * keeping the closed loop deterministically testable; production wires the real\n * router-backed engines (see `productionPromptGenerator`).\n */\n\nimport { type AnalystFinding, callLlmJson, type LlmClientOptions } from '@tangle-network/agent-eval'\nimport {\n type GenerationRecord,\n gepaProposer,\n isProposedCandidate,\n type MutableSurface,\n type ProposeContext,\n type ProposedCandidate,\n type SurfaceProposer,\n} from '@tangle-network/agent-eval/campaign'\nimport type { AgentProfile } from '@tangle-network/agent-interface'\nimport type { CandidateGenerator, GenerateContext } from './generator'\nimport type { ArtifactInput } from './types'\n\n/** A proposed prompt instruction line plus the WHY behind it. The `rationale`\n * rides into the artifact metadata so a promotion decision is auditable. */\nexport interface PromptDraft {\n /** The instruction line appended to `profile.prompt.instructions`. */\n instruction: string\n /** Short human label for review surfaces. */\n label: string\n /** Why this line was proposed — which failure / framing it targets. */\n rationale: string\n}\n\n/**\n * REFINE — incumbent-grounded rewrites. Given the lifecycle context, return\n * targeted edits OF the current prompt framing. The production implementation\n * drives `gepaProposer`; a test injects a pure function. Returns zero or more\n * drafts.\n */\nexport type RefinePrompt = (ctx: GenerateContext) => Promise<PromptDraft[]> | PromptDraft[]\n\n/**\n * SEED — author N genuinely DIVERSE fresh instruction lines from the task spec,\n * NOT mutations of the incumbent. This is the local-minimum escape: each seed\n * MUST take a different framing so the population spans multiple basins. The\n * production implementation makes one LLM call at a non-trivial temperature; a\n * test injects a pure function. Returns up to `count` drafts.\n */\nexport type AuthorDiverseSeeds = (\n ctx: GenerateContext,\n count: number,\n) => Promise<PromptDraft[]> | PromptDraft[]\n\nexport interface PromptGeneratorOptions {\n /** OPTIONAL — the exploit arm (incumbent-grounded rewrites via `gepaProposer`).\n * Omit to run seeds-only. */\n refine?: RefinePrompt\n /** OPTIONAL — the explore arm (diverse fresh seeds). Omit to run refine-only. */\n authorDiverseSeeds?: AuthorDiverseSeeds\n /** How many diverse seeds the explore arm authors each generation. Default 3.\n * Zero disables seeding even when `authorDiverseSeeds` is set. */\n diverseSeedCount?: number\n}\n\n/**\n * Build a `CandidateGenerator` for the prompt surface. Each generation it pools\n * the refine arm (incumbent rewrites) and the seed arm (diverse fresh framings),\n * de-duplicates by instruction text, and emits each as a `prompt` artifact.\n *\n * At least one of `refine` / `authorDiverseSeeds` MUST be provided — a generator\n * with neither has no way to produce a candidate and is a wiring bug, so it\n * throws at construction rather than silently returning `[]` every round.\n *\n * @example Production wiring (refine = gepaProposer, seed = LLM author):\n * promptGenerator({\n * refine: gepaRefine(llm, model),\n * authorDiverseSeeds: routerSeedAuthor(llm, model),\n * })\n */\nexport function promptGenerator(opts: PromptGeneratorOptions): CandidateGenerator<'prompt'> {\n if (!opts.refine && !opts.authorDiverseSeeds) {\n throw new TypeError(\n 'promptGenerator: at least one of `refine` or `authorDiverseSeeds` is required — a generator with neither can never produce a candidate',\n )\n }\n const seedCount = opts.diverseSeedCount ?? 3\n\n return {\n kind: 'prompt',\n async generate(ctx): Promise<ArtifactInput<'prompt'>[]> {\n const drafts: PromptDraft[] = []\n // Explore first: the diverse seeds anchor the population in multiple\n // basins before the incumbent-grounded refinements pile onto one.\n if (opts.authorDiverseSeeds && seedCount > 0) {\n drafts.push(...(await opts.authorDiverseSeeds(ctx, seedCount)))\n }\n if (opts.refine) {\n drafts.push(...(await opts.refine(ctx)))\n }\n return dedupeByInstruction(drafts).map(toPromptArtifact)\n },\n }\n}\n\n/** Drop drafts whose instruction text (trimmed) duplicates an earlier one — the\n * refine arm and a seed can independently land on the same line. First wins. */\nfunction dedupeByInstruction(drafts: PromptDraft[]): PromptDraft[] {\n const seen = new Set<string>()\n const out: PromptDraft[] = []\n for (const draft of drafts) {\n const key = draft.instruction.trim()\n if (key.length === 0 || seen.has(key)) continue\n seen.add(key)\n out.push(draft)\n }\n return out\n}\n\n/** Turn a prompt draft into a `prompt` artifact; the rationale rides in\n * metadata so the promotion decision stays auditable. */\nfunction toPromptArtifact(draft: PromptDraft): ArtifactInput<'prompt'> {\n return {\n kind: 'prompt',\n name: draft.label,\n description: draft.rationale,\n payload: { instruction: draft.instruction },\n metadata: { rationale: draft.rationale },\n }\n}\n\n// ---------------------------------------------------------------------------\n// Production wiring — the real router-backed seams. Kept here (not in the test)\n// so consumers wire the prompt surface in one call; the seams above stay pure\n// so the closed loop is deterministically testable.\n// ---------------------------------------------------------------------------\n\n/** Default reflection/authoring model — a model the Tangle router serves.\n * Callers should pass their own `model`; this is the zero-config fallback. */\nconst defaultPromptModel = 'deepseek-v4-flash'\n\nexport interface ProductionPromptGeneratorOptions {\n /** Router transport (baseUrl/apiKey) for both the refine and seed arms. */\n llm: LlmClientOptions\n /** Model that performs reflection + seed authoring. Default `deepseek-v4-flash`. */\n model?: string\n /** Population size handed to `gepaProposer`'s `propose`. Default 3. */\n refinePopulation?: number\n /** Diverse-seed count. Default 3. */\n diverseSeedCount?: number\n /** Seed-authoring temperature — high on purpose so the seeds spread across\n * framings rather than collapsing onto one. Default 1.0. */\n seedTemperature?: number\n}\n\n/**\n * Production `promptGenerator`: refine via `gepaProposer`, seed via a\n * router-backed diverse author. The one call a consumer makes to grow prompt\n * artifacts with the real engines.\n */\nexport function productionPromptGenerator(\n opts: ProductionPromptGeneratorOptions,\n): CandidateGenerator<'prompt'> {\n const model = opts.model ?? defaultPromptModel\n return promptGenerator({\n refine: gepaRefine(opts.llm, model, opts.refinePopulation ?? 3),\n authorDiverseSeeds: routerSeedAuthor(opts.llm, model, opts.seedTemperature ?? 1.0),\n diverseSeedCount: opts.diverseSeedCount ?? 3,\n })\n}\n\n/**\n * Wrap `gepaProposer` as a `RefinePrompt`. The proposer reflects on the\n * incumbent prompt (`ctx.baseline.prompt.systemPrompt`) and the trace findings\n * to propose `population` targeted rewrites. We feed it the generation-0\n * `ProposeContext` (no scored history yet inside one lifecycle round), so it\n * reflects on the current surface against its mutation primitives — exactly the\n * \"polish the framing you already have\" arm.\n */\nexport function gepaRefine(llm: LlmClientOptions, model: string, population: number): RefinePrompt {\n const proposer: SurfaceProposer<AnalystFinding> = gepaProposer({\n llm,\n model,\n target: 'agent system prompt',\n }) as SurfaceProposer<AnalystFinding>\n\n return async (ctx) => {\n const baseline = baselinePromptSurface(ctx.baseline)\n const proposeCtx: ProposeContext<AnalystFinding> = {\n currentSurface: baseline,\n history: [] as GenerationRecord[],\n findings: [...ctx.findings],\n populationSize: population,\n generation: 0,\n signal: ctx.signal ?? new AbortController().signal,\n }\n const proposed = await proposer.propose(proposeCtx)\n return proposed.flatMap((p) => promptDraftFromProposal(p, baseline))\n }\n}\n\n/**\n * A router-backed `AuthorDiverseSeeds`: one structured LLM call that authors\n * `count` instruction lines, each REQUIRED to take a distinct framing. The\n * prompt is grounded in the task spec (the incumbent system prompt as the spec\n * of WHAT the agent must do) plus the trace findings (what it gets wrong) — but\n * the model is told to author FRESH lines, not edits, so the output spans\n * basins the incumbent's neighborhood never reaches.\n */\nexport function routerSeedAuthor(\n llm: LlmClientOptions,\n model: string,\n temperature: number,\n): AuthorDiverseSeeds {\n return async (ctx, count) => {\n const spec = baselinePromptSurface(ctx.baseline)\n const findingLines = ctx.findings\n .map(\n (f) =>\n `- [${f.area}] ${f.claim}${f.recommended_action ? ` → ${f.recommended_action}` : ''}`,\n )\n .join('\\n')\n const system =\n 'You author standing instructions for an autonomous agent. Given the agent task ' +\n 'spec and its observed mistakes, write DIVERSE candidate instruction lines. CRITICAL: ' +\n 'the candidates must NOT be paraphrases of each other or of the existing spec — each must ' +\n 'take a GENUINELY DIFFERENT framing (e.g. one imperative directive, one as a pre-flight ' +\n 'checklist, one written failure-mode-first, one as a principle, …). Different framings let ' +\n 'a search escape a local optimum instead of polishing one. Return JSON only.'\n const user =\n `Agent task spec (what it must do):\\n${spec || '(no explicit spec — infer from the domain)'}\\n\\n` +\n `Domain: ${ctx.domain}\\n\\n` +\n `Observed mistakes from traces:\\n${findingLines || '(none captured this round)'}\\n\\n` +\n `Author exactly ${count} instruction-line candidates, each a single standing instruction, ` +\n 'each with a distinct framing.'\n\n const { value } = await callLlmJson<{ candidates?: PromptDraftWire[] }>(\n {\n model,\n messages: [\n { role: 'system', content: system },\n { role: 'user', content: user },\n ],\n temperature,\n jsonSchema: {\n name: 'diverse_prompt_seeds',\n schema: {\n type: 'object',\n properties: {\n candidates: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n instruction: { type: 'string' },\n framing: { type: 'string' },\n rationale: { type: 'string' },\n },\n required: ['instruction', 'framing'],\n },\n },\n },\n required: ['candidates'],\n },\n },\n },\n llm,\n )\n\n const wire = value.candidates ?? []\n return wire.slice(0, count).map((c, i) => ({\n instruction: c.instruction,\n label: c.framing?.trim() ? `seed:${c.framing.trim()}` : `seed-${i + 1}`,\n rationale:\n c.rationale?.trim() ||\n `diverse seed (${c.framing?.trim() || 'fresh framing'}) authored from the task spec to escape a local minimum`,\n }))\n }\n}\n\n/** Wire shape of one authored seed from the router. */\ninterface PromptDraftWire {\n instruction: string\n framing?: string\n rationale?: string\n}\n\n/** The prompt surface `gepaProposer` mutates is the system prompt string. */\nfunction baselinePromptSurface(profile: AgentProfile): string {\n return profile.prompt?.systemPrompt ?? ''\n}\n\n/** Turn a `gepaProposer` proposal into a `PromptDraft`. The proposer returns a\n * full rewritten surface; we keep the delta against the baseline as the\n * appended instruction (an artifact is ONE additive line, not a whole-surface\n * swap), falling back to the whole surface when it is a pure addition. */\nfunction promptDraftFromProposal(\n proposal: MutableSurface | ProposedCandidate,\n baseline: string,\n): PromptDraft[] {\n if (isProposedCandidate(proposal)) {\n const surface = proposal.surface\n if (typeof surface !== 'string') return []\n const instruction = surfaceDelta(baseline, surface)\n if (!instruction) return []\n return [\n {\n instruction,\n label: proposal.label?.trim() || 'refine',\n rationale: proposal.rationale?.trim() || 'incumbent-grounded rewrite (gepaProposer)',\n },\n ]\n }\n if (typeof proposal !== 'string') return []\n const instruction = surfaceDelta(baseline, proposal)\n if (!instruction) return []\n return [{ instruction, label: 'refine', rationale: 'incumbent-grounded rewrite (gepaProposer)' }]\n}\n\n/** The line(s) a proposed surface ADDS over the baseline — the additive\n * instruction an artifact represents. When the proposal is a wholesale rewrite\n * (no shared prefix), the whole rewrite is the instruction. */\nfunction surfaceDelta(baseline: string, proposed: string): string {\n const trimmed = proposed.trim()\n const base = baseline.trim()\n if (trimmed === base) return ''\n if (base && trimmed.startsWith(base)) {\n return trimmed.slice(base.length).trim()\n }\n return trimmed\n}\n","/**\n * `ArtifactRegistry` — a typed catalog of profile artifacts with stable ids.\n *\n * The registry is the lifecycle's source of truth for \"what pieces could go into\n * this agent's profile\". It holds `register` / `list` / `get` / `promote`, assigns\n * stable ids, and can `compose` a subset of artifacts onto a baseline profile via\n * the single `applyArtifact` bridge. It owns NO measurement, NO gate, NO LLM — it\n * is a pure in-memory store so callers can persist/snapshot it however they like.\n */\n\nimport type { AgentProfile } from '@tangle-network/agent-interface'\nimport { ValidationError } from '../errors'\nimport { applyArtifacts } from './apply'\nimport type { ArtifactInput, ArtifactKind, ArtifactStatus, ProfileArtifact } from './types'\n\n/** Filter for `list`. Omit a field to leave that dimension unconstrained. */\nexport interface ArtifactQuery {\n kind?: ArtifactKind\n status?: ArtifactStatus\n}\n\n/**\n * The metadata key under which the registry stores an artifact's measured held-\n * back lift. This is the registry INVARIANT's anchor: an artifact is `active`\n * IFF this key holds a finite number — see `promoteWithLift` and `liftOf`. The\n * lifecycle never promotes by status flag alone; the lift score is the receipt.\n * `driftWatch` overwrites it with the latest re-measure so `liftOf` always\n * reflects the most recent evidence.\n */\nexport const liftMetadataKey = 'measuredLift'\n\n/**\n * The metadata key under which the registry records WHY an artifact left the\n * active set — the human-readable reason a `demote` (→ decayed) or `retire`\n * (→ retired) carried. Kept so a demotion/retirement is auditable, not silent.\n */\nexport const lifecycleReasonKey = 'lifecycleReason'\n\n/**\n * A typed, in-memory registry of `ProfileArtifact`s with stable ids.\n *\n * Ids are stable for the life of the registry: `register` assigns one (or honors\n * a caller-supplied id idempotently), and no later operation reassigns it.\n * Re-registering the same id REPLACES the artifact's mutable fields but preserves\n * the id, so a re-proposed candidate keeps its identity across generations.\n */\nexport class ArtifactRegistry {\n private readonly artifacts = new Map<string, ProfileArtifact>()\n private counter = 0\n\n /**\n * Register an artifact, returning the stored record (with its assigned id).\n * When `input.id` is set it is honored (idempotent re-registration replaces\n * the record under the same id); otherwise a stable id is minted as\n * `<kind>-<n>`. `status` defaults to `'candidate'`.\n */\n register<K extends ArtifactKind>(input: ArtifactInput<K>): ProfileArtifact<K> {\n const id = input.id ?? this.mintId(input.kind)\n if (input.id !== undefined && (input.id.length === 0 || input.id.trim() !== input.id)) {\n throw new ValidationError(\n `ArtifactRegistry.register: explicit id must be a non-empty, untrimmed-free string (got ${JSON.stringify(input.id)})`,\n )\n }\n const record: ProfileArtifact<K> = {\n id,\n kind: input.kind,\n key: input.key,\n name: input.name,\n description: input.description,\n payload: input.payload,\n status: input.status ?? 'candidate',\n metadata: input.metadata,\n }\n this.artifacts.set(id, record as ProfileArtifact)\n return record\n }\n\n /** Get an artifact by id, or `undefined` if it was never registered. */\n get(id: string): ProfileArtifact | undefined {\n return this.artifacts.get(id)\n }\n\n /**\n * List artifacts, optionally filtered by `kind` and/or `status`. Returns a new\n * array in registration order; callers may safely sort/mutate the result.\n */\n list(query: ArtifactQuery = {}): ProfileArtifact[] {\n const out: ProfileArtifact[] = []\n for (const artifact of this.artifacts.values()) {\n if (query.kind !== undefined && artifact.kind !== query.kind) continue\n if (query.status !== undefined && artifact.status !== query.status) continue\n out.push(artifact)\n }\n return out\n }\n\n /**\n * Mark an artifact `active`. Fails loud on an unknown id — promoting a\n * non-existent artifact is a caller bug, not a no-op. Returns the updated\n * record. Idempotent: promoting an already-active artifact is a no-op return.\n *\n * NOTE: the artifact-lifecycle INVARIANT (no measured lift ⇒ not active) is\n * enforced by `promoteWithLift`, the path the closed loop uses. This bare\n * `promote` exists for callers that gate elsewhere and just flip the flag; it\n * does NOT record a lift score, so `liftOf` returns `undefined` and a\n * lift-ranked `composeProfile` will skip it. Prefer `promoteWithLift`.\n */\n promote(id: string): ProfileArtifact {\n const artifact = this.artifacts.get(id)\n if (!artifact) {\n throw new ValidationError(\n `ArtifactRegistry.promote: no artifact with id ${JSON.stringify(id)} is registered`,\n )\n }\n if (artifact.status === 'active') return artifact\n const promoted: ProfileArtifact = { ...artifact, status: 'active' }\n this.artifacts.set(id, promoted)\n return promoted\n }\n\n /**\n * Promote an artifact AND record the measured held-back lift that earned it.\n * This is the closed loop's promotion path and the enforcement point of the\n * lifecycle invariant: an artifact becomes `active` only WITH a finite lift\n * number stamped under `liftMetadataKey`. A non-finite `lift` (NaN/Infinity)\n * fails loud — promoting on a broken measurement is exactly the silent-zero the\n * doctrine forbids. Re-promotes a `decayed` artifact whose lift recovered.\n * Returns the updated record.\n */\n promoteWithLift(id: string, lift: number): ProfileArtifact {\n if (!Number.isFinite(lift)) {\n throw new ValidationError(\n `ArtifactRegistry.promoteWithLift: lift for ${JSON.stringify(id)} must be a finite number (got ${lift})`,\n )\n }\n const artifact = this.artifacts.get(id)\n if (!artifact) {\n throw new ValidationError(\n `ArtifactRegistry.promoteWithLift: no artifact with id ${JSON.stringify(id)} is registered`,\n )\n }\n const promoted: ProfileArtifact = {\n ...artifact,\n status: 'active',\n metadata: { ...artifact.metadata, [liftMetadataKey]: lift },\n }\n this.artifacts.set(id, promoted)\n return promoted\n }\n\n /**\n * Demote an `active` artifact to `decayed`: it was promoted, but a later\n * re-measure (`driftWatch`) found its held-back lift fell below the keep-bar.\n * Records the latest re-measured `lift` (so `liftOf` reflects current evidence)\n * and the `reason` (so the demotion is auditable). The artifact stays in the\n * registry — `decayed`, not deleted — so it can be re-promoted if a future\n * re-measure recovers the lift. Fails loud on an unknown id. Demoting a\n * non-`active` artifact fails loud too: only the active set decays.\n */\n demote(id: string, reason: string, lift?: number): ProfileArtifact {\n const artifact = this.artifacts.get(id)\n if (!artifact) {\n throw new ValidationError(\n `ArtifactRegistry.demote: no artifact with id ${JSON.stringify(id)} is registered`,\n )\n }\n if (artifact.status !== 'active') {\n throw new ValidationError(\n `ArtifactRegistry.demote: artifact ${JSON.stringify(id)} is '${artifact.status}', not 'active' — only an active artifact can decay`,\n )\n }\n if (lift !== undefined && !Number.isFinite(lift)) {\n throw new ValidationError(\n `ArtifactRegistry.demote: re-measured lift for ${JSON.stringify(id)} must be a finite number (got ${lift})`,\n )\n }\n const demoted: ProfileArtifact = {\n ...artifact,\n status: 'decayed',\n metadata: {\n ...artifact.metadata,\n [lifecycleReasonKey]: reason,\n ...(lift !== undefined ? { [liftMetadataKey]: lift } : {}),\n },\n }\n this.artifacts.set(id, demoted)\n return demoted\n }\n\n /**\n * Retire an artifact to the terminal `retired` state: it is permanently out of\n * the active set (`dedupeArtifacts` retires the weaker half of a non-stacking\n * pair). Records the `reason` for the audit trail. Unlike `demote`, this is\n * terminal — a retired artifact is never re-promoted by the loop. Idempotent on\n * an already-retired artifact; fails loud on an unknown id.\n */\n retire(id: string, reason: string): ProfileArtifact {\n const artifact = this.artifacts.get(id)\n if (!artifact) {\n throw new ValidationError(\n `ArtifactRegistry.retire: no artifact with id ${JSON.stringify(id)} is registered`,\n )\n }\n if (artifact.status === 'retired') return artifact\n const retired: ProfileArtifact = {\n ...artifact,\n status: 'retired',\n metadata: { ...artifact.metadata, [lifecycleReasonKey]: reason },\n }\n this.artifacts.set(id, retired)\n return retired\n }\n\n /**\n * The measured held-back lift recorded at promotion time (and overwritten by\n * the latest `driftWatch` re-measure), or `undefined` when the artifact was\n * never promoted WITH a lift (a fresh candidate, or one promoted via the bare\n * `promote`). The lifecycle invariant in one accessor: `liftOf(id) ===\n * undefined` ⇒ the artifact has no measured lift ⇒ it is not eligible for a\n * lift-ranked compose. Note this returns the recorded lift regardless of status\n * — `composeProfile` separately filters to `active`, so a `decayed` artifact's\n * stale lift is visible for audit but never folded into a profile.\n */\n liftOf(id: string): number | undefined {\n const value = this.artifacts.get(id)?.metadata?.[liftMetadataKey]\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined\n }\n\n /**\n * Compose a set of registered artifacts onto a baseline profile. With no ids\n * given, composes every `active` artifact (the \"ship the passing set\"\n * default). With explicit ids, composes exactly those (in id order given),\n * failing loud on any unknown id. The applied order is the order passed (or\n * registration order for the active-default), and later artifacts win on key\n * conflicts — same semantics as `applyArtifacts`.\n */\n compose(base: AgentProfile, ids?: readonly string[]): AgentProfile {\n const selected =\n ids === undefined\n ? this.list({ status: 'active' })\n : ids.map((id) => {\n const artifact = this.artifacts.get(id)\n if (!artifact) {\n throw new ValidationError(\n `ArtifactRegistry.compose: no artifact with id ${JSON.stringify(id)} is registered`,\n )\n }\n return artifact\n })\n return applyArtifacts(base, selected)\n }\n\n /** Number of registered artifacts (any status). */\n get size(): number {\n return this.artifacts.size\n }\n\n private mintId(kind: ArtifactKind): string {\n let id: string\n do {\n this.counter += 1\n id = `${kind}-${this.counter}`\n } while (this.artifacts.has(id))\n return id\n }\n}\n\n/** Construct an empty `ArtifactRegistry`. */\nexport function createArtifactRegistry(): ArtifactRegistry {\n return new ArtifactRegistry()\n}\n","/**\n * `runLifecycle` — the ONE closed-loop orchestrator: generate → measure →\n * promote → store.\n *\n * It is surface-agnostic: it knows nothing about skills vs tools vs prompts. All\n * per-surface logic lives behind the `CandidateGenerator` seam. The loop:\n *\n * 1. GENERATE — ask each generator for candidate artifacts from the agent's\n * history (traces/findings). Register them as `candidate`s.\n * 2. MEASURE — for each candidate, run `measureMarginalLift` on the held-\n * back split (the with/without ablation, baseline shared across\n * candidates so the \"without\" arm runs once).\n * 3. PROMOTE — run the `PromotionGate` (default: the held-back exam). On a\n * pass, `promoteWithLift` records the measured lift — the\n * registry invariant: no measured lift ⇒ never active.\n * 4. STORE — every candidate (promoted or not) lands in the registry with\n * provenance (domain, generation, generator kind, gate verdict)\n * + its lift in metadata, so the decision is auditable.\n *\n * Compose is intentionally NOT part of this loop — folding the promoted set back\n * into a profile is a separate, explicit `composeProfile` call the caller makes\n * when it wants a deployable profile. Keeping them separate means a caller can\n * run the loop to grow the catalog without committing a profile change.\n */\n\nimport type { AnalystFinding } from '@tangle-network/agent-eval'\nimport type { AgentProfile } from '@tangle-network/agent-interface'\nimport { ValidationError } from '../errors'\nimport type { PromotionGate, PromotionVerdict } from './gate'\nimport type { CandidateGenerator, GenerateContext } from './generator'\nimport { type EvalResult, type EvalRunner, measureMarginalLift } from './marginal-lift'\nimport { ArtifactRegistry } from './registry'\nimport type { ArtifactKind, ProfileArtifact } from './types'\n\nexport interface RunLifecycleOptions {\n /** The baseline profile candidates are proposed and measured on top of. On a\n * cold start this is the empty (or near-empty) profile. */\n baseline: AgentProfile\n /** The agent/domain id — namespaces provenance + scopes generators. */\n domain: string\n /** The per-surface candidate generators. One per surface the loop grows; the\n * loop runs them in order and pools their candidates. */\n generators: ReadonlyArray<CandidateGenerator>\n /** Scores a profile on the HELD-BACK split. Run by `measureMarginalLift` —\n * once for the shared baseline, once per candidate. */\n evalRunner: EvalRunner\n /** The promotion gate (the held-back exam). Default-free on purpose: the\n * caller chooses the policy (`thresholdPromotionGate` / `heldOutPromotionGate`). */\n gate: PromotionGate\n /** Trace-analyst findings the generators distill/propose from. */\n findings?: ReadonlyArray<AnalystFinding>\n /** Raw captured traces for generators (e.g. a skill `distill`). Opaque. */\n traces?: unknown\n /** Generation counter for provenance. Default 0. */\n generation?: number\n /** An existing registry to grow across generations. Default: a fresh one. */\n registry?: ArtifactRegistry\n /** Cooperative cancellation. */\n signal?: AbortSignal\n}\n\n/** The per-candidate record of what the loop decided and why. */\nexport interface CandidateOutcome {\n /** The stored artifact (status reflects the gate verdict). */\n artifact: ProfileArtifact\n /** The surface this candidate targeted. */\n kind: ArtifactKind\n /** Measured held-back lift (with − without composite). */\n scoreDelta: number\n /** Measured extra USD cost the artifact adds. */\n costDelta: number\n /** The gate's verdict. */\n verdict: PromotionVerdict\n /** Whether the candidate was promoted into the registry as active. */\n promoted: boolean\n}\n\nexport interface RunLifecycleResult {\n /** The registry, grown with this generation's candidates + promotions. */\n registry: ArtifactRegistry\n /** One outcome per candidate the generators produced, in generation order. */\n outcomes: CandidateOutcome[]\n /** Ids of the artifacts promoted this generation. */\n promoted: string[]\n /** The shared baseline eval (the \"without\" arm, measured once). */\n baselineResult: EvalResult\n}\n\n/**\n * Run ONE generation of the artifact lifecycle.\n *\n * @example Cold start on a fixture domain (the closed loop in one call):\n * const out = await runLifecycle({\n * baseline: emptyProfile,\n * domain: 'support-bot',\n * generators: [skillGenerator({ distill, refine })],\n * evalRunner: scoreOnHeldBackSplit,\n * gate: thresholdPromotionGate(),\n * traces: seededTraces,\n * })\n * const composed = composeProfile(out.registry, emptyProfile, { kind: 'skill' })\n */\nexport async function runLifecycle(opts: RunLifecycleOptions): Promise<RunLifecycleResult> {\n if (opts.generators.length === 0) {\n throw new ValidationError('runLifecycle: at least one CandidateGenerator is required')\n }\n\n const registry = opts.registry ?? new ArtifactRegistry()\n const generation = opts.generation ?? 0\n const findings = opts.findings ?? []\n const ctx: GenerateContext = {\n baseline: opts.baseline,\n domain: opts.domain,\n findings,\n traces: opts.traces,\n signal: opts.signal,\n }\n\n // 1. GENERATE — pool candidates from every surface's generator.\n const candidates: ProfileArtifact[] = []\n for (const generator of opts.generators) {\n if (opts.signal?.aborted) break\n const inputs = await generator.generate(ctx)\n for (const input of inputs) {\n const stored = registry.register({\n ...input,\n // Provenance: who proposed this, for which domain, in which generation.\n metadata: {\n ...input.metadata,\n domain: opts.domain,\n generation,\n generatorKind: generator.kind,\n },\n })\n candidates.push(stored)\n }\n }\n\n // 2. MEASURE — share the baseline arm across all candidates (run it once).\n const baselineResult = await opts.evalRunner(opts.baseline, opts.signal)\n\n const outcomes: CandidateOutcome[] = []\n const promoted: string[] = []\n for (const candidate of candidates) {\n if (opts.signal?.aborted) break\n const lift = await measureMarginalLift({\n baseline: opts.baseline,\n candidate,\n evalRunner: opts.evalRunner,\n baselineResult,\n signal: opts.signal,\n })\n\n // 3. PROMOTE — the held-back exam decides; the lift score is the receipt.\n const verdict = opts.gate.decide(lift)\n let artifact = candidate\n if (verdict.promote) {\n artifact = registry.promoteWithLift(candidate.id, lift.scoreDelta)\n promoted.push(candidate.id)\n } else {\n // 4. STORE the verdict on a rejected candidate too (auditable trail).\n artifact = registry.register({\n ...toInput(candidate),\n metadata: {\n ...candidate.metadata,\n gateReason: verdict.reason,\n gateRejectionCode: verdict.rejectionCode,\n scoreDelta: lift.scoreDelta,\n },\n })\n }\n\n outcomes.push({\n artifact,\n kind: candidate.kind,\n scoreDelta: lift.scoreDelta,\n costDelta: lift.costDelta,\n verdict,\n promoted: verdict.promote,\n })\n }\n\n return { registry, outcomes, promoted, baselineResult }\n}\n\n/** Re-derive a registry input from a stored artifact (preserves the stable id so\n * re-registration replaces in place rather than minting a duplicate). */\nfunction toInput(artifact: ProfileArtifact): {\n id: string\n kind: ArtifactKind\n key?: string\n name: string\n description?: string\n payload: ProfileArtifact['payload']\n} {\n return {\n id: artifact.id,\n kind: artifact.kind,\n key: artifact.key,\n name: artifact.name,\n description: artifact.description,\n payload: artifact.payload,\n }\n}\n","/**\n * `skillGenerator` — the reference `CandidateGenerator` for the SKILL surface,\n * and the literal answer to \"an empty profile has no skills, so what creates\n * one?\".\n *\n * It is a two-step pipeline:\n *\n * 1. DISTILL — read the agent's traces/findings and WRITE a new skill document\n * (a reusable how-to note: \"check state before acting\", \"verify after every\n * edit\"). This is the CREATE step. It is the piece `skillOpt` cannot do —\n * an optimizer refines an existing skill, it cannot conjure one from\n * nothing. The production `distill` is `reflectiveGenerator`-style: an LLM\n * reflection over the trace produces the first draft.\n *\n * 2. REFINE — take the distilled draft and improve its wording/structure. The\n * production `refine` drives agent-eval's `skillOptProposer` (the uniform\n * skill-surface proposer factory from `@tangle-network/agent-eval/campaign`,\n * the same source as `gepaProposer` for the prompt surface). Refinement is\n * optional: with no `refine`, the distilled draft IS the candidate.\n *\n * Both steps are INJECTED seams, not hardcoded engines — per the §1.5 law, the\n * generator AUTHORS a profile piece; it does not embed a specific LLM loop. That\n * keeps the closed loop deterministically testable (inject pure stubs) while\n * production wires the real distill + skillOpt. The generator emits a `skill`\n * artifact (an inline `SKILL.md` resource ref) the orchestrator measures + gates.\n */\n\nimport type { AgentProfileResourceRef } from '@tangle-network/agent-interface'\nimport type { CandidateGenerator, GenerateContext } from './generator'\nimport type { ArtifactInput } from './types'\n\n/** A distilled skill draft: a name + the `SKILL.md` body. */\nexport interface SkillDraft {\n /** Skill name — becomes the inline resource ref name + the artifact name. */\n name: string\n /** The `SKILL.md` document body (markdown). */\n content: string\n /** Optional one-line description for review surfaces. */\n description?: string\n}\n\n/**\n * DISTILL — create new skill drafts from the agent's history. Returns zero or\n * more drafts (zero is valid: nothing worth distilling this round). The\n * production implementation reflects over `ctx.traces` / `ctx.findings` with an\n * LLM; a test injects a pure function.\n */\nexport type DistillSkills = (ctx: GenerateContext) => Promise<SkillDraft[]> | SkillDraft[]\n\n/**\n * REFINE — improve ONE distilled draft (wording, structure, examples). The\n * production implementation drives `skillOptProposer`. Returns the refined draft;\n * when omitted from `skillGenerator`, the distilled draft is used as-is.\n */\nexport type RefineSkill = (draft: SkillDraft) => Promise<SkillDraft> | SkillDraft\n\nexport interface SkillGeneratorOptions {\n /** REQUIRED — the create step. Without it there is no skill to optimize. */\n distill: DistillSkills\n /** OPTIONAL — the optimize step. Omit to ship distilled drafts unrefined. */\n refine?: RefineSkill\n}\n\n/**\n * Build a `CandidateGenerator` for the skill surface that distills new skills\n * from history, then (optionally) refines them, and emits each as a `skill`\n * artifact carrying an inline `SKILL.md` resource ref.\n *\n * @example Production wiring (distill = LLM reflection, refine = skillOptProposer):\n * skillGenerator({\n * distill: reflectiveDistill, // creates the draft from traces\n * refine: skillOptRefine, // optimizes the draft via skillOptProposer\n * })\n */\nexport function skillGenerator(opts: SkillGeneratorOptions): CandidateGenerator<'skill'> {\n return {\n kind: 'skill',\n async generate(ctx): Promise<ArtifactInput<'skill'>[]> {\n const drafts = await opts.distill(ctx)\n const out: ArtifactInput<'skill'>[] = []\n for (const draft of drafts) {\n const refined = opts.refine ? await opts.refine(draft) : draft\n out.push(toSkillArtifact(refined))\n }\n return out\n },\n }\n}\n\n/** Turn a (refined) draft into a `skill` artifact with an inline resource ref. */\nfunction toSkillArtifact(draft: SkillDraft): ArtifactInput<'skill'> {\n const resource: AgentProfileResourceRef = {\n kind: 'inline',\n name: draft.name,\n content: draft.content,\n }\n return {\n kind: 'skill',\n name: draft.name,\n description: draft.description,\n payload: { resource },\n }\n}\n","/**\n * `worktreeBuildCandidate` — the PRODUCTION `BuildCandidate`: one fan-out leaf\n * that builds a real tool / MCP server in a fresh git worktree with a real\n * coding harness, and verifies it by the surface's intrinsic check.\n *\n * It is the wiring that makes `buildableGenerator`'s dispatch real, composed\n * entirely from shipped engines — NO new execution model:\n *\n * - `gitWorktreeAdapter` (agent-eval/campaign) cuts the isolated worktree.\n * - `improvementDriver` (this repo) owns the worktree lifecycle (create →\n * generate → finalize/discard) and drives ONE candidate.\n * - `agenticGenerator` (this repo) runs the coding harness IN the worktree\n * with the surface's build-prompt (`toolBuildPrompt` / `mcpBuildPrompt`):\n * research → implement → test, multi-shot resume-on-failure.\n * - the surface's verifier proves the result: `commandVerifier('pnpm', ['test'])`\n * for a tool (compiles + tests pass), `mcpServeVerifier(serve)` for an MCP\n * (boots over stdio + answers `tools/list`). A build that never verifies is\n * dropped by `agenticGenerator` (no `CodeSurface` finalized) → `verified:false`.\n *\n * Kept out of `tool-generator.ts` so the dispatch core stays process-free and\n * unit-testable; this file is the one call a consumer makes to wire the real\n * harness fan-out.\n */\n\nimport type { AnalystFinding } from '@tangle-network/agent-eval'\nimport {\n type CodeSurface,\n gitWorktreeAdapter,\n resolveWorktreePath,\n} from '@tangle-network/agent-eval/campaign'\nimport { ValidationError } from '../errors'\nimport { agenticGenerator, commandVerifier } from '../improvement/agentic-generator'\nimport { mcpBuildPrompt, toolBuildPrompt } from '../improvement/build-prompts'\nimport { type McpServeSpec, mcpServeVerifier } from '../improvement/mcp-serve-verifier'\nimport type { LocalHarness } from '../mcp/local-harness'\nimport type { GenerateContext } from './generator'\nimport type { BuildableKind, BuildCandidate, BuiltCandidate } from './tool-generator'\n\nexport interface WorktreeBuildOptions {\n /** The buildable surface to build (`tool` / `mcp`). */\n kind: BuildableKind\n /** Absolute path to the git checkout each candidate worktree is cut from. */\n repoRoot: string\n /** Which local coding harness drives the build. Default `claude`. */\n harness?: LocalHarness\n /** Base ref each worktree forks from. Default `main`. */\n baseRef?: string\n /** Max harness shots per candidate (the depth dial — resume-on-failure). Default 3. */\n maxShots?: number\n /** Per-shot wall-clock timeout (ms). Forwarded to `agenticGenerator`. */\n timeoutMs?: number\n /**\n * For a `tool` build: the verify command (run in the worktree, exit 0 = pass)\n * and the tool name the resulting grant lands under. Default command\n * `pnpm test`.\n */\n tool?: { verifyCommand?: string; verifyArgs?: string[]; toolName: string }\n /**\n * For an `mcp` build: how to BOOT the built server (the boot-and-probe spec)\n * — used both to verify it serves AND as the artifact's start command. The\n * `cwd` defaults to the candidate worktree.\n */\n mcp?: McpServeSpec\n}\n\n/**\n * Build the production per-candidate seam for `buildableGenerator`. Each call to\n * the returned `BuildCandidate` cuts a fresh worktree, drives the harness to\n * implement + verify the surface, and reports the verified worktree (or\n * `verified:false` with the reason) back to the dispatch for ranking.\n */\nexport function worktreeBuildCandidate(opts: WorktreeBuildOptions): BuildCandidate {\n const harness = opts.harness ?? 'claude'\n const baseRef = opts.baseRef ?? 'main'\n const maxShots = opts.maxShots ?? 3\n const worktree = gitWorktreeAdapter({\n repoRoot: opts.repoRoot,\n branchPrefix: `build-${opts.kind}`,\n })\n\n // The harness generator: surface-specific build-prompt + surface-specific\n // verifier, everything else shared. Identical to the documented composition\n // in build-prompts.ts — no per-kind wrapper, just the pieces wired by data.\n const generator = agenticGenerator({\n harness,\n ...(opts.timeoutMs !== undefined ? { timeoutMs: opts.timeoutMs } : {}),\n buildPrompt: opts.kind === 'mcp' ? mcpBuildPrompt : toolBuildPrompt,\n verify: buildVerifier(opts),\n })\n\n return async (\n ctx: GenerateContext,\n index: number,\n signal: AbortSignal,\n ): Promise<BuiltCandidate> => {\n const label = `${opts.kind}-cand${index}`\n const wt = await worktree.create({ baseRef, label })\n try {\n const { applied, summary } = await generator.generate({\n worktreePath: wt.path,\n report: undefined,\n findings: [...ctx.findings] as AnalystFinding[],\n maxShots,\n signal,\n })\n if (!applied) {\n // The harness never produced a verified change — drop the worktree, and\n // report an unverified build so the dispatch ranks the survivors only.\n await worktree.discard(wt).catch(() => {})\n return {\n label,\n verified: false,\n worktreeRef: wt.path,\n failureReason: 'no verified candidate within maxShots',\n }\n }\n const surface = await worktree.finalize(wt, summary)\n return verifiedBuild(opts, label, surface)\n } catch (err) {\n await worktree.discard(wt).catch(() => {})\n throw err\n }\n }\n}\n\n/** The intrinsic verifier for the surface: `pnpm test` (tool) or boot-and-probe\n * the MCP server (mcp). The verifier is what makes \"verified\" mean compiled +\n * serves, not \"the harness said it was done\". */\nfunction buildVerifier(opts: WorktreeBuildOptions) {\n if (opts.kind === 'mcp') {\n if (!opts.mcp) {\n throw new ValidationError(\n 'worktreeBuildCandidate: an mcp build requires opts.mcp (the boot-and-probe serve spec)',\n )\n }\n return mcpServeVerifier(opts.mcp)\n }\n const command = opts.tool?.verifyCommand ?? 'pnpm'\n const args = opts.tool?.verifyArgs ?? (opts.tool?.verifyCommand ? [] : ['test'])\n return commandVerifier(command, args)\n}\n\n/** Turn a finalized `CodeSurface` into a verified `BuiltCandidate`, carrying the\n * per-surface payload (tool name, or the MCP serve command) the artifact needs. */\nfunction verifiedBuild(\n opts: WorktreeBuildOptions,\n label: string,\n surface: CodeSurface,\n): BuiltCandidate {\n const worktreeRef = resolveWorktreePath(surface)\n if (opts.kind === 'tool') {\n const toolName = opts.tool?.toolName\n if (!toolName || toolName.trim().length === 0) {\n throw new ValidationError(\n 'worktreeBuildCandidate: a tool build requires opts.tool.toolName for the grant key',\n )\n }\n return {\n label,\n verified: true,\n worktreeRef,\n toolName,\n metadata: { summary: surface.summary, baseRef: surface.baseRef },\n }\n }\n const mcp = opts.mcp!\n return {\n label,\n verified: true,\n worktreeRef,\n serve: {\n command: mcp.command,\n ...(mcp.args ? { args: mcp.args } : {}),\n cwd: worktreeRef,\n ...(mcp.env ? { env: mcp.env } : {}),\n },\n metadata: { summary: surface.summary, baseRef: surface.baseRef },\n }\n}\n","/**\n * `buildableGenerator` — the `CandidateGenerator` for the BUILDABLE surfaces\n * (`tool` and `mcp`), and the answer to \"a profile can't *write* a new tool from\n * a prompt rewrite — who builds it?\".\n *\n * Unlike the prompt and skill surfaces, where a candidate is a piece of TEXT an\n * LLM authors in one call, a tool or an MCP server is CODE that must compile,\n * pass its tests, and — for an MCP server — actually boot and serve. You cannot\n * one-shot that reliably. So this generator is a SUPERVISOR DISPATCH, not a\n * single author:\n *\n * 1. FAN OUT — spawn N parallel candidate implementations, each built in its\n * OWN git worktree by a real coding harness (research → implement\n * → test → prove it compiles / actually serves). The per-candidate\n * build is the `buildCandidate` seam; production wires it to the\n * shipped `improvementDriver` + `agenticGenerator` + a verifier\n * (`commandVerifier` for a tool, `mcpServeVerifier` for an MCP).\n * 2. FILTER — keep only the VERIFIED builds. An unverified worktree is never a\n * candidate (the verifier is the gate — same valid-only discipline\n * as `worktreeFanout`'s `selectValidWinner`). A build that never\n * compiles/serves is discarded, never ranked.\n * 3. RANK — score each verified survivor by `measureMarginalLift` against\n * `ctx.baseline` (the with/without held-back ablation — the SAME\n * selector the rest of the lifecycle uses; never a judge).\n * 4. EMIT — return the single best survivor as ONE `tool` / `mcp` artifact,\n * carrying the measured lift + the winning worktree ref as\n * auditable provenance in metadata.\n *\n * Steps 2–4 (filter, lift-rank, emit) are surface-agnostic and live here; only\n * `buildCandidate` (the per-candidate worktree build) varies, and it is INJECTED\n * — production wires the real harness fan-out, a test injects pure stubs so the\n * dispatch is deterministically exercisable without spawning processes or models.\n *\n * NB the lifecycle orchestrator (`runLifecycle`) STILL measures + gates whatever\n * this returns. The rank here is an INTERNAL selection — \"which of the N parallel\n * builds is the best candidate to put forward\" — not the promotion gate. A\n * generator that puts forward a measured-best candidate and an orchestrator that\n * re-measures it on the held-back exam are not redundant: the first picks among\n * siblings, the second decides whether the winner clears the bar to ship.\n */\n\nimport type { AgentProfileMcpServer } from '@tangle-network/agent-interface'\nimport { ValidationError } from '../errors'\nimport type { CandidateGenerator, GenerateContext } from './generator'\nimport { type EvalRunner, measureMarginalLift } from './marginal-lift'\nimport { ArtifactRegistry } from './registry'\nimport type { ArtifactInput, ProfileArtifact } from './types'\n\n/** The buildable surfaces — the kinds whose candidate IS code that must compile\n * / serve, so building one is a fan-out-and-verify dispatch, not a one-shot. */\nexport type BuildableKind = 'tool' | 'mcp'\n\n/**\n * The result of building ONE candidate in its own worktree. A build either\n * verified (compiled + tests passed, or — for an MCP — booted and served) or it\n * did not; an unverified build is dropped before ranking, so `verified:false`\n * carries the reason for the audit trail but never becomes a candidate.\n */\nexport interface BuiltCandidate {\n /** A short label for this candidate (worktree branch / trace node). */\n label: string\n /** Did the build compile + pass its verifier? Only verified builds rank. */\n verified: boolean\n /** The worktree path / git ref holding the built change (provenance). */\n worktreeRef: string\n /**\n * For an `mcp` candidate: how to START the built server (stdio transport).\n * Becomes the `AgentProfileMcpServer` the artifact carries. REQUIRED for an\n * `mcp` build; ignored for a `tool` build.\n */\n serve?: { command: string; args?: string[]; cwd?: string; env?: Record<string, string> }\n /**\n * For a `tool` candidate: the tool name the grant lands under (the profile\n * `tools` key). REQUIRED for a `tool` build; ignored for an `mcp` build.\n */\n toolName?: string\n /** Why the build failed verification, when `verified` is false (audit only). */\n failureReason?: string\n /** Free-form provenance to ride into the artifact metadata (build summary, …). */\n metadata?: Record<string, unknown>\n}\n\n/**\n * BUILD ONE candidate. Given the lifecycle context and the index in the fan-out,\n * produce a fresh worktree implementation and report whether it verified. The\n * production implementation drives a real coding harness in a fresh worktree\n * (`worktreeBuildCandidate`); a test injects a pure function.\n *\n * MUST NOT measure lift, gate, or register — that is the dispatch's / the\n * orchestrator's job. It only builds + verifies ONE sibling.\n */\nexport type BuildCandidate = (\n ctx: GenerateContext,\n index: number,\n signal: AbortSignal,\n) => Promise<BuiltCandidate>\n\nexport interface BuildableGeneratorOptions {\n /** The buildable surface this generator targets (`tool` or `mcp`). */\n kind: BuildableKind\n /** The per-candidate build seam (the fan-out leaf). REQUIRED. */\n buildCandidate: BuildCandidate\n /** How many candidate implementations to build in parallel each generation.\n * Default 3. Must be >= 1. The conserved-budget / live-worker caps that bound\n * the real fan-out live in the production `buildCandidate` wiring. */\n fanout?: number\n /** Scores a profile on the held-back split — used to RANK the verified\n * siblings by `measureMarginalLift`. REQUIRED: without it there is no way to\n * pick the best of N, and \"best\" is the whole point of a fan-out. */\n evalRunner: EvalRunner\n}\n\n/**\n * Build a `CandidateGenerator` for a buildable surface (`tool` / `mcp`). Each\n * generation it fans out `fanout` parallel worktree builds, keeps the verified\n * ones, ranks them by held-back marginal lift, and emits the single best as one\n * artifact. Returns `[]` when no build verifies — the surface had nothing\n * shippable to contribute this round (a valid, common outcome for hard builds).\n *\n * @example Production wiring (build = real harness fan-out, rank = held-back eval):\n * buildableGenerator({\n * kind: 'mcp',\n * buildCandidate: worktreeBuildCandidate({ repoRoot, harness: 'claude' }),\n * evalRunner: scoreOnHeldBackSplit,\n * fanout: 4,\n * })\n */\nexport function buildableGenerator(\n opts: BuildableGeneratorOptions,\n): CandidateGenerator<BuildableKind> {\n const fanout = opts.fanout ?? 3\n if (fanout < 1) {\n throw new ValidationError(\n `buildableGenerator: fanout must be >= 1 (got ${fanout}) — a dispatch with no candidates can never build anything`,\n )\n }\n\n return {\n kind: opts.kind,\n async generate(ctx): Promise<ArtifactInput<BuildableKind>[]> {\n const signal = ctx.signal ?? new AbortController().signal\n\n // 1. FAN OUT — N parallel candidate builds, each in its own worktree.\n const settled = await Promise.all(\n Array.from({ length: fanout }, (_unused, i) => opts.buildCandidate(ctx, i, signal)),\n )\n\n // 2. FILTER — only VERIFIED builds are candidates. An unverified worktree\n // (failed to compile / never served) is discarded, never ranked.\n const verified = settled.filter((b) => b.verified)\n if (verified.length === 0) return []\n\n // 3. RANK — score each survivor by the held-back ablation against the\n // shared baseline (measured once, reused across siblings).\n const baselineResult = await opts.evalRunner(ctx.baseline, signal)\n const ranked: Array<{ built: BuiltCandidate; scoreDelta: number; costDelta: number }> = []\n for (const built of verified) {\n if (signal.aborted) break\n // Stage each survivor as a throwaway artifact so the SAME `applyArtifact`\n // bridge `measureMarginalLift` uses elsewhere applies the built surface.\n const probe = stageProbeArtifact(opts.kind, built)\n const lift = await measureMarginalLift({\n baseline: ctx.baseline,\n candidate: probe,\n evalRunner: opts.evalRunner,\n baselineResult,\n signal,\n })\n ranked.push({ built, scoreDelta: lift.scoreDelta, costDelta: lift.costDelta })\n }\n if (ranked.length === 0) return []\n\n // 4. EMIT the single best survivor as one artifact. Ties → the\n // earliest-built (the fan-out order) wins, matching the kernel's\n // `defaultSelectWinner` tie-break.\n ranked.sort((a, b) => b.scoreDelta - a.scoreDelta)\n const winner = ranked[0]!\n return [toArtifact(opts.kind, winner.built, winner.scoreDelta, winner.costDelta, fanout)]\n },\n }\n}\n\n/**\n * A throwaway artifact used ONLY to drive `applyArtifact` inside the internal\n * rank — it is registered into a private registry (never the lifecycle's), so it\n * gets a real id for `measureMarginalLift` without polluting the caller's\n * catalog. The orchestrator re-registers the EMITTED winner with provenance.\n */\nconst probeRegistry = new ArtifactRegistry()\nfunction stageProbeArtifact(kind: BuildableKind, built: BuiltCandidate): ProfileArtifact {\n return probeRegistry.register(bareArtifact(kind, built))\n}\n\n/** The artifact shape for a built candidate, WITHOUT the lift/provenance the\n * emit step stamps — shared by the probe (internal rank) and the emit. */\nfunction bareArtifact(kind: BuildableKind, built: BuiltCandidate): ArtifactInput<BuildableKind> {\n if (kind === 'tool') {\n const toolName = built.toolName\n if (!toolName || toolName.trim().length === 0) {\n throw new ValidationError(\n `buildableGenerator: a verified 'tool' build must report a toolName (label=${built.label})`,\n )\n }\n return {\n kind: 'tool',\n key: toolName,\n name: toolName,\n payload: { enabled: true },\n } as ArtifactInput<BuildableKind>\n }\n const serve = built.serve\n if (!serve?.command || serve.command.trim().length === 0) {\n throw new ValidationError(\n `buildableGenerator: a verified 'mcp' build must report a serve command (label=${built.label})`,\n )\n }\n const server: AgentProfileMcpServer = {\n transport: 'stdio',\n command: serve.command,\n ...(serve.args ? { args: serve.args } : {}),\n ...(serve.cwd ? { cwd: serve.cwd } : { cwd: built.worktreeRef }),\n ...(serve.env ? { env: serve.env } : {}),\n enabled: true,\n }\n return {\n kind: 'mcp',\n key: built.label,\n name: built.label,\n payload: { server },\n } as ArtifactInput<BuildableKind>\n}\n\n/** The EMITTED winner: the bare artifact plus the measured lift + worktree\n * provenance in metadata, so the promotion decision is auditable. The\n * orchestrator stamps domain/generation provenance on top when it registers. */\nfunction toArtifact(\n kind: BuildableKind,\n built: BuiltCandidate,\n scoreDelta: number,\n costDelta: number,\n fanout: number,\n): ArtifactInput<BuildableKind> {\n const bare = bareArtifact(kind, built)\n return {\n ...bare,\n description: `built via ${fanout}-way fan-out; won on +${scoreDelta.toFixed(4)} held-back lift`,\n metadata: {\n ...built.metadata,\n worktreeRef: built.worktreeRef,\n buildLabel: built.label,\n fanout,\n // The INTERNAL rank lift (best-of-N selection), distinct from the\n // orchestrator's promotion-gate measurement.\n siblingScoreDelta: scoreDelta,\n siblingCostDelta: costDelta,\n },\n }\n}\n"],"mappings":"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","names":["instruction"]}
@@ -2,8 +2,8 @@ import { Scenario } from '@tangle-network/agent-eval/campaign';
2
2
  import { SelfImproveOptions, SelfImproveResult } from '@tangle-network/agent-eval/contract';
3
3
  import { R as RunAnalystLoopOpts, a as RunAnalystLoopResult } from './types-BC3bZpH0.js';
4
4
  import { F as FactCandidate, C as CreateKbGateOptions } from './kb-gate-CuzMYGYM.js';
5
- import { B as Budget } from './worktree-CtuEQ7bZ.js';
6
- import { A as AuthoredHarness, W as WinnerStrategy, a as WorktreeFanoutOptions, b as WorktreePatchArtifact } from './worktree-fanout-Cdez8GR7.js';
5
+ import { B as Budget } from './worktree-CDxqwxGo.js';
6
+ import { A as AuthoredHarness, W as WinnerStrategy, a as WorktreeFanoutOptions, b as WorktreePatchArtifact } from './worktree-fanout-CljF1L2v.js';
7
7
 
8
8
  /**
9
9
  * @experimental
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
- export { L as LoopRunnerCliArgs, e as LoopRunnerCliResult, p as parseLoopRunnerArgv, k as runLoopRunnerCli } from './loop-runner-bin-BPthX22l.js';
2
+ export { L as LoopRunnerCliArgs, e as LoopRunnerCliResult, p as parseLoopRunnerArgv, k as runLoopRunnerCli } from './loop-runner-bin-Dbtg787n.js';
3
3
  import '@tangle-network/agent-eval/campaign';
4
4
  import '@tangle-network/agent-eval/contract';
5
5
  import './types-BC3bZpH0.js';
6
6
  import '@tangle-network/agent-eval';
7
7
  import './kb-gate-CuzMYGYM.js';
8
- import './worktree-CtuEQ7bZ.js';
8
+ import './worktree-CDxqwxGo.js';
9
9
  import '@tangle-network/agent-interface';
10
10
  import '@tangle-network/sandbox';
11
- import './types-YimN9PQP.js';
12
- import './worktree-fanout-Cdez8GR7.js';
11
+ import './types-B-jWSfcu.js';
12
+ import './worktree-fanout-CljF1L2v.js';
13
13
  import './local-harness-BE_h8szs.js';
14
14
  import 'node:child_process';