@tangle-network/agent-eval 0.114.0 → 0.115.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.
package/dist/openapi.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "openapi": "3.1.0",
3
3
  "info": {
4
4
  "title": "@tangle-network/agent-eval — wire protocol",
5
- "version": "0.114.0",
5
+ "version": "0.115.1",
6
6
  "description": "HTTP and stdio RPC interface to agent-eval. The TypeScript runtime is the source of truth; this spec is the contract that cross-language clients (Python, Rust, Go) generate from.\n\nWire-protocol version: 1.0.0. Bumps on breaking changes to request/response schemas.",
7
7
  "contact": {
8
8
  "name": "Tangle Network",
@@ -1,5 +1,6 @@
1
1
  import { A as AgentEvalError } from './errors-oeQrLqXC.js';
2
2
  import { R as RunRecord } from './run-record-DksGsfgv.js';
3
+ import { P as PairedBootstrapOptions, M as McNemarResult, R as RiskDifferenceResult, a as PairedBootstrapResult } from './statistics-oUbOJe-S.js';
3
4
  import { C as ChatClient } from './kind-factory-20hcaYpf.js';
4
5
  import { a as JudgeDimension, S as Scenario, b as JudgeConfig } from './types-CgSlO6wT.js';
5
6
  import { TCloud } from '@tangle-network/tcloud';
@@ -76,6 +77,159 @@ declare function assertRealBackend(records: ReadonlyArray<RunRecord>, opts?: {
76
77
  allowMixed?: boolean;
77
78
  }): BackendIntegrityReport;
78
79
 
80
+ /**
81
+ * Matched-pair arm comparison — "did the treatment arm beat the baseline arm
82
+ * on the SAME work items?"
83
+ *
84
+ * An arm A/B over run records is only trustworthy when it is PAIRED: the same
85
+ * task/scenario/seed evaluated under both arms, compared item-by-item, so
86
+ * inter-item difficulty variance cancels instead of masquerading as an arm
87
+ * effect. This module owns the two error-prone steps every consumer otherwise
88
+ * hand-rolls:
89
+ *
90
+ * 1. Pairing — matching rows across arms by `pairKey` (and by `repKey`
91
+ * within multi-rep items), with leftovers REPORTED rather than silently
92
+ * dropped (a silently unbalanced pairing biases every paired statistic
93
+ * downstream). Pairing never keys on outcome content: matching reps by
94
+ * their outcomes deflates discordant-pair counts and makes McNemar
95
+ * anti-conservative, so reps pair only by (`pairKey`, `repKey`) identity.
96
+ * 2. Composition — feeding the matched pairs to the correct paired
97
+ * estimators that already live in `statistics`: `mcnemar` +
98
+ * `pairedRiskDifference` for pass/fail, `pairedBootstrap` +
99
+ * `wilcoxonSignedRank` for continuous metrics. No statistic is
100
+ * re-implemented here.
101
+ *
102
+ * The row shape is deliberately structural — callers project a `RunRecord`
103
+ * (or any record) into `{ pairKey, arm, pass?, metrics? }`. Arm names are
104
+ * caller-supplied parameters; the module ships no domain literal.
105
+ */
106
+
107
+ /** One arm observation of one work item. Structural on purpose: callers
108
+ * project their own record type (e.g. a `RunRecord`) into this shape. */
109
+ interface PairedArmRow {
110
+ /** Matching key — rows sharing a `pairKey` across both arms form pairs
111
+ * (typically the task/scenario/seed identity). */
112
+ pairKey: string;
113
+ /** Rep identity within a `pairKey` (e.g. a seed or rep number). Required on
114
+ * every row of a `pairKey` that has more than one rep in either arm; reps
115
+ * then pair only on exact (`pairKey`, `repKey`) match, never on outcome
116
+ * content. Optional when each arm has at most one rep of the item. */
117
+ repKey?: string;
118
+ /** Arm label this row was produced under. */
119
+ arm: string;
120
+ /** Binary outcome; omit when the comparison has no pass/fail notion. */
121
+ pass?: boolean;
122
+ /** Named numeric measurements (score, cost, latency, …). */
123
+ metrics?: Record<string, number>;
124
+ }
125
+ interface PairArmsOptions {
126
+ /** Arm treated as the control side of every pair. */
127
+ baselineArm: string;
128
+ /** Arm treated as the treatment side of every pair. */
129
+ treatmentArm: string;
130
+ }
131
+ /** One matched (baseline, treatment) observation of the same work item. */
132
+ interface MatchedPair {
133
+ pairKey: string;
134
+ /** 0-based position of this pair within its `pairKey`, ordered by sorted
135
+ * `repKey` (always 0 for a single-rep item). The rep identity itself is on
136
+ * the rows (`baseline.repKey` / `treatment.repKey`). */
137
+ repIndex: number;
138
+ baseline: PairedArmRow;
139
+ treatment: PairedArmRow;
140
+ }
141
+ interface PairArmsResult {
142
+ /** Matched pairs, ordered by (`pairKey`, `repIndex`). */
143
+ pairs: MatchedPair[];
144
+ /** Baseline rows left without a treatment counterpart — reported, never
145
+ * silently dropped. */
146
+ unpairedBaseline: PairedArmRow[];
147
+ /** Treatment rows left without a baseline counterpart. */
148
+ unpairedTreatment: PairedArmRow[];
149
+ }
150
+ /**
151
+ * Match rows across two arms into (baseline, treatment) pairs by `pairKey`.
152
+ *
153
+ * A `pairKey` with at most one row per arm pairs directly, no `repKey`
154
+ * needed. A `pairKey` with multiple reps in either arm requires `repKey` on
155
+ * every one of its rows, and reps pair only on exact (`pairKey`, `repKey`)
156
+ * match — pairing is keyed purely on row identity, never on outcome content
157
+ * (outcome-keyed matching deflates discordant counts and biases McNemar), and
158
+ * is therefore independent of input order. Reps whose `repKey` has no
159
+ * counterpart in the other arm, and items present in only one arm, land in
160
+ * the unpaired lists — reported, never truncated.
161
+ *
162
+ * Fail-loud: throws when either named arm has zero rows (an unknown arm
163
+ * name would otherwise read as "everything unpaired"), when the two arm
164
+ * names are equal, when a multi-rep `pairKey` has a row without `repKey`, or
165
+ * when a (`pairKey`, arm) group repeats a `repKey` (the match would be
166
+ * ambiguous).
167
+ */
168
+ declare function pairArms(rows: readonly PairedArmRow[], opts: PairArmsOptions): PairArmsResult;
169
+ /** Paired pass/fail comparison over the pairs where BOTH sides carry `pass`. */
170
+ interface PairedCorrectness {
171
+ /** Discordant pairs where the treatment passed and the baseline failed. */
172
+ b10: number;
173
+ /** Discordant pairs where the baseline passed and the treatment failed. */
174
+ b01: number;
175
+ /** Exact McNemar significance over the paired outcomes (`b === b10`, `c === b01`). */
176
+ mcnemar: McNemarResult;
177
+ /** Paired effect size: p(treatment) − p(baseline) with a paired-variance CI. */
178
+ riskDifference: RiskDifferenceResult;
179
+ }
180
+ /** Paired delta summary for one named metric (delta = treatment − baseline). */
181
+ interface PairedMetricDelta {
182
+ name: string;
183
+ /** Pairs where BOTH sides carry a finite value for this metric. */
184
+ n: number;
185
+ /** Pairs where at least one side does not carry the metric. */
186
+ nMissing: number;
187
+ /** Median paired delta; NaN when `n === 0` (no data ≠ measured zero). */
188
+ medianDelta: number;
189
+ /** Mean paired delta; NaN when `n === 0`. */
190
+ meanDelta: number;
191
+ /** Bootstrap CI on the paired delta (`pairedBootstrap`); null when
192
+ * `n === 0` — a zero-width [0, 0] interval on no data would read as a
193
+ * measured tight null. */
194
+ bootstrapCi: PairedBootstrapResult | null;
195
+ /** Wilcoxon signed-rank test on the paired deltas; null when `n === 0`. */
196
+ wilcoxon: {
197
+ w: number;
198
+ p: number;
199
+ } | null;
200
+ }
201
+ interface ComparePairedArmsOptions extends PairArmsOptions {
202
+ /** Metrics to compare. Default: every metric name observed on any matched
203
+ * pair, sorted. A name that appears on no pair is still reported (with
204
+ * `n = 0`) so a misspelled metric is visible instead of vanishing. */
205
+ metricNames?: string[];
206
+ /** Passed through to `pairedBootstrap` — set `seed` for reproducible CIs. */
207
+ bootstrap?: PairedBootstrapOptions;
208
+ }
209
+ interface PairedArmsComparison {
210
+ nPairs: number;
211
+ nUnpairedBaseline: number;
212
+ nUnpairedTreatment: number;
213
+ /** null when no matched pair carries `pass` on both sides — a pass/fail
214
+ * verdict over rows that never measured pass/fail would be fabricated. */
215
+ correctness: PairedCorrectness | null;
216
+ metricDeltas: PairedMetricDelta[];
217
+ }
218
+ /**
219
+ * Full matched-pair arm comparison: pair via {@link pairArms}, then compose
220
+ * the paired estimators from `statistics` over the matched pairs.
221
+ *
222
+ * Correctness uses only the pairs where both sides carry `pass` (`mcnemar.n`
223
+ * is that subset's size); each metric uses only the pairs where both sides
224
+ * carry a finite value for it, with the remainder counted in `nMissing`.
225
+ * Deltas are treatment − baseline throughout.
226
+ *
227
+ * Fail-loud: inherits {@link pairArms}'s unknown-arm throw, and throws on a
228
+ * non-finite metric value — silently treating corrupt telemetry as "metric
229
+ * absent" would misreport it as missing coverage.
230
+ */
231
+ declare function comparePairedArms(rows: readonly PairedArmRow[], opts: ComparePairedArmsOptions): PairedArmsComparison;
232
+
79
233
  /**
80
234
  * Artifact validators.
81
235
  *
@@ -585,4 +739,4 @@ declare function evaluateHypothesis(manifest: SignedManifest, observed: {
585
739
  pValue: number;
586
740
  }): Promise<HypothesisResult>;
587
741
 
588
- export { type Artifact as A, type BackendIntegrityReport as B, type CompletionRequirement as C, extractProducedState as D, hashJson as E, jsonHasKeys as F, parseCorrectnessResponse as G, type HypothesisManifest as H, regexMatch as I, signManifest as J, summarizeBackendIntegrity as K, type LlmJudgeDimension as L, verifyCompletion as M, verifyManifest as N, type ProducedState as P, type RuntimeEventLike as R, type SignedManifest as S, type TaskGold as T, type ValidationContext as V, type CompletionVerdict as a, type CorrectnessChecker as b, type LlmJudgeOptions as c, type ArtifactEventLike as d, type ArtifactValidator as e, BackendIntegrityError as f, type HypothesisResult as g, type LlmCorrectnessCheckerOpts as h, type ProducedProposal as i, type ProposalEventLike as j, type RequirementCheck as k, llmJudge as l, type SatisfiedBy as m, type SignedManifestAlgo as n, type ToolCallEventLike as o, type ValidationIssue as p, type ValidationResult as q, assertRealBackend as r, byteLengthRange as s, canonicalize as t, completionVerdict as u, composeValidators as v, containsAll as w, createLlmCorrectnessChecker as x, createTokenRecallChecker as y, evaluateHypothesis as z };
742
+ export { verifyCompletion as $, type Artifact as A, type BackendIntegrityReport as B, type CompletionRequirement as C, canonicalize as D, comparePairedArms as E, completionVerdict as F, composeValidators as G, type HypothesisManifest as H, containsAll as I, createLlmCorrectnessChecker as J, createTokenRecallChecker as K, type LlmJudgeDimension as L, type MatchedPair as M, evaluateHypothesis as N, extractProducedState as O, type PairedArmsComparison as P, hashJson as Q, type RuntimeEventLike as R, type SignedManifest as S, type TaskGold as T, jsonHasKeys as U, type ValidationContext as V, pairArms as W, parseCorrectnessResponse as X, regexMatch as Y, signManifest as Z, summarizeBackendIntegrity as _, type CompletionVerdict as a, verifyManifest as a0, type ProducedState as b, type CorrectnessChecker as c, type LlmJudgeOptions as d, type ArtifactEventLike as e, type ArtifactValidator as f, BackendIntegrityError as g, type ComparePairedArmsOptions as h, type HypothesisResult as i, type LlmCorrectnessCheckerOpts as j, type PairArmsOptions as k, llmJudge as l, type PairArmsResult as m, type PairedArmRow as n, type PairedCorrectness as o, type PairedMetricDelta as p, type ProducedProposal as q, type ProposalEventLike as r, type RequirementCheck as s, type SatisfiedBy as t, type SignedManifestAlgo as u, type ToolCallEventLike as v, type ValidationIssue as w, type ValidationResult as x, assertRealBackend as y, byteLengthRange as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-eval",
3
- "version": "0.114.0",
3
+ "version": "0.115.1",
4
4
  "description": "Evaluate and improve AI agents from runs, traces, judges, and feedback. Compare candidates, cluster failures, measure lift, and gate releases.",
5
5
  "homepage": "https://github.com/tangle-network/agent-eval#readme",
6
6
  "repository": {
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/analyst/ax-service.ts","../src/analyst/chat-client.ts","../src/concurrency.ts","../src/locked-jsonl-appender.ts","../src/analyst/findings-store.ts","../src/analyst/kinds/skill-usage.ts","../src/run-critic.ts","../src/semantic-concept-judge.ts"],"sourcesContent":["import type { AxAIService } from '@ax-llm/ax'\nimport { ai } from '@ax-llm/ax'\n\nexport interface CreateAnalystAiConfig {\n /** OpenAI-compatible API key forwarded as `Authorization: Bearer`.\n * cli-bridge ignores the value on loopback but Ax requires a non-empty string. */\n apiKey: string\n /** OpenAI-compatible base URL — e.g. `https://router.tangle.tools/v1` or a\n * cli-bridge loopback. */\n baseUrl: string\n /** Model id forwarded to the analyst actor + responder. */\n model: string\n /** Ax provider name. Defaults to the OpenAI-compatible client. */\n provider?: 'openai' | 'anthropic'\n}\n\n/**\n * Construct the `AxAIService` an analyst kind calls through\n * (`createTraceAnalystKind({ ai })`).\n *\n * Ax's `ai()` pins `config.model` to the OpenAI catalog enum, but every\n * OpenAI-compatible router an analyst points at (router.tangle.tools,\n * cli-bridge) accepts arbitrary model ids (claude-code/sonnet, openai/gpt-5.4,\n * …). Consumers were each re-rolling `ai({ name, apiKey, apiURL, config })`\n * behind an `as (a: any) => any` cast to dodge the enum; this is the one\n * canonical constructor so they don't have to — and don't take a direct\n * `@ax-llm/ax` dependency for it.\n */\nexport function createAnalystAi(config: CreateAnalystAiConfig): AxAIService {\n return ai({\n name: config.provider ?? 'openai',\n apiKey: config.apiKey,\n apiURL: config.baseUrl,\n config: { model: config.model },\n })\n}\n","/**\n * ChatClient — the single LLM abstraction analysts call.\n *\n * agent-eval already ships an `LlmClient` (OpenAI-compatible, retry,\n * graceful JSON-schema degrade) and judges that talk to `TCloud`. Two\n * mixed patterns force every analyst author to pick a transport, which\n * couples analyst code to runtime concerns (cli-bridge vs router vs\n * sandbox-sdk) it shouldn't know about.\n *\n * `ChatClient` is one interface every analyst takes via `AnalystContext.chat`.\n * The operator decides at the registry boundary which transport binds\n * to it. Analyst code stays transport-agnostic; swapping production\n * (sandbox-sdk) for local dev (cli-bridge) or tests (mock) is a one-\n * line factory call.\n *\n * Designed to coexist: existing `LlmClient` callers and existing\n * `TCloud`-based judges keep working untouched. New analyst code uses\n * `ChatClient`. When old call sites migrate, they pick up budgeting,\n * cancellation, and unified telemetry for free.\n */\n\nimport {\n type LlmCallRequest,\n type LlmCallResult,\n LlmClient,\n type LlmClientOptions,\n} from '../llm-client'\n\n/**\n * Unified chat interface. Mirrors LlmCallRequest/Result so the OpenAI-\n * compatible mental model stays. Two methods: a one-shot `chat()` and\n * an `streamChat()` for future agentic loops (not yet exposed).\n */\nexport interface ChatClient {\n /** Display name of the bound transport — included in telemetry. */\n readonly transport: ChatTransport\n /** Default model when caller omits — operators bind this per environment. */\n readonly defaultModel?: string\n\n chat(req: ChatRequest, opts?: ChatCallOpts): Promise<ChatResponse>\n}\n\nexport type ChatTransport =\n | 'router' // router.tangle.tools — production paid models\n | 'sandbox-sdk' // box.streamPrompt() — chat completion via sandbox SDK\n | 'cli-bridge' // local cli-bridge for dev / local-only runs\n | 'direct-provider' // direct OpenAI / Anthropic / etc. — bypass router\n | 'mock' // test-time injection\n\nexport interface ChatRequest extends Omit<LlmCallRequest, 'model'> {\n /** Optional — falls back to ChatClient.defaultModel. */\n model?: string\n}\n\nexport type ChatResponse = LlmCallResult\n\nexport interface ChatCallOpts {\n /** Cancel the in-flight request. */\n signal?: AbortSignal\n /** Hard USD ceiling for this single call (informational; the underlying transport may not enforce). */\n maxCostUsd?: number\n /** Correlation tag carried into request headers when the transport allows. */\n correlationId?: string\n}\n\n// ── Factory ─────────────────────────────────────────────────────────\n\nexport type CreateChatClientOpts =\n | RouterTransportOpts\n | CliBridgeTransportOpts\n | DirectProviderTransportOpts\n | SandboxSdkTransportOpts\n | MockTransportOpts\n\ninterface BaseTransportOpts {\n defaultModel?: string\n}\n\nexport interface RouterTransportOpts extends BaseTransportOpts {\n transport: 'router'\n baseUrl?: string\n apiKey: string\n}\n\nexport interface CliBridgeTransportOpts extends BaseTransportOpts {\n transport: 'cli-bridge'\n baseUrl?: string\n bearer?: string\n}\n\nexport interface DirectProviderTransportOpts extends BaseTransportOpts {\n transport: 'direct-provider'\n baseUrl: string\n apiKey: string\n}\n\n/**\n * Sandbox-SDK transport. Provided as a thin pass-through: the caller\n * supplies a callable that mimics LlmClient.chat() against an already-\n * configured Sandbox handle. We don't import the SDK here to keep\n * agent-eval dep-free of @tangle-network/sandbox.\n */\nexport interface SandboxSdkTransportOpts extends BaseTransportOpts {\n transport: 'sandbox-sdk'\n chat: (req: ChatRequest, opts?: ChatCallOpts) => Promise<ChatResponse>\n}\n\n/**\n * Mock transport for tests. The handler receives the request and returns\n * whatever the test wants. No retries, no JSON-schema degrade.\n */\nexport interface MockTransportOpts extends BaseTransportOpts {\n transport: 'mock'\n handler: (req: ChatRequest, opts?: ChatCallOpts) => Promise<ChatResponse>\n}\n\n/**\n * Build a ChatClient bound to a specific transport. The returned client\n * is safe to share across analysts in a single registry run.\n */\nexport function createChatClient(opts: CreateChatClientOpts): ChatClient {\n switch (opts.transport) {\n case 'router':\n return wrapLlmClient(\n opts.transport,\n opts.defaultModel,\n new LlmClient({\n baseUrl: opts.baseUrl ?? 'https://router.tangle.tools/v1',\n apiKey: opts.apiKey,\n } as LlmClientOptions),\n )\n case 'cli-bridge':\n return wrapLlmClient(\n opts.transport,\n opts.defaultModel,\n new LlmClient({\n baseUrl: opts.baseUrl ?? 'http://127.0.0.1:3344/v1',\n apiKey: opts.bearer ?? '',\n } as LlmClientOptions),\n )\n case 'direct-provider':\n return wrapLlmClient(\n opts.transport,\n opts.defaultModel,\n new LlmClient({\n baseUrl: opts.baseUrl,\n apiKey: opts.apiKey,\n } as LlmClientOptions),\n )\n case 'sandbox-sdk':\n return {\n transport: 'sandbox-sdk',\n defaultModel: opts.defaultModel,\n chat: async (req, callOpts) => opts.chat(resolveModel(req, opts.defaultModel), callOpts),\n }\n case 'mock':\n return {\n transport: 'mock',\n defaultModel: opts.defaultModel,\n chat: async (req, callOpts) => opts.handler(resolveModel(req, opts.defaultModel), callOpts),\n }\n }\n}\n\nfunction wrapLlmClient(\n transport: ChatTransport,\n defaultModel: string | undefined,\n inner: LlmClient,\n): ChatClient {\n return {\n transport,\n defaultModel,\n chat: async (req, callOpts) => {\n const resolved = resolveModel(req, defaultModel)\n // LlmClient.call doesn't accept an external AbortSignal today (it\n // owns its own AbortController for the per-attempt timeout). We\n // race the response against the caller's signal so awaiting code\n // unblocks on abort. The in-flight HTTP request still runs to its\n // own timeoutMs — when LlmClient grows a signal parameter, wire\n // it directly here and drop the race.\n const call = inner.call({\n model: resolved.model!,\n messages: req.messages,\n jsonMode: req.jsonMode,\n jsonSchema: req.jsonSchema,\n temperature: req.temperature,\n maxTokens: req.maxTokens,\n timeoutMs: req.timeoutMs,\n })\n if (!callOpts?.signal) return await call\n return await Promise.race([call, abortAsRejection(callOpts.signal)])\n },\n }\n}\n\nfunction abortAsRejection(signal: AbortSignal): Promise<never> {\n if (signal.aborted) return Promise.reject(toAbortError(signal))\n return new Promise<never>((_, reject) => {\n signal.addEventListener('abort', () => reject(toAbortError(signal)), { once: true })\n })\n}\n\nfunction toAbortError(signal: AbortSignal): Error {\n const reason = (signal as { reason?: unknown }).reason\n if (reason instanceof Error) return reason\n const e = new Error('ChatClient.chat: aborted')\n e.name = 'AbortError'\n return e\n}\n\nfunction resolveModel(req: ChatRequest, defaultModel: string | undefined): ChatRequest {\n if (req.model) return req\n if (!defaultModel) {\n throw new Error(\n 'ChatClient.chat: no model on request and no defaultModel on the client. ' +\n 'Either pass req.model or bind defaultModel at createChatClient().',\n )\n }\n return { ...req, model: defaultModel }\n}\n","/**\n * concurrency — small primitives the evolution loop needs.\n *\n * `Mutex` is a zero-dep async lock with FIFO fairness. The evolution loop\n * uses it to serialise checkout/build/commit sequences inside a single\n * pool slot, and to gate concurrent JSONL writers (see\n * `lockedJsonlReferenceReplayStore`).\n *\n * Deliberately minimal — no priority queue, no timeouts. If you need\n * those, swap to `async-mutex` at the call site.\n */\n\nexport class Mutex {\n private locked = false\n private readonly waiters: Array<() => void> = []\n\n async acquire(): Promise<() => void> {\n if (!this.locked) {\n this.locked = true\n return () => this.release()\n }\n return new Promise<() => void>((resolve) => {\n this.waiters.push(() => {\n resolve(() => this.release())\n })\n })\n }\n\n private release(): void {\n const next = this.waiters.shift()\n if (next) {\n next()\n } else {\n this.locked = false\n }\n }\n\n async runExclusive<T>(fn: () => Promise<T> | T): Promise<T> {\n const release = await this.acquire()\n try {\n return await fn()\n } finally {\n release()\n }\n }\n\n /** True iff someone holds the lock right now. Diagnostics only. */\n get isLocked(): boolean {\n return this.locked\n }\n\n /** Pending waiter count. Diagnostics only. */\n get pending(): number {\n return this.waiters.length\n }\n}\n","/**\n * LockedJsonlAppender — mutex-serialized JSONL append helper for arbitrary\n * payloads. The reference-replay store does the same thing for typed\n * `ReferenceReplayRun` rows; this is the generic version used by\n * `MutationTelemetry`, `TrialTelemetry`, and any other consumer that wants\n * append-only durable telemetry without rolling its own lock.\n *\n * Locks are per absolute file path (process-local). Cross-process\n * concurrency is NOT addressed — that's an fcntl/flock problem.\n */\n\nimport { appendFileSync, existsSync, mkdirSync } from 'node:fs'\nimport { dirname } from 'node:path'\nimport { Mutex } from './concurrency'\n\nconst mutexes = new Map<string, Mutex>()\n\nfunction getMutex(path: string): Mutex {\n let m = mutexes.get(path)\n if (!m) {\n m = new Mutex()\n mutexes.set(path, m)\n }\n return m\n}\n\nexport class LockedJsonlAppender {\n private readonly mutex: Mutex\n constructor(public readonly path: string) {\n this.mutex = getMutex(path)\n if (!existsSync(dirname(path))) {\n mkdirSync(dirname(path), { recursive: true })\n }\n }\n\n async append(entry: unknown): Promise<void> {\n const line = `${JSON.stringify(entry)}\\n`\n await this.mutex.runExclusive(() => {\n appendFileSync(this.path, line)\n })\n }\n}\n\n/** Reset all internal mutex state — tests only. */\nexport function resetLockedAppendersForTesting(): void {\n mutexes.clear()\n}\n","/**\n * FindingsStore — durable persistence for AnalystFinding rows + a diff\n * helper so we can answer \"what changed since the last run?\" without\n * recomputing analysts.\n *\n * On-disk shape is JSONL: one finding per line, append-only, locked via\n * LockedJsonlAppender. Operators get crash-safety (no partial JSON),\n * cheap reads (sequential parse), and trivial backup (rsync the file).\n *\n * Reads are non-locking: a reader sees a consistent snapshot of all\n * fully-written lines and skips an incomplete trailing line if the\n * writer is mid-append. Cross-process locking is intentionally out of\n * scope (see locked-jsonl-appender.ts).\n *\n * The store is run-scoped: callers pass `runId` on append and on load,\n * which keeps multi-run files cleanly partitioned. The `diffFindings`\n * helper compares two run-id sets using stable `finding_id` semantics —\n * the diff is the cross-run signal the regression dashboard renders.\n */\n\nimport { existsSync, readFileSync } from 'node:fs'\n\nimport { LockedJsonlAppender } from '../locked-jsonl-appender'\nimport type { AnalystFinding } from './types'\n\n/**\n * One persisted row. We attach `run_id` on disk so a single file can\n * hold multiple runs and the diff helper can query without re-walking\n * separate files.\n */\nexport interface PersistedFinding extends AnalystFinding {\n run_id: string\n}\n\nexport class FindingsStore {\n private readonly appender: LockedJsonlAppender\n\n constructor(public readonly path: string) {\n this.appender = new LockedJsonlAppender(path)\n }\n\n async append(runId: string, findings: AnalystFinding[]): Promise<void> {\n for (const f of findings) {\n const row: PersistedFinding = { ...f, run_id: runId }\n await this.appender.append(row)\n }\n }\n\n /** Load every persisted finding. Discards malformed trailing lines silently. */\n loadAll(): PersistedFinding[] {\n if (!existsSync(this.path)) return []\n const raw = readFileSync(this.path, 'utf8')\n if (!raw) return []\n const out: PersistedFinding[] = []\n for (const line of raw.split('\\n')) {\n if (!line) continue\n try {\n out.push(JSON.parse(line) as PersistedFinding)\n } catch {\n // Skip torn trailing line — the lock guarantees no torn lines\n // mid-file, only at EOF when a writer is in-flight.\n }\n }\n return out\n }\n\n /** Filter to a single run. */\n loadRun(runId: string): PersistedFinding[] {\n return this.loadAll().filter((r) => r.run_id === runId)\n }\n}\n\n// ── Cross-run diff ──────────────────────────────────────────────────\n\nexport interface FindingsDiff {\n /** New finding ids in `current` that weren't in `previous`. */\n appeared: PersistedFinding[]\n /** Finding ids in `previous` that aren't in `current`. */\n disappeared: PersistedFinding[]\n /** Same finding id present in both runs and unchanged per the materiality test. */\n persisted: PersistedFinding[]\n /**\n * Same finding id in both runs but at least one non-identity field\n * shifted per `DiffPolicy.isMaterial`. Reported as [previous, current].\n */\n changed: Array<{ previous: PersistedFinding; current: PersistedFinding }>\n}\n\nexport interface DiffPolicy {\n /**\n * Predicate that decides whether two findings (same finding_id) count\n * as a material change. Defaults to {@link defaultIsMaterial}: severity\n * shift, confidence Δ > 0.05, or evidence count change. Compliance /\n * perf consumers MAY supply a stricter predicate (e.g. rationale text\n * diff, metric Δ thresholds).\n */\n isMaterial?: (previous: AnalystFinding, current: AnalystFinding) => boolean\n}\n\n/**\n * Default materiality test. Deliberately narrow so LLM-reword churn\n * doesn't flood the diff. Stricter tests are opt-in via DiffPolicy.\n */\nexport function defaultIsMaterial(a: AnalystFinding, b: AnalystFinding): boolean {\n if (a.severity !== b.severity) return true\n if (Math.abs((a.confidence ?? 0) - (b.confidence ?? 0)) > 0.05) return true\n if (a.evidence_refs.length !== b.evidence_refs.length) return true\n return false\n}\n\n/**\n * Diff two findings sets by stable finding_id. Callers typically load\n * the two run-id slices from the same store and pass them in.\n */\nexport function diffFindings(\n previous: PersistedFinding[],\n current: PersistedFinding[],\n policy: DiffPolicy = {},\n): FindingsDiff {\n const isMaterial = policy.isMaterial ?? defaultIsMaterial\n const prevById = new Map(previous.map((f) => [f.finding_id, f]))\n const curById = new Map(current.map((f) => [f.finding_id, f]))\n\n const appeared: PersistedFinding[] = []\n const disappeared: PersistedFinding[] = []\n const persisted: PersistedFinding[] = []\n const changed: FindingsDiff['changed'] = []\n\n for (const [id, cur] of curById) {\n const prev = prevById.get(id)\n if (!prev) {\n appeared.push(cur)\n continue\n }\n if (isMaterial(prev, cur)) {\n changed.push({ previous: prev, current: cur })\n } else {\n persisted.push(cur)\n }\n }\n for (const [id, prev] of prevById) {\n if (!curById.has(id)) disappeared.push(prev)\n }\n return { appeared, disappeared, persisted, changed }\n}\n","/**\n * Skill-usage analyst — a DETERMINISTIC `Analyst` over a Claude/Codex skill\n * library + its trace corpus. Unlike the trace-store kinds (failure-mode,\n * improvement, ...) this kind calls no LLM: it mines real usage and skill\n * structure and emits findings by rule.\n *\n * It exists because the naive \"Skill-tool invocation count\" lies low — it\n * misses orchestrated sub-dispatch (a leaf skill run BY /pursue or /governor\n * logs under the parent), slash-command entry, local-script bypass, and\n * on-disk artifacts. The 2026-05-30 skill audit found 39/53 skills at zero\n * direct invocations, yet only one was a genuine cut: the rest were\n * measurement-invisible or discovery-limited. This analyst encodes that\n * lesson as a multi-signal usage model so a cheap repeatable pass can keep\n * the library honest, and so the expensive audit workflow's verdicts can\n * GEPA-distill it toward agreement (see `gold/skill-verdicts.gold.jsonl`).\n *\n * Report-building (`buildSkillUsageReport`, an fs scan) is separated from\n * finding emission (`SkillUsageAnalyst.analyze`, pure) so the slow scan runs\n * once at the registry boundary and the rule logic stays unit-testable.\n */\n\nimport { type Dirent, existsSync, readdirSync, readFileSync, statSync } from 'node:fs'\nimport { join } from 'node:path'\nimport type { Analyst, AnalystContext, AnalystFinding, AnalystSeverity } from '../types'\nimport { computeFindingId } from '../types'\n\n// ── Input model ──────────────────────────────────────────────────────\n\nexport type SkillKind = 'public' | 'private'\n\n/** One skill's multi-signal usage + structure. All counts are deterministic. */\nexport interface SkillUsageRecord {\n name: string\n kind: SkillKind\n /** Absolute path to the skill's SKILL.md. */\n path: string\n lines: number\n /** `\"skill\":\"<name>\"` Skill-tool invocations across the trace corpus. */\n directInvocations: number\n /** `<command-name>/<name>` slash invocations across the trace corpus. */\n slashInvocations: number\n /** Sibling skills whose SKILL.md dispatches to this one (`/<name>`). Proxy\n * for orchestrated sub-dispatch the per-skill counter cannot see. */\n inboundRefs: number\n /** On-disk artifacts attributable to the skill (e.g. `.evolve/<name>/**`). */\n artifactCount: number\n /** Tangle-private reference count in the body (leak signal for public skills). */\n tanglePrivateRefs: number\n hasReferencesDir: boolean\n hasEvalsDir: boolean\n /** Body mentions `skill-runs.jsonl` (visible to /reflect + /governor). */\n logsRuns: boolean\n /** Description carries an explicit `Triggers:` clause / trigger phrases. */\n hasTriggerPhrases: boolean\n}\n\nexport interface SkillUsageReport {\n generatedFromTraces: number\n records: SkillUsageRecord[]\n}\n\nexport interface SkillUsageScanConfig {\n /** Dirs holding `*.jsonl` transcripts (Claude `~/.claude/projects`, Codex sessions). */\n transcriptDirs: string[]\n /** Skill roots to scan; each dir directly under `root` with a `SKILL.md` is a skill. */\n skillRoots: { root: string; kind: SkillKind }[]\n /** Roots scanned for `<root>/.evolve/<skill>` artifact dirs. */\n artifactRoots?: string[]\n /** Token-prefixed mappings: skill name → extra artifact subpaths under an artifactRoot\n * (e.g. reflect → `.evolve/reflections`). Catches non-eponymous artifact dirs. */\n artifactAliases?: Record<string, string[]>\n /** Cap files read per transcript dir (bounds a huge corpus); 0 = unbounded. */\n maxTranscriptsPerDir?: number\n}\n\n// ── Deterministic thresholds ─────────────────────────────────────────\n\n/** Anthropic's authoring guidance keeps SKILL.md short; past this with no\n * `references/` split the body burns context budget every session. */\nconst BLOAT_LINE_THRESHOLD = 300\n\nconst TANGLE_PRIVATE_RE =\n /\\b(cli-bridge|tangletools|ops-board|drew-gtr-pro|@tangle-network\\/|~\\/company|tangle\\.tools|gtm-agent)\\b|\\bkimi\\b|\\btcloud\\b/gi\nconst TRIGGER_RE = /triggers?\\s*[:-]/i\n\n// ── Report builder (fs scan — slow, runs once at the registry boundary) ──\n\nfunction listSkillDirs(root: string): { name: string; path: string }[] {\n if (!existsSync(root)) return []\n const out: { name: string; path: string }[] = []\n for (const entry of readdirSync(root, { withFileTypes: true })) {\n if (!entry.isDirectory() && !entry.isSymbolicLink()) continue\n const skillMd = join(root, entry.name, 'SKILL.md')\n if (existsSync(skillMd)) out.push({ name: entry.name, path: skillMd })\n }\n return out\n}\n\nfunction walkJsonl(dir: string, cap: number): string[] {\n if (!existsSync(dir)) return []\n const files: string[] = []\n const stack = [dir]\n while (stack.length) {\n const cur = stack.pop()!\n let entries: Dirent[]\n try {\n entries = readdirSync(cur, { withFileTypes: true })\n } catch {\n continue\n }\n for (const e of entries) {\n const full = join(cur, e.name)\n if (e.isDirectory()) stack.push(full)\n else if (e.name.endsWith('.jsonl')) {\n files.push(full)\n if (cap > 0 && files.length >= cap) return files\n }\n }\n }\n return files\n}\n\nfunction frontmatterDescription(body: string): string {\n const fm = /^---\\n([\\s\\S]*?)\\n---/.exec(body)\n const block = fm?.[1] ?? ''\n const m = /description:\\s*(.+)/i.exec(block)\n return m?.[1] ?? ''\n}\n\nfunction countArtifacts(roots: string[], name: string, aliases: string[]): number {\n let n = 0\n for (const root of roots) {\n const candidates = [join(root, '.evolve', name), ...aliases.map((a) => join(root, a))]\n for (const dir of candidates) {\n if (!existsSync(dir)) continue\n try {\n if (statSync(dir).isDirectory()) n += readdirSync(dir).length\n else n += 1\n } catch {\n /* unreadable — skip */\n }\n }\n }\n return n\n}\n\n/** Scan the corpus + skill roots into a {@link SkillUsageReport}. Deterministic. */\nexport function buildSkillUsageReport(config: SkillUsageScanConfig): SkillUsageReport {\n const skills = config.skillRoots.flatMap(({ root, kind }) =>\n listSkillDirs(root).map((s) => ({ ...s, kind })),\n )\n const names = skills.map((s) => s.name)\n\n // One pass over the corpus accumulating direct + slash counts per skill.\n const direct = new Map<string, number>(names.map((n) => [n, 0]))\n const slash = new Map<string, number>(names.map((n) => [n, 0]))\n const skillRe = /\"skill\"\\s*:\\s*\"([a-z0-9_:-]+)\"/g\n const cmdRe = /<command-name>\\/?([a-z0-9_:-]+)<\\/command-name>/g\n let transcripts = 0\n for (const dir of config.transcriptDirs) {\n for (const file of walkJsonl(dir, config.maxTranscriptsPerDir ?? 0)) {\n transcripts += 1\n let data: string\n try {\n data = readFileSync(file, 'utf8')\n } catch {\n continue\n }\n for (const m of data.matchAll(skillRe)) {\n const g = m[1]\n if (!g) continue\n const n = g.split(':').pop() ?? g\n const prev = direct.get(n)\n if (prev !== undefined) direct.set(n, prev + 1)\n }\n for (const m of data.matchAll(cmdRe)) {\n const g = m[1]\n if (g === undefined) continue\n const prev = slash.get(g)\n if (prev !== undefined) slash.set(g, prev + 1)\n }\n }\n }\n\n // Read each skill body once; compute structure + inbound refs across siblings.\n const bodies = new Map<string, string>()\n for (const s of skills) {\n try {\n bodies.set(s.name, readFileSync(s.path, 'utf8'))\n } catch {\n bodies.set(s.name, '')\n }\n }\n const inbound = new Map<string, number>(names.map((n) => [n, 0]))\n for (const target of names) {\n const ref = new RegExp(`/${target}\\\\b|\\\\[\\\\[${target}\\\\]\\\\]`)\n for (const s of skills) {\n if (s.name === target) continue\n if (ref.test(bodies.get(s.name) ?? '')) inbound.set(target, inbound.get(target)! + 1)\n }\n }\n\n const records: SkillUsageRecord[] = skills.map((s) => {\n const body = bodies.get(s.name) ?? ''\n const dir = s.path.replace(/\\/SKILL\\.md$/, '')\n return {\n name: s.name,\n kind: s.kind,\n path: s.path,\n lines: body ? body.split('\\n').length : 0,\n directInvocations: direct.get(s.name) ?? 0,\n slashInvocations: slash.get(s.name) ?? 0,\n inboundRefs: inbound.get(s.name) ?? 0,\n artifactCount: countArtifacts(\n config.artifactRoots ?? [],\n s.name,\n config.artifactAliases?.[s.name] ?? [],\n ),\n tanglePrivateRefs: (body.match(TANGLE_PRIVATE_RE) ?? []).length,\n hasReferencesDir: existsSync(join(dir, 'references')),\n hasEvalsDir: existsSync(join(dir, 'evals')),\n logsRuns: body.includes('skill-runs.jsonl'),\n hasTriggerPhrases: TRIGGER_RE.test(frontmatterDescription(body) || body.slice(0, 600)),\n }\n })\n return { generatedFromTraces: transcripts, records }\n}\n\n// ── Finding emission (pure — unit-testable, no LLM, no fs) ────────────\n\nconst ANALYST_ID = 'skill-usage'\n\nfunction finding(\n area: string,\n subject: string,\n claim: string,\n severity: AnalystSeverity,\n confidence: number,\n producedAt: string,\n recommended: string,\n evidenceUri: string,\n rationale?: string,\n): AnalystFinding {\n return {\n schema_version: '1.0.0',\n finding_id: computeFindingId({ analyst_id: ANALYST_ID, area, subject, claim }),\n analyst_id: ANALYST_ID,\n produced_at: producedAt,\n severity,\n area,\n claim,\n rationale,\n evidence_refs: [{ kind: 'artifact', uri: evidenceUri }],\n recommended_action: recommended,\n confidence,\n subject,\n }\n}\n\n/** Pure rule pass over a report → findings. Exported for direct/unit use. */\nexport function emitSkillUsageFindings(\n report: SkillUsageReport,\n producedAt: string,\n): AnalystFinding[] {\n const out: AnalystFinding[] = []\n for (const r of report.records) {\n const directTotal = r.directInvocations + r.slashInvocations\n const trueUsage = directTotal + r.inboundRefs + r.artifactCount\n\n // 1. Dead: no usage signal of ANY kind. The only real deprecation candidate.\n if (trueUsage === 0) {\n out.push(\n finding(\n 'skill-usage',\n r.name,\n `Skill '${r.name}' has zero usage across all signals (direct, slash, inbound-refs, artifacts)`,\n 'high',\n 0.6,\n producedAt,\n 'Confirm the skill covers a real recurring job; if not, deprecate. Zero true usage is the only deterministic deprecation candidate.',\n r.path,\n 'No Skill-tool call, no slash invocation, no sibling dispatches to it, and no on-disk artifacts.',\n ),\n )\n } else if (directTotal === 0 && r.inboundRefs + r.artifactCount > 0) {\n // 2. Measurement-invisible: real use via orchestration/artifacts, never invoked directly.\n out.push(\n finding(\n 'skill-usage',\n r.name,\n `Skill '${r.name}' shows 0 direct invocations but is used via orchestration/artifacts (inbound=${r.inboundRefs}, artifacts=${r.artifactCount})`,\n 'info',\n 0.8,\n producedAt,\n 'Do NOT treat as unused — usage is real but logged under parent skills or on disk. Strengthen direct-invocation discovery only if direct use is desired.',\n r.path,\n 'The Skill-tool counter undercounts orchestrated/chained leaf skills.',\n ),\n )\n }\n\n // 3. Discovery gap: low direct use AND weak trigger surface.\n if (directTotal <= 2 && !r.hasTriggerPhrases) {\n out.push(\n finding(\n 'discoverability',\n r.name,\n `Skill '${r.name}' is rarely invoked directly and its description has no explicit trigger phrases`,\n 'medium',\n 0.7,\n producedAt,\n 'Add a `Triggers:` clause with verbatim user phrases to the frontmatter description so the model auto-invokes it.',\n r.path,\n ),\n )\n }\n\n // 4. Public-repo leak.\n if (r.kind === 'public' && r.tanglePrivateRefs > 0) {\n out.push(\n finding(\n 'safety',\n r.name,\n `Public skill '${r.name}' carries ${r.tanglePrivateRefs} Tangle-private reference(s)`,\n 'high',\n 0.75,\n producedAt,\n 'Sanitize incidental internal refs (cli-bridge/kimi/tcloud/~company/private repos) or relocate to a private repo. Verify @tangle-network/* refs are to PUBLISHED packages before treating as a leak.',\n r.path,\n ),\n )\n }\n\n // 5. Bloat / no progressive disclosure.\n if (r.lines > BLOAT_LINE_THRESHOLD && !r.hasReferencesDir) {\n out.push(\n finding(\n 'maintainability',\n r.name,\n `Skill '${r.name}' is ${r.lines} lines with no references/ split (progressive disclosure)`,\n 'medium',\n 0.8,\n producedAt,\n `Split detail into references/ loaded on demand; keep SKILL.md a short overview. ${r.lines} lines load into every session's context budget.`,\n r.path,\n ),\n )\n }\n\n // 6. No evals (Anthropic's \">=3 evals before docs\" rule).\n if (!r.hasEvalsDir) {\n out.push(\n finding(\n 'data-quality',\n r.name,\n `Skill '${r.name}' ships no evals/`,\n 'low',\n 0.6,\n producedAt,\n 'Add evals/evals.json with >=3 scenarios proving the skill beats baseline; gives regression coverage.',\n r.path,\n ),\n )\n }\n\n // 7. No run logging → invisible to /reflect and /governor.\n if (!r.logsRuns) {\n out.push(\n finding(\n 'observability',\n r.name,\n `Skill '${r.name}' never appends to .evolve/skill-runs.jsonl`,\n 'low',\n 0.55,\n producedAt,\n 'Append one run line to .evolve/skill-runs.jsonl on completion, or declare it a non-logging leaf, so the self-improvement loop can see it ran.',\n r.path,\n ),\n )\n }\n }\n return out\n}\n\n// ── The Analyst ──────────────────────────────────────────────────────\n\nexport class SkillUsageAnalyst implements Analyst<SkillUsageReport> {\n readonly id = ANALYST_ID\n readonly description =\n 'Deterministic multi-signal skill-usage analysis: flags dead skills, measurement-invisible (orchestrated) usage, discovery gaps, public-repo leaks, bloat, missing evals, and missing run-logging.'\n readonly inputKind = 'custom' as const\n readonly cost = { kind: 'deterministic' as const, est_usd_per_run: 0 }\n readonly version = '1.0.0'\n\n async analyze(input: SkillUsageReport, ctx: AnalystContext): Promise<AnalystFinding[]> {\n const producedAt = ctx.tags?.producedAt ?? new Date().toISOString()\n ctx.log?.(\n `skill-usage: ${input.records.length} skills over ${input.generatedFromTraces} transcripts`,\n )\n return emitSkillUsageFindings(input, producedAt)\n }\n}\n\nexport const SKILL_USAGE_ANALYST = new SkillUsageAnalyst()\n","import { NotFoundError } from './errors'\nimport { aggregateRunScore, clamp01, type RunScore, type RunScoreWeights } from './run-score'\nimport type { Artifact, BudgetLedgerEntry, Run, Span, TraceEvent, TraceStore } from './trace'\n\nexport interface RunTrace {\n run: Run\n spans: Span[]\n events: TraceEvent[]\n artifacts: Artifact[]\n budget: BudgetLedgerEntry[]\n}\n\nexport interface RunCriticOptions {\n weights?: Partial<RunScoreWeights>\n driftPatterns?: RegExp[]\n}\n\nconst DEFAULT_DRIFT_PATTERNS = [\n /https?:\\/\\//i,\n /\\btitle:\\s/i,\n /\\bsummary:\\s/i,\n /\\burl:\\s/i,\n /\\bnpm package usage\\b/i,\n /\\bnews\\b/i,\n]\n\nexport class RunCritic {\n private readonly weights?: Partial<RunScoreWeights>\n private readonly driftPatterns: RegExp[]\n\n constructor(options: RunCriticOptions = {}) {\n this.weights = options.weights\n this.driftPatterns = options.driftPatterns ?? DEFAULT_DRIFT_PATTERNS\n }\n\n async score(store: TraceStore, runId: string): Promise<RunScore> {\n const run = await store.getRun(runId)\n if (!run) throw new NotFoundError(`run ${runId} not found`)\n const [spans, events, artifacts, budget] = await Promise.all([\n store.spans({ runId }),\n store.events({ runId }),\n store.artifacts(runId),\n store.budget(runId),\n ])\n return this.scoreTrace({ run, spans, events, artifacts, budget })\n }\n\n scoreTrace(trace: RunTrace): RunScore {\n const notes: string[] = []\n const llmSpans = trace.spans.filter(\n (s): s is Extract<Span, { kind: 'llm' }> => s.kind === 'llm',\n )\n const toolSpans = trace.spans.filter(\n (s): s is Extract<Span, { kind: 'tool' }> => s.kind === 'tool',\n )\n const judgeSpans = trace.spans.filter(\n (s): s is Extract<Span, { kind: 'judge' }> => s.kind === 'judge',\n )\n const sandboxSpans = trace.spans.filter(\n (s): s is Extract<Span, { kind: 'sandbox' }> => s.kind === 'sandbox',\n )\n const finalGateSpans = judgeSpans.filter(\n (span) => span.dimension === 'final_gate' || span.attributes?.finalGate === true,\n )\n\n const success =\n trace.run.outcome?.pass === true ? 1 : trace.run.status === 'completed' ? 0.5 : 0\n if (!success) notes.push('run did not complete with pass=true')\n\n const judgeAverage = judgeSpans.length\n ? judgeSpans.reduce((sum, span) => sum + normalizeJudgeScore(span.score), 0) /\n judgeSpans.length\n : undefined\n const outcomeScore =\n typeof trace.run.outcome?.score === 'number'\n ? clamp01(\n trace.run.outcome.score > 1 ? trace.run.outcome.score / 100 : trace.run.outcome.score,\n )\n : undefined\n const goalProgress = outcomeScore ?? judgeAverage ?? success\n\n const successfulTools = toolSpans.filter((span) => span.status !== 'error').length\n const toolUseQuality = toolSpans.length === 0 ? 0 : successfulTools / toolSpans.length\n if (toolSpans.length === 0) notes.push('no tool spans recorded')\n\n const patchEvidence =\n trace.artifacts.length +\n toolSpans.filter((span) => /write|edit|patch|apply/i.test(span.toolName)).length\n const patchQuality = patchEvidence > 0 ? clamp01(patchEvidence / 4) : 0\n if (!patchQuality) notes.push('no artifact or edit evidence recorded')\n\n const sandboxTests = sandboxSpans.filter(\n (span) => typeof span.testsTotal === 'number' && span.testsTotal > 0,\n )\n const testReality = sandboxTests.length\n ? sandboxTests.reduce(\n (sum, span) => sum + (span.testsPassed ?? 0) / Math.max(1, span.testsTotal ?? 1),\n 0,\n ) / sandboxTests.length\n : toolSpans.some((span) =>\n /\\btest|vitest|pytest|jest|build|tsc\\b/i.test(JSON.stringify(span.args)),\n )\n ? 0.4\n : 0\n if (!testReality) notes.push('no real test/build evidence recorded')\n\n const blockerSpans = judgeSpans.filter((span) => isBlockingJudge(span))\n const finalGateBlockers = finalGateSpans.filter((span) => isBlockingJudge(span))\n const finalGate = finalGateSpans.length ? (finalGateBlockers.length ? 0 : 1) : success\n if (finalGateBlockers.length)\n notes.push(`final gate blocked by ${finalGateBlockers.length} reviewer(s)`)\n else if (!finalGateSpans.length) notes.push('no final gate judgment recorded')\n\n const reviewerBlockers = judgeSpans.length ? blockerSpans.length / judgeSpans.length : 0\n if (reviewerBlockers) notes.push(`detected ${blockerSpans.length} blocking reviewer signal(s)`)\n\n const positiveGroundingSignals =\n patchEvidence +\n sandboxSpans.length +\n llmSpans.filter((span) => looksRepoGrounded(span.output ?? '')).length\n const driftSignals =\n llmSpans.filter((span) => this.isDrift(span.output ?? '')).length +\n trace.events.filter((event) => this.isDrift(JSON.stringify(event.payload))).length\n const repoGroundedness =\n positiveGroundingSignals + driftSignals === 0\n ? 0\n : positiveGroundingSignals / (positiveGroundingSignals + driftSignals)\n const driftPenalty =\n positiveGroundingSignals + driftSignals === 0\n ? 0\n : driftSignals / (positiveGroundingSignals + driftSignals)\n if (driftSignals > 0) notes.push(`detected ${driftSignals} drift signal(s)`)\n\n const costUsd = trace.budget.length\n ? Math.max(\n ...trace.budget\n .filter((entry: BudgetLedgerEntry) => entry.dimension === 'usd')\n .map((entry: BudgetLedgerEntry) => entry.consumed),\n 0,\n )\n : llmSpans.reduce((sum, span) => sum + (span.costUsd ?? 0), 0)\n const wallSeconds =\n trace.run.endedAt && trace.run.startedAt\n ? Math.max(0, (trace.run.endedAt - trace.run.startedAt) / 1000)\n : 0\n\n return {\n success,\n goalProgress,\n repoGroundedness,\n driftPenalty,\n toolUseQuality,\n patchQuality,\n testReality,\n finalGate,\n reviewerBlockers,\n costUsd,\n wallSeconds,\n notes,\n }\n }\n\n rank(score: RunScore): number {\n return aggregateRunScore(score, this.weights)\n }\n\n private isDrift(text: string): boolean {\n return this.driftPatterns.some((pattern) => pattern.test(text))\n }\n}\n\nfunction normalizeJudgeScore(score: number): number {\n return score > 1 ? clamp01(score / 10) : clamp01(score)\n}\n\nfunction looksRepoGrounded(text: string): boolean {\n return /(?:src\\/|tests?\\/|package\\.json|tsconfig|\\.ts\\b|\\.tsx\\b|git status|pnpm |npm |vitest|pytest|jest)/i.test(\n text,\n )\n}\n\nfunction isBlockingJudge(span: Extract<Span, { kind: 'judge' }>): boolean {\n return (\n span.attributes?.blocking === true ||\n span.attributes?.verdict === 'BLOCKING' ||\n positiveNumber(span.attributes?.blockingFindings) ||\n positiveNumber(span.attributes?.highFindings) ||\n span.score <= 2\n )\n}\n\nfunction positiveNumber(value: unknown): boolean {\n return typeof value === 'number' && value > 0\n}\n","/**\n * Semantic concept judge — \"does the built artifact actually implement\n * the features the user asked for?\"\n *\n * Distinct from the domain/code/coherence judges in `judges.ts`:\n * - those judges score free-form conversational agent outputs along\n * quality dimensions (accuracy, depth, etc.)\n * - this judge scores a *built artifact* (served HTML + source files)\n * against an explicit list of expected concepts, returning per-concept\n * {present, score 0-10, evidence, severity}.\n *\n * The judge is strict about distinguishing (a) a working implementation\n * from (b) a keyword-present stub. \"// TODO: mint button\" is NOT present.\n * Only real, functional, wired-up code counts.\n *\n * Use via {@link createSemanticConceptJudge} or directly via\n * {@link runSemanticConceptJudge}. Soft-fails (available=false) on LLM\n * or JSON-parse errors so the caller can treat that as \"layer skipped\"\n * rather than \"layer failed\" in a multi-layer pipeline.\n */\n\nimport { callLlmJson, type LlmClientOptions } from './llm-client'\nimport type { Severity } from './multi-layer-verifier'\n\n// ─── Types ──────────────────────────────────────────────────────────────\n\n/**\n * Implementation complexity class for weighted scoring.\n *\n * - `render` (default): the concept is a UI surface that displays static\n * data — render a list, show a counter, lay out a button. Single-file\n * work, no external integration.\n * - `integrate`: the concept requires wiring a real external system —\n * wallet connect (wagmi + RainbowKit + chain config), payment provider\n * (Stripe Elements + intent + webhook), an API client with auth.\n * Multi-file, library-knowledge, runtime correctness matters.\n * - `compute`: the concept requires algorithmic work — solver, simulator,\n * constraint propagation, ML inference. Correctness > UI polish.\n *\n * Default weights (when applied via `weightConcepts: 'complexity'`):\n * render=1.0, integrate=2.0, compute=2.5\n *\n * Cross-vertical scoring without complexity weighting silently inflates\n * the rate of UI-heavy verticals (healthcare, fintech dashboards) vs\n * integration-heavy verticals (DeFi, wallets) — all concepts treated\n * equally even though the agent does 2-3x the work for `integrate`.\n */\nexport type ConceptComplexity = 'render' | 'integrate' | 'compute'\n\nexport interface ConceptSpec {\n name: string\n /** Short hints that help the judge; not used for matching. */\n keywords?: string[]\n /** Optional explicit weight; default 1.0. Overrides complexity-derived weight. */\n weight?: number\n /** Implementation complexity class. Default `render`. */\n complexity?: ConceptComplexity\n}\n\nexport interface ConceptFinding {\n concept: string\n present: boolean\n /** 0..10. 10 = production-ready; 7 = functional thin; 4 = partial; 0 = absent. */\n score: number\n evidence: string\n severity: Severity\n}\n\nexport interface SemanticConceptJudgeInput {\n /** Full natural-language prompt the agent was handed. */\n userRequest: string\n /** Rendered HTML the preview returns (UI artifacts). Optional. */\n servedHtml?: string\n /** Top-level source files from the agent's workdir. */\n sourceFiles: Array<{ path: string; content: string }>\n /** The expected concept list. */\n expectedConcepts: ConceptSpec[]\n /** Free-form metadata (id, difficulty) to inject into the prompt. */\n artifactLabel?: string\n artifactDescription?: string\n}\n\nexport interface SemanticConceptJudgeResult {\n kind: 'semantic-concept'\n version: string\n /** Normalized 0..1 score — mean of per-concept scores / 10. */\n score: number\n presentCount: number\n totalCount: number\n findings: ConceptFinding[]\n summary: string\n durationMs: number\n costUsd: number | null\n /** False on LLM/JSON error — treat as \"skipped / unable to judge\" in pipelines. */\n available: boolean\n error?: string\n}\n\n/**\n * Score-aggregation strategy. `mean` averages 0-10 scores uniformly.\n * `complexity` applies the default weight table (render=1, integrate=2,\n * compute=2.5) unless a concept has an explicit `weight`. `explicit`\n * honors only `weight` (defaulting to 1 for unspecified).\n */\nexport type ConceptWeightStrategy = 'mean' | 'complexity' | 'explicit'\n\nexport const DEFAULT_COMPLEXITY_WEIGHTS: Record<ConceptComplexity, number> = {\n render: 1.0,\n integrate: 2.0,\n compute: 2.5,\n}\n\nexport interface SemanticConceptJudgeOptions {\n /** Model id to call. Default 'claude-sonnet-4-6' via agent-eval defaults. */\n model?: string\n /** Per-call timeout. Default 300s. */\n timeoutMs?: number\n /** Pipeline budget for the prompt (source blob truncation). Default 45000. */\n maxSourceChars?: number\n /** Per-file cap before inclusion. Default 20000. */\n maxPerFileChars?: number\n /** HTML cap. Default 30000. */\n maxHtmlChars?: number\n /** LlmClient config (baseUrl, apiKey, authHeader, …). */\n llm?: LlmClientOptions\n /**\n * Score aggregation strategy. Default `mean` — uniform average across\n * concepts. Cross-vertical comparisons should use `complexity` to\n * neutralize the integrate-vs-render asymmetry.\n */\n weightConcepts?: ConceptWeightStrategy\n /** Override the default complexity → weight table. */\n complexityWeights?: Partial<Record<ConceptComplexity, number>>\n}\n\n// ─── Prompt assembly ────────────────────────────────────────────────────\n\nexport const SEMANTIC_CONCEPT_JUDGE_VERSION = 'semantic-concept-judge-v1-2026-04-24'\n\nconst DEFAULT_MAX_SOURCE = 45_000\nconst DEFAULT_MAX_HTML = 30_000\nconst DEFAULT_MAX_PER_FILE = 20_000\nconst DEFAULT_TIMEOUT = 300_000\nconst DEFAULT_MODEL = 'claude-sonnet-4-6'\n\nconst SEMANTIC_SCHEMA = {\n type: 'object',\n additionalProperties: false,\n required: ['summary', 'concepts'],\n properties: {\n summary: { type: 'string', minLength: 20, maxLength: 600 },\n concepts: {\n type: 'array',\n minItems: 1,\n items: {\n type: 'object',\n additionalProperties: false,\n required: ['concept', 'present', 'score', 'evidence', 'severity'],\n properties: {\n concept: { type: 'string', minLength: 1, maxLength: 120 },\n present: { type: 'boolean' },\n score: { type: 'number', minimum: 0, maximum: 10 },\n evidence: { type: 'string', minLength: 5, maxLength: 400 },\n severity: { type: 'string', enum: ['critical', 'major', 'minor', 'info'] },\n },\n },\n },\n },\n}\n\nfunction truncate(body: string, cap: number, label: string): string {\n if (body.length <= cap) return body\n return `${body.slice(0, cap)}\\n… [truncated ${body.length - cap} chars of ${label}]`\n}\n\nfunction buildPrompt(\n input: SemanticConceptJudgeInput,\n opts: Required<SemanticConceptJudgeOptions>,\n): string {\n const sourceBlob = input.sourceFiles\n .filter((f) => f.content.length <= opts.maxPerFileChars)\n .map((f) => `--- FILE: ${f.path} ---\\n${f.content}`)\n .join('\\n\\n')\n\n const html = input.servedHtml ?? ''\n\n return `You are a strict code-review judge evaluating whether an agent's 0-to-1 build actually implements the features the user asked for.\n\nYou MUST distinguish:\n (a) WORKING code that implements the concept (rendered UI, wired handler, real API call),\n (b) KEYWORD-PRESENT stub (comments mentioning the concept, variable names, TODOs),\n (c) ABSENT (concept nowhere).\n\nA comment like \"// TODO: add mint button\" is NOT present — score 2-3. Only count a concept as present if there is real functional code: a rendered component, a call handler wired to state or a network call, a computed value actually used.\n\nUSER REQUEST (what the agent was asked to build):\n${input.userRequest}\n\n${input.artifactLabel ? `ARTIFACT METADATA:\\n name: ${input.artifactLabel}\\n description: ${input.artifactDescription ?? ''}\\n\\n` : ''}EXPECTED CONCEPTS (each must be graded independently):\n${input.expectedConcepts\n .map(\n (c, i) =>\n ` ${i + 1}. \"${c.name}\"${c.keywords?.length ? ` — hints: [${c.keywords.slice(0, 6).join(' | ')}]` : ''}`,\n )\n .join('\\n')}\n\n${html ? `SERVED HTML (what the preview returns when hit):\\n${truncate(html, opts.maxHtmlChars, 'HTML')}\\n\\n` : ''}SOURCE FILES (the agent's workdir):\n${truncate(sourceBlob, opts.maxSourceChars, 'source')}\n\nFor EACH concept, return:\n - concept: the concept name as given (match exactly)\n - present: boolean — does a working implementation exist?\n - score: 0-10 — 10 = production-ready; 7 = functional but thin; 4 = partial/stubbed; 2 = keyword-only comment; 0 = absent\n - evidence: cite \"<file>:<line>\" or \"served-html:<selector>\" pointing at the strongest supporting code. If the concept is absent or stubbed, explain what's missing.\n - severity:\n \"info\" when present: true AND score >= 7\n \"minor\" when present: true AND 4 <= score < 7\n \"major\" when present: false OR score < 4\n \"critical\" when the concept is not only absent but a core user flow depends on it\n\nAlso produce a \"summary\" (one sentence, 20-600 chars): overall verdict on whether this is a shippable implementation of the user request vs a keyword-dense placeholder.\n\nBE SKEPTICAL. Keyword matching already passed — your job is to catch what keyword matching misses. If the agent shipped a working build, say so. If it shipped a stub, say so. Don't grade on effort.\n\nReturn STRICT JSON. No prose outside the JSON.`\n}\n\n// ─── Runner ─────────────────────────────────────────────────────────────\n\n/**\n * Run the semantic concept judge. Soft-fails to available=false on\n * LLM/JSON errors — callers in a MultiLayerVerifier pipeline can treat\n * that as \"skip\" rather than \"fail.\"\n */\nexport async function runSemanticConceptJudge(\n input: SemanticConceptJudgeInput,\n options: SemanticConceptJudgeOptions = {},\n): Promise<SemanticConceptJudgeResult> {\n const start = Date.now()\n const totalCount = input.expectedConcepts.length\n\n if (totalCount === 0) {\n return {\n kind: 'semantic-concept',\n version: SEMANTIC_CONCEPT_JUDGE_VERSION,\n score: 0,\n presentCount: 0,\n totalCount: 0,\n findings: [],\n summary: 'no expected concepts declared',\n durationMs: 0,\n costUsd: null,\n available: false,\n error: 'no expected concepts declared',\n }\n }\n\n const opts: Required<SemanticConceptJudgeOptions> = {\n model: options.model ?? DEFAULT_MODEL,\n timeoutMs: options.timeoutMs ?? DEFAULT_TIMEOUT,\n maxSourceChars: options.maxSourceChars ?? DEFAULT_MAX_SOURCE,\n maxPerFileChars: options.maxPerFileChars ?? DEFAULT_MAX_PER_FILE,\n maxHtmlChars: options.maxHtmlChars ?? DEFAULT_MAX_HTML,\n llm: options.llm ?? {},\n weightConcepts: options.weightConcepts ?? 'mean',\n complexityWeights: { ...DEFAULT_COMPLEXITY_WEIGHTS, ...(options.complexityWeights ?? {}) },\n }\n\n // Build a name → weight map for aggregation. Mean strategy keeps every\n // weight at 1 (uniform average). Complexity strategy reads the table\n // and lets an explicit `weight` override. Explicit strategy uses ONLY\n // the spec's `weight` (defaulting to 1).\n const weightForConcept = (spec: ConceptSpec): number => {\n if (opts.weightConcepts === 'mean') return 1\n if (spec.weight != null) return spec.weight\n if (opts.weightConcepts === 'complexity') {\n return opts.complexityWeights[spec.complexity ?? 'render'] ?? 1\n }\n return 1\n }\n const weightByName = new Map<string, number>(\n input.expectedConcepts.map((c) => [c.name, weightForConcept(c)]),\n )\n\n try {\n const { value, result } = await callLlmJson<{\n summary: string\n concepts: ConceptFinding[]\n }>(\n {\n model: opts.model,\n messages: [\n {\n role: 'system',\n content:\n 'You are a strict code-review judge. Return strict JSON only. No prose outside the JSON. A keyword in a comment is NOT a working implementation.',\n },\n { role: 'user', content: buildPrompt(input, opts) },\n ],\n jsonSchema: { name: 'semantic_concept_judge', schema: SEMANTIC_SCHEMA },\n temperature: 0,\n timeoutMs: opts.timeoutMs,\n },\n opts.llm,\n )\n\n if (!value?.concepts || !Array.isArray(value.concepts)) {\n throw new Error('judge returned malformed response — expected array under \"concepts\"')\n }\n\n const findings: ConceptFinding[] = value.concepts.map((c) => ({\n concept: String(c.concept),\n present: Boolean(c.present),\n score: Math.max(0, Math.min(10, Number(c.score ?? 0))),\n evidence: String(c.evidence ?? ''),\n severity: (['critical', 'major', 'minor', 'info'] as const).includes(c.severity)\n ? c.severity\n : 'info',\n }))\n\n const presentCount = findings.filter((f) => f.present && f.score >= 7).length\n let weightSum = 0\n let weightedScoreSum = 0\n for (const f of findings) {\n const w = weightByName.get(f.concept) ?? 1\n weightSum += w\n weightedScoreSum += w * f.score\n }\n const scoreAvg =\n weightSum > 0\n ? weightedScoreSum / weightSum\n : findings.reduce((a, f) => a + f.score, 0) / Math.max(1, findings.length)\n\n return {\n kind: 'semantic-concept',\n version: SEMANTIC_CONCEPT_JUDGE_VERSION,\n score: Number((scoreAvg / 10).toFixed(3)),\n presentCount,\n totalCount,\n findings,\n summary: String(value.summary ?? ''),\n durationMs: Date.now() - start,\n costUsd: result.costUsd ?? null,\n available: true,\n }\n } catch (err) {\n return {\n kind: 'semantic-concept',\n version: SEMANTIC_CONCEPT_JUDGE_VERSION,\n score: 0,\n presentCount: 0,\n totalCount,\n findings: [],\n summary: '',\n durationMs: Date.now() - start,\n costUsd: null,\n available: false,\n error: err instanceof Error ? err.message : String(err),\n }\n }\n}\n\n/**\n * Factory: pin LLM options once, return a closure that accepts inputs.\n * Convenient for pipelines that want to share a single LlmClient config.\n */\nexport function createSemanticConceptJudge(\n options: SemanticConceptJudgeOptions = {},\n): (input: SemanticConceptJudgeInput) => Promise<SemanticConceptJudgeResult> {\n return (input) => runSemanticConceptJudge(input, options)\n}\n"],"mappings":"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","names":["existsSync","existsSync","existsSync","readFileSync","existsSync","readFileSync"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/analyst/steer-firewall.ts","../src/analyst/policy-edit.ts","../src/run-score.ts"],"sourcesContent":["// The realness-oracle firewall (docs/learning-flywheel.md, \"The steer is f(trace)\").\n//\n// A realness/authenticity signal has TWO legitimate roles that must stay\n// separated by a firewall:\n// (a) anchor judge J — write-only: scores the chosen output, gates promotion,\n// NEVER seen by the worker/optimizer mid-run (else the loop games it).\n// (b) steer f(trace) — an analyst observes the agent's OWN behavior in the\n// trace (\"imported a stub\", \"used a non-crypto PRNG where encryption was\n// required\") and steers the next attempt. Legitimate, because it is derived\n// from OBSERVABLE BEHAVIOR, not from J's held-out verdict.\n//\n// The correct discriminator is PROVENANCE, not evidence presence. A judge verdict\n// lifted into a finding (createJudgeAdapter → liftJudgeScore) is a verdict even\n// when it cites an artifact; an evidence-less trace-analyst bullet is an\n// observation even though it cites nothing. So the firewall keys on\n// `AnalystFinding.derived_from_judge` (set at the judge lift site), NOT on whether\n// evidence_refs is populated. The instant a verdict steers the next attempt it is\n// a back-channel for J and the loop Goodharts realness exactly as it would\n// Goodhart pass-rate.\n\nimport type { AnalystFinding, EvidenceRef } from './types'\n\n/** Evidence grounded in the agent's OWN execution: OTLP trace elements\n * (`span`/`event`) or the artifact it produced (`artifact`). */\nconst OBSERVABLE_KINDS: ReadonlySet<EvidenceRef['kind']> = new Set<EvidenceRef['kind']>([\n 'span',\n 'event',\n 'artifact',\n])\n\n/** DESCRIPTIVE predicate: does the finding cite at least one observable\n * (span/event/artifact) evidence ref. Useful for ranking evidence quality or\n * rendering — it is NOT the steer gate. Evidence presence is the WRONG\n * discriminator for steering: a legitimate trace-analyst observation may cite\n * nothing (it would be wrongly rejected), and a judge verdict may cite an\n * artifact (it would be wrongly admitted). Use `assertNoJudgeVerdict` to gate\n * steering; use this only where \"is this grounded in observable evidence\" is the\n * literal question. */\nexport function isTraceObservable(finding: AnalystFinding): boolean {\n return finding.evidence_refs.some((ref) => OBSERVABLE_KINDS.has(ref.kind))\n}\n\n/** True iff the finding is a JUDGE VERDICT (an acceptance score lifted into a\n * finding), identified by provenance set at the lift site — independent of\n * whatever evidence it cites. */\nexport function isJudgeVerdict(finding: AnalystFinding): boolean {\n return finding.derived_from_judge === true\n}\n\n/**\n * THE steer firewall. Fail-loud guard for any path that admits analyst findings\n * as STEERING input (the `f(trace)` role): rejects — naming the offenders — any\n * finding whose provenance is a judge verdict, rather than let `J` leak into the\n * loop. Returns the findings unchanged for chaining.\n *\n * Call this at the chokepoint where a detector that ALSO scores/gates has its\n * findings turned into a steer (the judge-and-steer dual-role case). It keys on\n * provenance, so it correctly admits evidence-less trace-analyst observations and\n * correctly rejects an artifact-citing judge verdict — the cases an evidence\n * check gets backwards.\n *\n * It is necessary, not sufficient: it stops PROVENANCE-tagged verdicts. A judge\n * whose output is laundered through a hand-built finding with no provenance flag\n * is out of its reach — provenance must be honestly set at every judge→finding\n * lift (today: createJudgeAdapter). That is why the integrity rule lives at the\n * lift site, and why ProposeContext.judgeScores?: never is the complementary\n * compile-time tripwire on the obvious direct channel.\n */\nexport function assertNoJudgeVerdict(\n findings: ReadonlyArray<AnalystFinding>,\n context = 'steer',\n): ReadonlyArray<AnalystFinding> {\n const leaks = findings.filter(isJudgeVerdict)\n if (leaks.length > 0) {\n throw new Error(\n `${context}: a judge verdict cannot be admitted as steering input — that is the ` +\n `held-out judge leaking into the loop. Offending judge-derived findings: [${leaks\n .map((f) => f.finding_id)\n .join(', ')}]. Steering consumes observations of behavior, never acceptance verdicts.`,\n )\n }\n return findings\n}\n","import { createHash } from 'node:crypto'\nimport type { AgentProfileCell, AgentProfileJson } from '../agent-profile-cell'\nimport { validateAgentProfileCell } from '../agent-profile-cell'\nimport { ValidationError } from '../errors'\nimport { canonicalize } from '../pre-registration'\nimport { type FindingSubject, parseFindingSubject } from './finding-subject'\nimport { assertNoJudgeVerdict } from './steer-firewall'\nimport type { AnalystFinding, EvidenceRef } from './types'\n\nexport type PolicyEditSchemaVersion = 'policy-edit/v1'\n\nexport const POLICY_EDIT_AXES = [\n 'carrier',\n 'representation',\n 'budget',\n 'sampling',\n 'output_contract',\n 'tool_contract',\n 'routing',\n 'memory',\n 'agent_profile',\n 'deployment_target',\n] as const\n\nexport type PolicyEditAxis = (typeof POLICY_EDIT_AXES)[number]\n\nexport const POLICY_EDIT_TARGET_SURFACES = [\n 'prompt',\n 'tool-contract',\n 'runtime-config',\n 'memory',\n 'agent-profile',\n 'code',\n 'deployment',\n] as const\n\nexport type PolicyEditTargetSurface = (typeof POLICY_EDIT_TARGET_SURFACES)[number]\nexport type PolicyEditRisk = 'low' | 'medium' | 'high' | 'unknown'\nexport type PolicyEditGainDirection = 'increase' | 'decrease'\nexport type PolicyEditGainUnit = 'absolute' | 'relative' | 'percent' | 'score'\n\nexport interface PolicyEditTarget {\n surface: PolicyEditTargetSurface\n /** Stable path inside the target surface, for example `system-prompt:tools`\n * or `budget.maxTurns`. */\n path?: string\n /** Optional canonical deployment identity. Store the existing cell, not a\n * local profile shape. */\n agentProfileCell?: AgentProfileCell\n /** Human label when the path is not enough for a readable audit trail. */\n label?: string\n}\n\nexport type PolicyEditChange =\n | {\n kind: 'text'\n mode: 'append' | 'prepend' | 'replace'\n value: string\n /** Required when `mode === 'replace'`; exact match only. */\n find?: string\n }\n | {\n kind: 'json'\n mode: 'set' | 'merge' | 'remove'\n path: string\n value?: AgentProfileJson\n }\n\nexport interface PolicyEditExpectedGain {\n /** Metric this edit is expected to move, e.g. `holdout.composite`. */\n metric: string\n direction: PolicyEditGainDirection\n /** Positive magnitude in the metric's native units. */\n amount: number\n unit?: PolicyEditGainUnit\n rationale?: string\n}\n\nexport interface PolicyEditSource {\n findingIds: string[]\n analystIds: string[]\n evidenceRefs: EvidenceRef[]\n /** Mirrors `AnalystFinding.derived_from_judge`; admission rejects it. */\n derivedFromJudge?: boolean\n}\n\nexport interface PolicyEdit {\n schemaVersion: PolicyEditSchemaVersion\n editId: string\n axis: PolicyEditAxis\n target: PolicyEditTarget\n change: PolicyEditChange\n claim: string\n expectedGain: PolicyEditExpectedGain\n confidence: number\n risk: PolicyEditRisk\n source: PolicyEditSource\n rationale?: string\n validationPlan?: string\n metadata?: Record<string, unknown>\n}\n\nexport type PolicyEditInit = Omit<PolicyEdit, 'schemaVersion' | 'editId'> & {\n schemaVersion?: PolicyEditSchemaVersion\n editId?: string\n}\n\nexport class PolicyEditValidationError extends ValidationError {\n readonly path: string\n constructor(message: string, path = '') {\n super(path ? `${message} (at ${path})` : message)\n this.path = path\n }\n}\n\nexport interface FindingToPolicyEditOptions {\n expectedGain?:\n | PolicyEditExpectedGain\n | ((finding: AnalystFinding) => PolicyEditExpectedGain | null | undefined)\n risk?: PolicyEditRisk | ((finding: AnalystFinding) => PolicyEditRisk)\n defaultAxis?: PolicyEditAxis\n defaultTargetSurface?: PolicyEditTargetSurface\n}\n\nexport interface PolicyEditAdmissionOptions {\n minScore?: number\n minExpectedGain?: number\n allowHighRisk?: boolean\n requireEvidence?: boolean\n}\n\nexport interface PolicyEditAdmission {\n edit: PolicyEdit\n decision: 'admit' | 'reject'\n score: number\n reasons: string[]\n}\n\nconst DEFAULT_MIN_SCORE = 0.7\nconst DEFAULT_MIN_EXPECTED_GAIN = 0.01\nconst POLICY_EDIT_ID = /^policy-edit:sha256:[0-9a-f]{64}$/\n\nexport function makePolicyEdit(init: PolicyEditInit): PolicyEdit {\n const normalized = normalizePolicyEdit({\n schemaVersion: 'policy-edit/v1',\n ...init,\n source: normalizeSource(init.source),\n })\n const edit = {\n ...normalized,\n editId: init.editId ?? computePolicyEditId(normalized),\n }\n return validatePolicyEdit(edit)\n}\n\nexport function computePolicyEditId(edit: Omit<PolicyEdit, 'editId'> | PolicyEdit): string {\n const { editId: _editId, schemaVersion, ...material } = edit as PolicyEdit\n void _editId\n const canonical = JSON.stringify(canonicalize({ schemaVersion, ...material }))\n return `policy-edit:sha256:${createHash('sha256').update(canonical).digest('hex')}`\n}\n\nexport function validatePolicyEdit(input: unknown): PolicyEdit {\n if (input === null || typeof input !== 'object') {\n throw new PolicyEditValidationError('expected object')\n }\n const obj = input as PolicyEdit\n expectLiteral(obj.schemaVersion, 'policy-edit/v1', 'schemaVersion')\n expectString(obj.editId, 'editId')\n if (!POLICY_EDIT_ID.test(obj.editId)) {\n throw new PolicyEditValidationError(\n 'editId must match policy-edit:sha256:<64 lowercase hex chars>',\n 'editId',\n )\n }\n expectOneOf(obj.axis, POLICY_EDIT_AXES, 'axis')\n validateTarget(obj.target)\n validateChange(obj.change)\n expectString(obj.claim, 'claim')\n validateExpectedGain(obj.expectedGain)\n expectConfidence(obj.confidence, 'confidence')\n expectOneOf(obj.risk, ['low', 'medium', 'high', 'unknown'] as const, 'risk')\n validateSource(obj.source)\n if (obj.rationale !== undefined) expectString(obj.rationale, 'rationale')\n if (obj.validationPlan !== undefined) expectString(obj.validationPlan, 'validationPlan')\n if (obj.metadata !== undefined && (obj.metadata === null || typeof obj.metadata !== 'object')) {\n throw new PolicyEditValidationError('expected object', 'metadata')\n }\n const expectedId = computePolicyEditId(obj)\n if (obj.editId !== expectedId) {\n throw new PolicyEditValidationError('editId does not match policy edit content', 'editId')\n }\n return obj\n}\n\nexport function isPolicyEdit(input: unknown): input is PolicyEdit {\n try {\n validatePolicyEdit(input)\n return true\n } catch {\n return false\n }\n}\n\nexport function policyEditsFromFindings(\n findings: ReadonlyArray<AnalystFinding>,\n opts: FindingToPolicyEditOptions = {},\n): PolicyEdit[] {\n assertNoJudgeVerdict(findings, 'policyEditsFromFindings')\n const edits: PolicyEdit[] = []\n for (const finding of findings) {\n const edit = policyEditFromFinding(finding, opts)\n if (edit) edits.push(edit)\n }\n return edits\n}\n\nexport function policyEditFromFinding(\n finding: AnalystFinding,\n opts: FindingToPolicyEditOptions = {},\n): PolicyEdit | null {\n assertNoJudgeVerdict([finding], 'policyEditFromFinding')\n if (!finding.recommended_action?.trim()) return null\n\n const expectedGain = resolveExpectedGain(finding, opts)\n if (!expectedGain) return null\n\n const routed = routeFindingSubject(finding.subject, opts)\n const risk = resolveRisk(finding, opts)\n return makePolicyEdit({\n axis: routed.axis,\n target: routed.target,\n change: { kind: 'text', mode: 'append', value: finding.recommended_action.trim() },\n claim: finding.claim,\n rationale: finding.rationale,\n expectedGain,\n confidence: finding.confidence,\n risk,\n validationPlan: finding.validation_plan,\n source: {\n findingIds: [finding.finding_id],\n analystIds: [finding.analyst_id],\n evidenceRefs: finding.evidence_refs,\n derivedFromJudge: finding.derived_from_judge,\n },\n })\n}\n\nexport function scorePolicyEditReadiness(\n edit: PolicyEdit,\n opts: PolicyEditAdmissionOptions = {},\n): number {\n validatePolicyEdit(edit)\n const minExpectedGain = opts.minExpectedGain ?? DEFAULT_MIN_EXPECTED_GAIN\n const evidenceScore = Math.min(1, edit.source.evidenceRefs.length / 2)\n const confidenceScore = clamp01(edit.confidence)\n const gainScore = clamp01(\n Math.abs(edit.expectedGain.amount) / Math.max(minExpectedGain * 5, 0.001),\n )\n const targetScore = targetSpecificityScore(edit)\n const riskPenalty =\n edit.risk === 'high' && opts.allowHighRisk !== true ? 0.35 : edit.risk === 'unknown' ? 0.2 : 0\n\n return clamp01(\n 0.3 * evidenceScore +\n 0.25 * confidenceScore +\n 0.25 * gainScore +\n 0.2 * targetScore -\n riskPenalty,\n )\n}\n\nexport function admitPolicyEdit(\n edit: PolicyEdit,\n opts: PolicyEditAdmissionOptions = {},\n): PolicyEditAdmission {\n const validated = validatePolicyEdit(edit)\n const score = scorePolicyEditReadiness(validated, opts)\n const reasons: string[] = []\n const minExpectedGain = opts.minExpectedGain ?? DEFAULT_MIN_EXPECTED_GAIN\n const requireEvidence = opts.requireEvidence ?? true\n\n if (validated.source.derivedFromJudge) {\n reasons.push('source is judge-derived; judge verdicts cannot steer policy edits')\n }\n if (requireEvidence && validated.source.evidenceRefs.length === 0) {\n reasons.push('missing evidence refs')\n }\n if (Math.abs(validated.expectedGain.amount) < minExpectedGain) {\n reasons.push(`expected gain below ${minExpectedGain}`)\n }\n if (validated.risk === 'high' && opts.allowHighRisk !== true) {\n reasons.push('high-risk edit requires explicit allowHighRisk')\n }\n if (score < (opts.minScore ?? DEFAULT_MIN_SCORE)) {\n reasons.push(\n `readiness score ${score.toFixed(3)} below ${(opts.minScore ?? DEFAULT_MIN_SCORE).toFixed(3)}`,\n )\n }\n\n return {\n edit: validated,\n decision: reasons.length === 0 ? 'admit' : 'reject',\n score,\n reasons,\n }\n}\n\nexport function applyPolicyEditToSurface(surface: unknown, edit: PolicyEdit): unknown {\n const validated = validatePolicyEdit(edit)\n if (validated.change.kind === 'text') return applyTextChange(surface, validated.change)\n return applyJsonChange(surface, validated.change)\n}\n\nfunction routeFindingSubject(\n subject: string | undefined,\n opts: FindingToPolicyEditOptions,\n): { axis: PolicyEditAxis; target: PolicyEditTarget } {\n const parsed = parseFindingSubject(subject)\n if (!parsed) {\n return {\n axis: opts.defaultAxis ?? 'representation',\n target: { surface: opts.defaultTargetSurface ?? 'prompt' },\n }\n }\n return routeParsedSubject(parsed)\n}\n\nfunction routeParsedSubject(subject: FindingSubject): {\n axis: PolicyEditAxis\n target: PolicyEditTarget\n} {\n switch (subject.kind) {\n case 'system-prompt':\n return {\n axis: 'representation',\n target: { surface: 'prompt', path: `system-prompt:${subject.section}` },\n }\n case 'skill':\n return {\n axis: 'agent_profile',\n target: { surface: 'agent-profile', path: `skill:${subject.name}` },\n }\n case 'tool-doc':\n return {\n axis: 'tool_contract',\n target: {\n surface: 'tool-contract',\n path: subject.aspect\n ? `tool-doc:${subject.tool}:${subject.aspect}`\n : `tool-doc:${subject.tool}`,\n },\n }\n case 'new-tool':\n return {\n axis: 'tool_contract',\n target: { surface: 'tool-contract', path: `new-tool:${subject.name}` },\n }\n case 'mcp':\n return {\n axis: 'tool_contract',\n target: {\n surface: 'agent-profile',\n path: subject.tool ? `mcp:${subject.server}:${subject.tool}` : `mcp:${subject.server}`,\n },\n }\n case 'hook':\n return {\n axis: 'agent_profile',\n target: { surface: 'agent-profile', path: `hook:${subject.name}` },\n }\n case 'subagent':\n return {\n axis: 'routing',\n target: { surface: 'agent-profile', path: `subagent:${subject.name}` },\n }\n case 'workflow':\n return {\n axis: 'routing',\n target: { surface: 'runtime-config', path: `workflow:${subject.name}` },\n }\n case 'rollout-policy':\n return {\n axis: rolloutPolicyAxis(subject.field),\n target: { surface: 'runtime-config', path: `rollout-policy:${subject.field}` },\n }\n case 'agent-profile':\n return {\n axis: 'agent_profile',\n target: { surface: 'agent-profile', path: `agent-profile:${subject.field}` },\n }\n case 'code':\n return {\n axis: 'representation',\n target: { surface: 'code', path: `code:${subject.path}` },\n }\n case 'rag':\n return {\n axis: 'memory',\n target: { surface: 'memory', path: `rag:${subject.corpus}:${subject.docId}` },\n }\n case 'memory':\n return { axis: 'memory', target: { surface: 'memory', path: `memory:${subject.key}` } }\n case 'scaffolding':\n return {\n axis: 'routing',\n target: { surface: 'runtime-config', path: `scaffolding:${subject.concern}` },\n }\n case 'output-schema':\n return {\n axis: 'output_contract',\n target: { surface: 'runtime-config', path: `output-schema:${subject.field}` },\n }\n case 'knowledge.wiki':\n return {\n axis: 'memory',\n target: {\n surface: 'memory',\n path: `agent-knowledge:wiki:${subject.slug}${subject.heading ? `#${subject.heading}` : ''}`,\n },\n }\n case 'knowledge.claim':\n return {\n axis: 'memory',\n target: { surface: 'memory', path: `agent-knowledge:claim:${subject.topic}` },\n }\n case 'knowledge.raw':\n return {\n axis: 'memory',\n target: { surface: 'memory', path: `agent-knowledge:raw:${subject.sourceId}` },\n }\n case 'knowledge.stale':\n return {\n axis: 'memory',\n target: { surface: 'memory', path: `agent-knowledge:stale:${subject.slug}` },\n }\n case 'websearch.outdated':\n return {\n axis: 'memory',\n target: { surface: 'memory', path: `websearch:outdated:${subject.topic}` },\n }\n case 'prior-run-summary':\n return {\n axis: 'memory',\n target: { surface: 'memory', path: `prior-run-summary:${subject.topic}` },\n }\n case 'cluster':\n return { axis: 'representation', target: { surface: 'prompt', path: subject.label } }\n }\n}\n\nfunction rolloutPolicyAxis(field: string): PolicyEditAxis {\n const normalized = field.toLowerCase()\n if (/budget|max(?:imum)?[-_. ]?(?:turns?|tokens?|cost)|timeout|deadline/.test(normalized)) {\n return 'budget'\n }\n if (/temperature|top[-_. ]?p|sampling|seed|shots?|parallel|concurrency/.test(normalized)) {\n return 'sampling'\n }\n if (/output|schema|format/.test(normalized)) return 'output_contract'\n return 'routing'\n}\n\nfunction resolveExpectedGain(\n finding: AnalystFinding,\n opts: FindingToPolicyEditOptions,\n): PolicyEditExpectedGain | null {\n if (typeof opts.expectedGain === 'function') return opts.expectedGain(finding) ?? null\n if (opts.expectedGain) return opts.expectedGain\n return readExpectedGainFromMetadata(finding.metadata)\n}\n\nfunction readExpectedGainFromMetadata(\n metadata: Record<string, unknown> | undefined,\n): PolicyEditExpectedGain | null {\n const raw =\n readPolicyEditMetadata(metadata)?.expectedGain ??\n readPolicyEditMetadata(metadata)?.expected_gain\n if (!raw || typeof raw !== 'object') return null\n const obj = raw as Record<string, unknown>\n if (\n typeof obj.metric !== 'string' ||\n (obj.direction !== 'increase' && obj.direction !== 'decrease') ||\n typeof obj.amount !== 'number'\n ) {\n return null\n }\n const out: PolicyEditExpectedGain = {\n metric: obj.metric,\n direction: obj.direction,\n amount: obj.amount,\n }\n if (\n obj.unit === 'absolute' ||\n obj.unit === 'relative' ||\n obj.unit === 'percent' ||\n obj.unit === 'score'\n ) {\n out.unit = obj.unit\n }\n if (typeof obj.rationale === 'string') out.rationale = obj.rationale\n return out\n}\n\nfunction readPolicyEditMetadata(\n metadata: Record<string, unknown> | undefined,\n): Record<string, unknown> | null {\n const raw = metadata?.policyEdit ?? metadata?.policy_edit\n return raw && typeof raw === 'object' ? (raw as Record<string, unknown>) : null\n}\n\nfunction resolveRisk(finding: AnalystFinding, opts: FindingToPolicyEditOptions): PolicyEditRisk {\n if (typeof opts.risk === 'function') return opts.risk(finding)\n if (opts.risk) return opts.risk\n const raw = readPolicyEditMetadata(finding.metadata)?.risk\n if (raw === 'low' || raw === 'medium' || raw === 'high' || raw === 'unknown') return raw\n if (finding.severity === 'critical' || finding.severity === 'high') return 'medium'\n return 'low'\n}\n\nfunction applyTextChange(\n surface: unknown,\n change: Extract<PolicyEditChange, { kind: 'text' }>,\n): string {\n if (typeof surface !== 'string') {\n throw new PolicyEditValidationError('text policy edits require a string surface', 'change')\n }\n if (change.mode === 'append') {\n if (hasExactTextBlock(surface, change.value)) return surface\n return `${surface.trimEnd()}\\n\\n${change.value}`.trimStart()\n }\n if (change.mode === 'prepend') {\n if (hasExactTextBlock(surface, change.value)) return surface\n return `${change.value}\\n\\n${surface.trimStart()}`.trimEnd()\n }\n const find = expectNonEmpty(change.find, 'change.find')\n if (!surface.includes(find)) {\n throw new PolicyEditValidationError('replace target not found in surface', 'change.find')\n }\n return surface.replace(find, change.value)\n}\n\nfunction applyJsonChange(\n surface: unknown,\n change: Extract<PolicyEditChange, { kind: 'json' }>,\n): AgentProfileJson {\n const root = parseJsonSurface(surface)\n const path = splitPath(change.path)\n if (change.mode === 'remove') return setJsonAtPath(root, path, undefined, 'remove')\n if (change.mode === 'set') return setJsonAtPath(root, path, change.value ?? null, 'set')\n const prior = readJsonAtPath(root, path)\n const merged =\n prior &&\n typeof prior === 'object' &&\n !Array.isArray(prior) &&\n change.value &&\n typeof change.value === 'object' &&\n !Array.isArray(change.value)\n ? { ...prior, ...change.value }\n : (change.value ?? null)\n return setJsonAtPath(root, path, merged as AgentProfileJson, 'set')\n}\n\nfunction parseJsonSurface(surface: unknown): AgentProfileJson {\n if (typeof surface === 'string') {\n try {\n return JSON.parse(surface) as AgentProfileJson\n } catch {\n throw new PolicyEditValidationError(\n 'json policy edits require a JSON string surface',\n 'change',\n )\n }\n }\n assertJson(surface, 'surface')\n return surface as AgentProfileJson\n}\n\nfunction readJsonAtPath(root: AgentProfileJson, path: string[]): AgentProfileJson | undefined {\n let cursor: AgentProfileJson | undefined = root\n for (const part of path) {\n if (!cursor || typeof cursor !== 'object' || Array.isArray(cursor)) return undefined\n cursor = cursor[part]\n }\n return cursor\n}\n\nfunction setJsonAtPath(\n root: AgentProfileJson,\n path: string[],\n value: AgentProfileJson | undefined,\n mode: 'set' | 'remove',\n): AgentProfileJson {\n if (path.length === 0) {\n if (mode === 'remove') return null\n return value ?? null\n }\n if (root === null || typeof root !== 'object' || Array.isArray(root)) {\n throw new PolicyEditValidationError('json edit root must be an object', 'change.path')\n }\n const out: Record<string, AgentProfileJson> = { ...root }\n let cursor: Record<string, AgentProfileJson> = out\n for (let i = 0; i < path.length - 1; i++) {\n const key = path[i]!\n const existing = cursor[key]\n if (\n mode === 'remove' &&\n (!existing || typeof existing !== 'object' || Array.isArray(existing))\n ) {\n return out\n }\n const next =\n existing && typeof existing === 'object' && !Array.isArray(existing) ? { ...existing } : {}\n cursor[key] = next\n cursor = next\n }\n const leaf = path[path.length - 1]!\n if (mode === 'remove') delete cursor[leaf]\n else cursor[leaf] = value ?? null\n return out\n}\n\nfunction normalizePolicyEdit(input: Omit<PolicyEdit, 'editId'>): Omit<PolicyEdit, 'editId'> {\n const out: Omit<PolicyEdit, 'editId'> = {\n schemaVersion: 'policy-edit/v1',\n axis: input.axis,\n target: normalizeTarget(input.target),\n change: normalizeChange(input.change),\n claim: input.claim.trim(),\n expectedGain: normalizeExpectedGain(input.expectedGain),\n confidence: input.confidence,\n risk: input.risk,\n source: normalizeSource(input.source),\n }\n if (input.rationale?.trim()) out.rationale = input.rationale.trim()\n if (input.validationPlan?.trim()) out.validationPlan = input.validationPlan.trim()\n if (input.metadata) out.metadata = input.metadata\n return out\n}\n\nfunction normalizeTarget(target: PolicyEditTarget): PolicyEditTarget {\n const out: PolicyEditTarget = { surface: target.surface }\n if (target.path?.trim()) out.path = target.path.trim()\n if (target.agentProfileCell)\n out.agentProfileCell = validateAgentProfileCell(target.agentProfileCell)\n if (target.label?.trim()) out.label = target.label.trim()\n return out\n}\n\nfunction normalizeChange(change: PolicyEditChange): PolicyEditChange {\n if (change.kind === 'text') {\n const out: Extract<PolicyEditChange, { kind: 'text' }> = {\n kind: 'text',\n mode: change.mode,\n value: change.value.trim(),\n }\n if (change.find?.trim()) out.find = change.find.trim()\n return out\n }\n const out: Extract<PolicyEditChange, { kind: 'json' }> = {\n kind: 'json',\n mode: change.mode,\n path: change.path.trim(),\n }\n if (change.value !== undefined) out.value = change.value\n return out\n}\n\nfunction normalizeExpectedGain(gain: PolicyEditExpectedGain): PolicyEditExpectedGain {\n const out: PolicyEditExpectedGain = {\n metric: gain.metric.trim(),\n direction: gain.direction,\n amount: gain.amount,\n }\n if (gain.unit) out.unit = gain.unit\n if (gain.rationale?.trim()) out.rationale = gain.rationale.trim()\n return out\n}\n\nfunction normalizeSource(source: PolicyEditSource): PolicyEditSource {\n const out: PolicyEditSource = {\n findingIds: uniqueSorted(source.findingIds.map((s) => s.trim()).filter(Boolean)),\n analystIds: uniqueSorted(source.analystIds.map((s) => s.trim()).filter(Boolean)),\n evidenceRefs: source.evidenceRefs,\n }\n if (source.derivedFromJudge) out.derivedFromJudge = true\n return out\n}\n\nfunction validateTarget(target: unknown): asserts target is PolicyEditTarget {\n if (!target || typeof target !== 'object')\n throw new PolicyEditValidationError('expected object', 'target')\n const obj = target as PolicyEditTarget\n expectOneOf(obj.surface, POLICY_EDIT_TARGET_SURFACES, 'target.surface')\n if (obj.path !== undefined) expectString(obj.path, 'target.path')\n if (obj.label !== undefined) expectString(obj.label, 'target.label')\n if (obj.agentProfileCell !== undefined) validateAgentProfileCell(obj.agentProfileCell)\n}\n\nfunction validateChange(change: unknown): asserts change is PolicyEditChange {\n if (!change || typeof change !== 'object')\n throw new PolicyEditValidationError('expected object', 'change')\n const obj = change as PolicyEditChange\n if (obj.kind !== 'text' && obj.kind !== 'json') {\n throw new PolicyEditValidationError('kind must be text or json', 'change.kind')\n }\n if (obj.kind === 'text') {\n expectOneOf(obj.mode, ['append', 'prepend', 'replace'] as const, 'change.mode')\n expectString(obj.value, 'change.value')\n if (obj.mode === 'replace') expectString(obj.find, 'change.find')\n return\n }\n expectOneOf(obj.mode, ['set', 'merge', 'remove'] as const, 'change.mode')\n expectString(obj.path, 'change.path')\n if (obj.value !== undefined) assertJson(obj.value, 'change.value')\n}\n\nfunction validateExpectedGain(gain: unknown): asserts gain is PolicyEditExpectedGain {\n if (!gain || typeof gain !== 'object')\n throw new PolicyEditValidationError('expected object', 'expectedGain')\n const obj = gain as PolicyEditExpectedGain\n expectString(obj.metric, 'expectedGain.metric')\n expectOneOf(obj.direction, ['increase', 'decrease'] as const, 'expectedGain.direction')\n if (!Number.isFinite(obj.amount) || obj.amount <= 0) {\n throw new PolicyEditValidationError(\n 'amount must be a positive finite number',\n 'expectedGain.amount',\n )\n }\n if (obj.unit !== undefined) {\n expectOneOf(\n obj.unit,\n ['absolute', 'relative', 'percent', 'score'] as const,\n 'expectedGain.unit',\n )\n }\n if (obj.rationale !== undefined) expectString(obj.rationale, 'expectedGain.rationale')\n}\n\nfunction validateSource(source: unknown): asserts source is PolicyEditSource {\n if (!source || typeof source !== 'object')\n throw new PolicyEditValidationError('expected object', 'source')\n const obj = source as PolicyEditSource\n expectNonEmptyStringArray(obj.findingIds, 'source.findingIds')\n expectNonEmptyStringArray(obj.analystIds, 'source.analystIds')\n if (!Array.isArray(obj.evidenceRefs)) {\n throw new PolicyEditValidationError('expected array', 'source.evidenceRefs')\n }\n for (const [i, ref] of obj.evidenceRefs.entries())\n validateEvidenceRef(ref, `source.evidenceRefs.${i}`)\n if (obj.derivedFromJudge !== undefined && typeof obj.derivedFromJudge !== 'boolean') {\n throw new PolicyEditValidationError('expected boolean', 'source.derivedFromJudge')\n }\n}\n\nfunction validateEvidenceRef(ref: unknown, path: string): asserts ref is EvidenceRef {\n if (!ref || typeof ref !== 'object') throw new PolicyEditValidationError('expected object', path)\n const obj = ref as EvidenceRef\n expectOneOf(obj.kind, ['span', 'event', 'artifact', 'finding', 'metric'] as const, `${path}.kind`)\n expectString(obj.uri, `${path}.uri`)\n if (obj.excerpt !== undefined) expectString(obj.excerpt, `${path}.excerpt`)\n}\n\nfunction assertJson(value: unknown, path: string): asserts value is AgentProfileJson {\n if (\n value === null ||\n typeof value === 'string' ||\n typeof value === 'boolean' ||\n (typeof value === 'number' && Number.isFinite(value))\n ) {\n return\n }\n if (Array.isArray(value)) {\n for (const [i, item] of value.entries()) assertJson(item, `${path}.${i}`)\n return\n }\n if (typeof value === 'object') {\n for (const [key, item] of Object.entries(value)) {\n if (!key) throw new PolicyEditValidationError('empty object key', path)\n assertJson(item, `${path}.${key}`)\n }\n return\n }\n throw new PolicyEditValidationError('expected JSON-compatible value', path)\n}\n\nfunction targetSpecificityScore(edit: PolicyEdit): number {\n let score = 0.4\n if (edit.target.path) score += 0.25\n if (edit.target.agentProfileCell) score += 0.15\n if (edit.change.kind === 'json' || edit.change.mode === 'replace') score += 0.2\n else if (edit.change.value.length > 0) score += 0.1\n return clamp01(score)\n}\n\nfunction splitPath(path: string): string[] {\n const parts = path\n .split('.')\n .map((p) => p.trim())\n .filter(Boolean)\n if (parts.length === 0)\n throw new PolicyEditValidationError('path must not be empty', 'change.path')\n return parts\n}\n\nfunction expectLiteral<T extends string>(\n value: unknown,\n expected: T,\n path: string,\n): asserts value is T {\n if (value !== expected) throw new PolicyEditValidationError(`expected ${expected}`, path)\n}\n\nfunction expectString(value: unknown, path: string): asserts value is string {\n if (typeof value !== 'string' || value.trim().length === 0) {\n throw new PolicyEditValidationError('expected non-empty string', path)\n }\n}\n\nfunction expectNonEmpty(value: unknown, path: string): string {\n expectString(value, path)\n return value\n}\n\nfunction expectConfidence(value: unknown, path: string): asserts value is number {\n if (typeof value !== 'number' || !Number.isFinite(value) || value < 0 || value > 1) {\n throw new PolicyEditValidationError('expected finite number in [0,1]', path)\n }\n}\n\nfunction hasExactTextBlock(surface: string, value: string): boolean {\n const needle = normalizeTextBlock(value)\n const normalizedSurface = surface.replace(/\\r\\n/g, '\\n')\n return [...normalizedSurface.split(/\\n{2,}/), ...normalizedSurface.split('\\n')].some(\n (block) => normalizeTextBlock(block) === needle,\n )\n}\n\nfunction normalizeTextBlock(value: string): string {\n return value.replace(/\\r\\n/g, '\\n').trim()\n}\n\nfunction expectOneOf<const T extends readonly string[]>(\n value: unknown,\n allowed: T,\n path: string,\n): asserts value is T[number] {\n if (typeof value !== 'string' || !allowed.includes(value)) {\n throw new PolicyEditValidationError(`expected one of ${allowed.join(', ')}`, path)\n }\n}\n\nfunction expectStringArray(value: unknown, path: string): asserts value is string[] {\n if (!Array.isArray(value)) throw new PolicyEditValidationError('expected array', path)\n for (const [i, item] of value.entries()) expectString(item, `${path}.${i}`)\n}\n\nfunction expectNonEmptyStringArray(value: unknown, path: string): asserts value is string[] {\n expectStringArray(value, path)\n if (value.length === 0) throw new PolicyEditValidationError('expected non-empty array', path)\n}\n\nfunction uniqueSorted(values: string[]): string[] {\n return [...new Set(values)].sort()\n}\n\nfunction clamp01(n: number): number {\n if (!Number.isFinite(n)) return 0\n if (n < 0) return 0\n if (n > 1) return 1\n return n\n}\n","export interface RunScore {\n success: number\n goalProgress: number\n repoGroundedness: number\n driftPenalty: number\n toolUseQuality: number\n patchQuality: number\n testReality: number\n finalGate: number\n reviewerBlockers: number\n costUsd: number\n wallSeconds: number\n notes?: string[]\n}\n\nexport interface RunScoreWeights {\n success: number\n goalProgress: number\n repoGroundedness: number\n driftPenalty: number\n toolUseQuality: number\n patchQuality: number\n testReality: number\n finalGate: number\n reviewerBlockers: number\n costUsd: number\n wallSeconds: number\n}\n\nexport const DEFAULT_RUN_SCORE_WEIGHTS: RunScoreWeights = {\n success: 4,\n goalProgress: 2,\n repoGroundedness: 1.5,\n driftPenalty: -1.5,\n toolUseQuality: 1,\n patchQuality: 1.25,\n testReality: 1.5,\n finalGate: 3,\n reviewerBlockers: -2,\n costUsd: -0.2,\n wallSeconds: -0.1,\n}\n\nexport function aggregateRunScore(score: RunScore, weights: Partial<RunScoreWeights> = {}): number {\n const w = { ...DEFAULT_RUN_SCORE_WEIGHTS, ...weights }\n return (\n w.success * clamp01(score.success) +\n w.goalProgress * clamp01(score.goalProgress) +\n w.repoGroundedness * clamp01(score.repoGroundedness) +\n w.driftPenalty * clamp01(score.driftPenalty) +\n w.toolUseQuality * clamp01(score.toolUseQuality) +\n w.patchQuality * clamp01(score.patchQuality) +\n w.testReality * clamp01(score.testReality) +\n w.finalGate * clamp01(score.finalGate) +\n w.reviewerBlockers * clamp01(score.reviewerBlockers) +\n w.costUsd * Math.max(0, finiteOrZero(score.costUsd)) +\n w.wallSeconds * Math.max(0, finiteOrZero(score.wallSeconds) / 60)\n )\n}\n\nexport function clamp01(value: number): number {\n if (!Number.isFinite(value)) return 0\n return Math.max(0, Math.min(1, value))\n}\n\nfunction finiteOrZero(value: number): number {\n return Number.isFinite(value) ? value : 0\n}\n"],"mappings":";;;;;;;;;;;;;;AAwBA,IAAM,mBAAqD,oBAAI,IAAyB;AAAA,EACtF;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAUM,SAAS,kBAAkB,SAAkC;AAClE,SAAO,QAAQ,cAAc,KAAK,CAAC,QAAQ,iBAAiB,IAAI,IAAI,IAAI,CAAC;AAC3E;AAKO,SAAS,eAAe,SAAkC;AAC/D,SAAO,QAAQ,uBAAuB;AACxC;AAqBO,SAAS,qBACd,UACA,UAAU,SACqB;AAC/B,QAAM,QAAQ,SAAS,OAAO,cAAc;AAC5C,MAAI,MAAM,SAAS,GAAG;AACpB,UAAM,IAAI;AAAA,MACR,GAAG,OAAO,sJACoE,MACzE,IAAI,CAAC,MAAM,EAAE,UAAU,EACvB,KAAK,IAAI,CAAC;AAAA,IACjB;AAAA,EACF;AACA,SAAO;AACT;;;AClFA,SAAS,kBAAkB;AAWpB,IAAM,mBAAmB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAIO,IAAM,8BAA8B;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAyEO,IAAM,4BAAN,cAAwC,gBAAgB;AAAA,EACpD;AAAA,EACT,YAAY,SAAiB,OAAO,IAAI;AACtC,UAAM,OAAO,GAAG,OAAO,QAAQ,IAAI,MAAM,OAAO;AAChD,SAAK,OAAO;AAAA,EACd;AACF;AAyBA,IAAM,oBAAoB;AAC1B,IAAM,4BAA4B;AAClC,IAAM,iBAAiB;AAEhB,SAAS,eAAe,MAAkC;AAC/D,QAAM,aAAa,oBAAoB;AAAA,IACrC,eAAe;AAAA,IACf,GAAG;AAAA,IACH,QAAQ,gBAAgB,KAAK,MAAM;AAAA,EACrC,CAAC;AACD,QAAM,OAAO;AAAA,IACX,GAAG;AAAA,IACH,QAAQ,KAAK,UAAU,oBAAoB,UAAU;AAAA,EACvD;AACA,SAAO,mBAAmB,IAAI;AAChC;AAEO,SAAS,oBAAoB,MAAuD;AACzF,QAAM,EAAE,QAAQ,SAAS,eAAe,GAAG,SAAS,IAAI;AACxD,OAAK;AACL,QAAM,YAAY,KAAK,UAAU,aAAa,EAAE,eAAe,GAAG,SAAS,CAAC,CAAC;AAC7E,SAAO,sBAAsB,WAAW,QAAQ,EAAE,OAAO,SAAS,EAAE,OAAO,KAAK,CAAC;AACnF;AAEO,SAAS,mBAAmB,OAA4B;AAC7D,MAAI,UAAU,QAAQ,OAAO,UAAU,UAAU;AAC/C,UAAM,IAAI,0BAA0B,iBAAiB;AAAA,EACvD;AACA,QAAM,MAAM;AACZ,gBAAc,IAAI,eAAe,kBAAkB,eAAe;AAClE,eAAa,IAAI,QAAQ,QAAQ;AACjC,MAAI,CAAC,eAAe,KAAK,IAAI,MAAM,GAAG;AACpC,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,cAAY,IAAI,MAAM,kBAAkB,MAAM;AAC9C,iBAAe,IAAI,MAAM;AACzB,iBAAe,IAAI,MAAM;AACzB,eAAa,IAAI,OAAO,OAAO;AAC/B,uBAAqB,IAAI,YAAY;AACrC,mBAAiB,IAAI,YAAY,YAAY;AAC7C,cAAY,IAAI,MAAM,CAAC,OAAO,UAAU,QAAQ,SAAS,GAAY,MAAM;AAC3E,iBAAe,IAAI,MAAM;AACzB,MAAI,IAAI,cAAc,OAAW,cAAa,IAAI,WAAW,WAAW;AACxE,MAAI,IAAI,mBAAmB,OAAW,cAAa,IAAI,gBAAgB,gBAAgB;AACvF,MAAI,IAAI,aAAa,WAAc,IAAI,aAAa,QAAQ,OAAO,IAAI,aAAa,WAAW;AAC7F,UAAM,IAAI,0BAA0B,mBAAmB,UAAU;AAAA,EACnE;AACA,QAAM,aAAa,oBAAoB,GAAG;AAC1C,MAAI,IAAI,WAAW,YAAY;AAC7B,UAAM,IAAI,0BAA0B,6CAA6C,QAAQ;AAAA,EAC3F;AACA,SAAO;AACT;AAEO,SAAS,aAAa,OAAqC;AAChE,MAAI;AACF,uBAAmB,KAAK;AACxB,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,wBACd,UACA,OAAmC,CAAC,GACtB;AACd,uBAAqB,UAAU,yBAAyB;AACxD,QAAM,QAAsB,CAAC;AAC7B,aAAW,WAAW,UAAU;AAC9B,UAAM,OAAO,sBAAsB,SAAS,IAAI;AAChD,QAAI,KAAM,OAAM,KAAK,IAAI;AAAA,EAC3B;AACA,SAAO;AACT;AAEO,SAAS,sBACd,SACA,OAAmC,CAAC,GACjB;AACnB,uBAAqB,CAAC,OAAO,GAAG,uBAAuB;AACvD,MAAI,CAAC,QAAQ,oBAAoB,KAAK,EAAG,QAAO;AAEhD,QAAM,eAAe,oBAAoB,SAAS,IAAI;AACtD,MAAI,CAAC,aAAc,QAAO;AAE1B,QAAM,SAAS,oBAAoB,QAAQ,SAAS,IAAI;AACxD,QAAM,OAAO,YAAY,SAAS,IAAI;AACtC,SAAO,eAAe;AAAA,IACpB,MAAM,OAAO;AAAA,IACb,QAAQ,OAAO;AAAA,IACf,QAAQ,EAAE,MAAM,QAAQ,MAAM,UAAU,OAAO,QAAQ,mBAAmB,KAAK,EAAE;AAAA,IACjF,OAAO,QAAQ;AAAA,IACf,WAAW,QAAQ;AAAA,IACnB;AAAA,IACA,YAAY,QAAQ;AAAA,IACpB;AAAA,IACA,gBAAgB,QAAQ;AAAA,IACxB,QAAQ;AAAA,MACN,YAAY,CAAC,QAAQ,UAAU;AAAA,MAC/B,YAAY,CAAC,QAAQ,UAAU;AAAA,MAC/B,cAAc,QAAQ;AAAA,MACtB,kBAAkB,QAAQ;AAAA,IAC5B;AAAA,EACF,CAAC;AACH;AAEO,SAAS,yBACd,MACA,OAAmC,CAAC,GAC5B;AACR,qBAAmB,IAAI;AACvB,QAAM,kBAAkB,KAAK,mBAAmB;AAChD,QAAM,gBAAgB,KAAK,IAAI,GAAG,KAAK,OAAO,aAAa,SAAS,CAAC;AACrE,QAAM,kBAAkB,QAAQ,KAAK,UAAU;AAC/C,QAAM,YAAY;AAAA,IAChB,KAAK,IAAI,KAAK,aAAa,MAAM,IAAI,KAAK,IAAI,kBAAkB,GAAG,IAAK;AAAA,EAC1E;AACA,QAAM,cAAc,uBAAuB,IAAI;AAC/C,QAAM,cACJ,KAAK,SAAS,UAAU,KAAK,kBAAkB,OAAO,OAAO,KAAK,SAAS,YAAY,MAAM;AAE/F,SAAO;AAAA,IACL,MAAM,gBACJ,OAAO,kBACP,OAAO,YACP,MAAM,cACN;AAAA,EACJ;AACF;AAEO,SAAS,gBACd,MACA,OAAmC,CAAC,GACf;AACrB,QAAM,YAAY,mBAAmB,IAAI;AACzC,QAAM,QAAQ,yBAAyB,WAAW,IAAI;AACtD,QAAM,UAAoB,CAAC;AAC3B,QAAM,kBAAkB,KAAK,mBAAmB;AAChD,QAAM,kBAAkB,KAAK,mBAAmB;AAEhD,MAAI,UAAU,OAAO,kBAAkB;AACrC,YAAQ,KAAK,mEAAmE;AAAA,EAClF;AACA,MAAI,mBAAmB,UAAU,OAAO,aAAa,WAAW,GAAG;AACjE,YAAQ,KAAK,uBAAuB;AAAA,EACtC;AACA,MAAI,KAAK,IAAI,UAAU,aAAa,MAAM,IAAI,iBAAiB;AAC7D,YAAQ,KAAK,uBAAuB,eAAe,EAAE;AAAA,EACvD;AACA,MAAI,UAAU,SAAS,UAAU,KAAK,kBAAkB,MAAM;AAC5D,YAAQ,KAAK,gDAAgD;AAAA,EAC/D;AACA,MAAI,SAAS,KAAK,YAAY,oBAAoB;AAChD,YAAQ;AAAA,MACN,mBAAmB,MAAM,QAAQ,CAAC,CAAC,WAAW,KAAK,YAAY,mBAAmB,QAAQ,CAAC,CAAC;AAAA,IAC9F;AAAA,EACF;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,UAAU,QAAQ,WAAW,IAAI,UAAU;AAAA,IAC3C;AAAA,IACA;AAAA,EACF;AACF;AAEO,SAAS,yBAAyB,SAAkB,MAA2B;AACpF,QAAM,YAAY,mBAAmB,IAAI;AACzC,MAAI,UAAU,OAAO,SAAS,OAAQ,QAAO,gBAAgB,SAAS,UAAU,MAAM;AACtF,SAAO,gBAAgB,SAAS,UAAU,MAAM;AAClD;AAEA,SAAS,oBACP,SACA,MACoD;AACpD,QAAM,SAAS,oBAAoB,OAAO;AAC1C,MAAI,CAAC,QAAQ;AACX,WAAO;AAAA,MACL,MAAM,KAAK,eAAe;AAAA,MAC1B,QAAQ,EAAE,SAAS,KAAK,wBAAwB,SAAS;AAAA,IAC3D;AAAA,EACF;AACA,SAAO,mBAAmB,MAAM;AAClC;AAEA,SAAS,mBAAmB,SAG1B;AACA,UAAQ,QAAQ,MAAM;AAAA,IACpB,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAQ,EAAE,SAAS,UAAU,MAAM,iBAAiB,QAAQ,OAAO,GAAG;AAAA,MACxE;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAQ,EAAE,SAAS,iBAAiB,MAAM,SAAS,QAAQ,IAAI,GAAG;AAAA,MACpE;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAQ;AAAA,UACN,SAAS;AAAA,UACT,MAAM,QAAQ,SACV,YAAY,QAAQ,IAAI,IAAI,QAAQ,MAAM,KAC1C,YAAY,QAAQ,IAAI;AAAA,QAC9B;AAAA,MACF;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAQ,EAAE,SAAS,iBAAiB,MAAM,YAAY,QAAQ,IAAI,GAAG;AAAA,MACvE;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAQ;AAAA,UACN,SAAS;AAAA,UACT,MAAM,QAAQ,OAAO,OAAO,QAAQ,MAAM,IAAI,QAAQ,IAAI,KAAK,OAAO,QAAQ,MAAM;AAAA,QACtF;AAAA,MACF;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAQ,EAAE,SAAS,iBAAiB,MAAM,QAAQ,QAAQ,IAAI,GAAG;AAAA,MACnE;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAQ,EAAE,SAAS,iBAAiB,MAAM,YAAY,QAAQ,IAAI,GAAG;AAAA,MACvE;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAQ,EAAE,SAAS,kBAAkB,MAAM,YAAY,QAAQ,IAAI,GAAG;AAAA,MACxE;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM,kBAAkB,QAAQ,KAAK;AAAA,QACrC,QAAQ,EAAE,SAAS,kBAAkB,MAAM,kBAAkB,QAAQ,KAAK,GAAG;AAAA,MAC/E;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAQ,EAAE,SAAS,iBAAiB,MAAM,iBAAiB,QAAQ,KAAK,GAAG;AAAA,MAC7E;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAQ,EAAE,SAAS,QAAQ,MAAM,QAAQ,QAAQ,IAAI,GAAG;AAAA,MAC1D;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAQ,EAAE,SAAS,UAAU,MAAM,OAAO,QAAQ,MAAM,IAAI,QAAQ,KAAK,GAAG;AAAA,MAC9E;AAAA,IACF,KAAK;AACH,aAAO,EAAE,MAAM,UAAU,QAAQ,EAAE,SAAS,UAAU,MAAM,UAAU,QAAQ,GAAG,GAAG,EAAE;AAAA,IACxF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAQ,EAAE,SAAS,kBAAkB,MAAM,eAAe,QAAQ,OAAO,GAAG;AAAA,MAC9E;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAQ,EAAE,SAAS,kBAAkB,MAAM,iBAAiB,QAAQ,KAAK,GAAG;AAAA,MAC9E;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAQ;AAAA,UACN,SAAS;AAAA,UACT,MAAM,wBAAwB,QAAQ,IAAI,GAAG,QAAQ,UAAU,IAAI,QAAQ,OAAO,KAAK,EAAE;AAAA,QAC3F;AAAA,MACF;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAQ,EAAE,SAAS,UAAU,MAAM,yBAAyB,QAAQ,KAAK,GAAG;AAAA,MAC9E;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAQ,EAAE,SAAS,UAAU,MAAM,uBAAuB,QAAQ,QAAQ,GAAG;AAAA,MAC/E;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAQ,EAAE,SAAS,UAAU,MAAM,yBAAyB,QAAQ,IAAI,GAAG;AAAA,MAC7E;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAQ,EAAE,SAAS,UAAU,MAAM,sBAAsB,QAAQ,KAAK,GAAG;AAAA,MAC3E;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAQ,EAAE,SAAS,UAAU,MAAM,qBAAqB,QAAQ,KAAK,GAAG;AAAA,MAC1E;AAAA,IACF,KAAK;AACH,aAAO,EAAE,MAAM,kBAAkB,QAAQ,EAAE,SAAS,UAAU,MAAM,QAAQ,MAAM,EAAE;AAAA,EACxF;AACF;AAEA,SAAS,kBAAkB,OAA+B;AACxD,QAAM,aAAa,MAAM,YAAY;AACrC,MAAI,qEAAqE,KAAK,UAAU,GAAG;AACzF,WAAO;AAAA,EACT;AACA,MAAI,oEAAoE,KAAK,UAAU,GAAG;AACxF,WAAO;AAAA,EACT;AACA,MAAI,uBAAuB,KAAK,UAAU,EAAG,QAAO;AACpD,SAAO;AACT;AAEA,SAAS,oBACP,SACA,MAC+B;AAC/B,MAAI,OAAO,KAAK,iBAAiB,WAAY,QAAO,KAAK,aAAa,OAAO,KAAK;AAClF,MAAI,KAAK,aAAc,QAAO,KAAK;AACnC,SAAO,6BAA6B,QAAQ,QAAQ;AACtD;AAEA,SAAS,6BACP,UAC+B;AAC/B,QAAM,MACJ,uBAAuB,QAAQ,GAAG,gBAClC,uBAAuB,QAAQ,GAAG;AACpC,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;AAC5C,QAAM,MAAM;AACZ,MACE,OAAO,IAAI,WAAW,YACrB,IAAI,cAAc,cAAc,IAAI,cAAc,cACnD,OAAO,IAAI,WAAW,UACtB;AACA,WAAO;AAAA,EACT;AACA,QAAM,MAA8B;AAAA,IAClC,QAAQ,IAAI;AAAA,IACZ,WAAW,IAAI;AAAA,IACf,QAAQ,IAAI;AAAA,EACd;AACA,MACE,IAAI,SAAS,cACb,IAAI,SAAS,cACb,IAAI,SAAS,aACb,IAAI,SAAS,SACb;AACA,QAAI,OAAO,IAAI;AAAA,EACjB;AACA,MAAI,OAAO,IAAI,cAAc,SAAU,KAAI,YAAY,IAAI;AAC3D,SAAO;AACT;AAEA,SAAS,uBACP,UACgC;AAChC,QAAM,MAAM,UAAU,cAAc,UAAU;AAC9C,SAAO,OAAO,OAAO,QAAQ,WAAY,MAAkC;AAC7E;AAEA,SAAS,YAAY,SAAyB,MAAkD;AAC9F,MAAI,OAAO,KAAK,SAAS,WAAY,QAAO,KAAK,KAAK,OAAO;AAC7D,MAAI,KAAK,KAAM,QAAO,KAAK;AAC3B,QAAM,MAAM,uBAAuB,QAAQ,QAAQ,GAAG;AACtD,MAAI,QAAQ,SAAS,QAAQ,YAAY,QAAQ,UAAU,QAAQ,UAAW,QAAO;AACrF,MAAI,QAAQ,aAAa,cAAc,QAAQ,aAAa,OAAQ,QAAO;AAC3E,SAAO;AACT;AAEA,SAAS,gBACP,SACA,QACQ;AACR,MAAI,OAAO,YAAY,UAAU;AAC/B,UAAM,IAAI,0BAA0B,8CAA8C,QAAQ;AAAA,EAC5F;AACA,MAAI,OAAO,SAAS,UAAU;AAC5B,QAAI,kBAAkB,SAAS,OAAO,KAAK,EAAG,QAAO;AACrD,WAAO,GAAG,QAAQ,QAAQ,CAAC;AAAA;AAAA,EAAO,OAAO,KAAK,GAAG,UAAU;AAAA,EAC7D;AACA,MAAI,OAAO,SAAS,WAAW;AAC7B,QAAI,kBAAkB,SAAS,OAAO,KAAK,EAAG,QAAO;AACrD,WAAO,GAAG,OAAO,KAAK;AAAA;AAAA,EAAO,QAAQ,UAAU,CAAC,GAAG,QAAQ;AAAA,EAC7D;AACA,QAAM,OAAO,eAAe,OAAO,MAAM,aAAa;AACtD,MAAI,CAAC,QAAQ,SAAS,IAAI,GAAG;AAC3B,UAAM,IAAI,0BAA0B,uCAAuC,aAAa;AAAA,EAC1F;AACA,SAAO,QAAQ,QAAQ,MAAM,OAAO,KAAK;AAC3C;AAEA,SAAS,gBACP,SACA,QACkB;AAClB,QAAM,OAAO,iBAAiB,OAAO;AACrC,QAAM,OAAO,UAAU,OAAO,IAAI;AAClC,MAAI,OAAO,SAAS,SAAU,QAAO,cAAc,MAAM,MAAM,QAAW,QAAQ;AAClF,MAAI,OAAO,SAAS,MAAO,QAAO,cAAc,MAAM,MAAM,OAAO,SAAS,MAAM,KAAK;AACvF,QAAM,QAAQ,eAAe,MAAM,IAAI;AACvC,QAAM,SACJ,SACA,OAAO,UAAU,YACjB,CAAC,MAAM,QAAQ,KAAK,KACpB,OAAO,SACP,OAAO,OAAO,UAAU,YACxB,CAAC,MAAM,QAAQ,OAAO,KAAK,IACvB,EAAE,GAAG,OAAO,GAAG,OAAO,MAAM,IAC3B,OAAO,SAAS;AACvB,SAAO,cAAc,MAAM,MAAM,QAA4B,KAAK;AACpE;AAEA,SAAS,iBAAiB,SAAoC;AAC5D,MAAI,OAAO,YAAY,UAAU;AAC/B,QAAI;AACF,aAAO,KAAK,MAAM,OAAO;AAAA,IAC3B,QAAQ;AACN,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,aAAW,SAAS,SAAS;AAC7B,SAAO;AACT;AAEA,SAAS,eAAe,MAAwB,MAA8C;AAC5F,MAAI,SAAuC;AAC3C,aAAW,QAAQ,MAAM;AACvB,QAAI,CAAC,UAAU,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,EAAG,QAAO;AAC3E,aAAS,OAAO,IAAI;AAAA,EACtB;AACA,SAAO;AACT;AAEA,SAAS,cACP,MACA,MACA,OACA,MACkB;AAClB,MAAI,KAAK,WAAW,GAAG;AACrB,QAAI,SAAS,SAAU,QAAO;AAC9B,WAAO,SAAS;AAAA,EAClB;AACA,MAAI,SAAS,QAAQ,OAAO,SAAS,YAAY,MAAM,QAAQ,IAAI,GAAG;AACpE,UAAM,IAAI,0BAA0B,oCAAoC,aAAa;AAAA,EACvF;AACA,QAAM,MAAwC,EAAE,GAAG,KAAK;AACxD,MAAI,SAA2C;AAC/C,WAAS,IAAI,GAAG,IAAI,KAAK,SAAS,GAAG,KAAK;AACxC,UAAM,MAAM,KAAK,CAAC;AAClB,UAAM,WAAW,OAAO,GAAG;AAC3B,QACE,SAAS,aACR,CAAC,YAAY,OAAO,aAAa,YAAY,MAAM,QAAQ,QAAQ,IACpE;AACA,aAAO;AAAA,IACT;AACA,UAAM,OACJ,YAAY,OAAO,aAAa,YAAY,CAAC,MAAM,QAAQ,QAAQ,IAAI,EAAE,GAAG,SAAS,IAAI,CAAC;AAC5F,WAAO,GAAG,IAAI;AACd,aAAS;AAAA,EACX;AACA,QAAM,OAAO,KAAK,KAAK,SAAS,CAAC;AACjC,MAAI,SAAS,SAAU,QAAO,OAAO,IAAI;AAAA,MACpC,QAAO,IAAI,IAAI,SAAS;AAC7B,SAAO;AACT;AAEA,SAAS,oBAAoB,OAA+D;AAC1F,QAAM,MAAkC;AAAA,IACtC,eAAe;AAAA,IACf,MAAM,MAAM;AAAA,IACZ,QAAQ,gBAAgB,MAAM,MAAM;AAAA,IACpC,QAAQ,gBAAgB,MAAM,MAAM;AAAA,IACpC,OAAO,MAAM,MAAM,KAAK;AAAA,IACxB,cAAc,sBAAsB,MAAM,YAAY;AAAA,IACtD,YAAY,MAAM;AAAA,IAClB,MAAM,MAAM;AAAA,IACZ,QAAQ,gBAAgB,MAAM,MAAM;AAAA,EACtC;AACA,MAAI,MAAM,WAAW,KAAK,EAAG,KAAI,YAAY,MAAM,UAAU,KAAK;AAClE,MAAI,MAAM,gBAAgB,KAAK,EAAG,KAAI,iBAAiB,MAAM,eAAe,KAAK;AACjF,MAAI,MAAM,SAAU,KAAI,WAAW,MAAM;AACzC,SAAO;AACT;AAEA,SAAS,gBAAgB,QAA4C;AACnE,QAAM,MAAwB,EAAE,SAAS,OAAO,QAAQ;AACxD,MAAI,OAAO,MAAM,KAAK,EAAG,KAAI,OAAO,OAAO,KAAK,KAAK;AACrD,MAAI,OAAO;AACT,QAAI,mBAAmB,yBAAyB,OAAO,gBAAgB;AACzE,MAAI,OAAO,OAAO,KAAK,EAAG,KAAI,QAAQ,OAAO,MAAM,KAAK;AACxD,SAAO;AACT;AAEA,SAAS,gBAAgB,QAA4C;AACnE,MAAI,OAAO,SAAS,QAAQ;AAC1B,UAAMA,OAAmD;AAAA,MACvD,MAAM;AAAA,MACN,MAAM,OAAO;AAAA,MACb,OAAO,OAAO,MAAM,KAAK;AAAA,IAC3B;AACA,QAAI,OAAO,MAAM,KAAK,EAAG,CAAAA,KAAI,OAAO,OAAO,KAAK,KAAK;AACrD,WAAOA;AAAA,EACT;AACA,QAAM,MAAmD;AAAA,IACvD,MAAM;AAAA,IACN,MAAM,OAAO;AAAA,IACb,MAAM,OAAO,KAAK,KAAK;AAAA,EACzB;AACA,MAAI,OAAO,UAAU,OAAW,KAAI,QAAQ,OAAO;AACnD,SAAO;AACT;AAEA,SAAS,sBAAsB,MAAsD;AACnF,QAAM,MAA8B;AAAA,IAClC,QAAQ,KAAK,OAAO,KAAK;AAAA,IACzB,WAAW,KAAK;AAAA,IAChB,QAAQ,KAAK;AAAA,EACf;AACA,MAAI,KAAK,KAAM,KAAI,OAAO,KAAK;AAC/B,MAAI,KAAK,WAAW,KAAK,EAAG,KAAI,YAAY,KAAK,UAAU,KAAK;AAChE,SAAO;AACT;AAEA,SAAS,gBAAgB,QAA4C;AACnE,QAAM,MAAwB;AAAA,IAC5B,YAAY,aAAa,OAAO,WAAW,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,OAAO,CAAC;AAAA,IAC/E,YAAY,aAAa,OAAO,WAAW,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,OAAO,CAAC;AAAA,IAC/E,cAAc,OAAO;AAAA,EACvB;AACA,MAAI,OAAO,iBAAkB,KAAI,mBAAmB;AACpD,SAAO;AACT;AAEA,SAAS,eAAe,QAAqD;AAC3E,MAAI,CAAC,UAAU,OAAO,WAAW;AAC/B,UAAM,IAAI,0BAA0B,mBAAmB,QAAQ;AACjE,QAAM,MAAM;AACZ,cAAY,IAAI,SAAS,6BAA6B,gBAAgB;AACtE,MAAI,IAAI,SAAS,OAAW,cAAa,IAAI,MAAM,aAAa;AAChE,MAAI,IAAI,UAAU,OAAW,cAAa,IAAI,OAAO,cAAc;AACnE,MAAI,IAAI,qBAAqB,OAAW,0BAAyB,IAAI,gBAAgB;AACvF;AAEA,SAAS,eAAe,QAAqD;AAC3E,MAAI,CAAC,UAAU,OAAO,WAAW;AAC/B,UAAM,IAAI,0BAA0B,mBAAmB,QAAQ;AACjE,QAAM,MAAM;AACZ,MAAI,IAAI,SAAS,UAAU,IAAI,SAAS,QAAQ;AAC9C,UAAM,IAAI,0BAA0B,6BAA6B,aAAa;AAAA,EAChF;AACA,MAAI,IAAI,SAAS,QAAQ;AACvB,gBAAY,IAAI,MAAM,CAAC,UAAU,WAAW,SAAS,GAAY,aAAa;AAC9E,iBAAa,IAAI,OAAO,cAAc;AACtC,QAAI,IAAI,SAAS,UAAW,cAAa,IAAI,MAAM,aAAa;AAChE;AAAA,EACF;AACA,cAAY,IAAI,MAAM,CAAC,OAAO,SAAS,QAAQ,GAAY,aAAa;AACxE,eAAa,IAAI,MAAM,aAAa;AACpC,MAAI,IAAI,UAAU,OAAW,YAAW,IAAI,OAAO,cAAc;AACnE;AAEA,SAAS,qBAAqB,MAAuD;AACnF,MAAI,CAAC,QAAQ,OAAO,SAAS;AAC3B,UAAM,IAAI,0BAA0B,mBAAmB,cAAc;AACvE,QAAM,MAAM;AACZ,eAAa,IAAI,QAAQ,qBAAqB;AAC9C,cAAY,IAAI,WAAW,CAAC,YAAY,UAAU,GAAY,wBAAwB;AACtF,MAAI,CAAC,OAAO,SAAS,IAAI,MAAM,KAAK,IAAI,UAAU,GAAG;AACnD,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,MAAI,IAAI,SAAS,QAAW;AAC1B;AAAA,MACE,IAAI;AAAA,MACJ,CAAC,YAAY,YAAY,WAAW,OAAO;AAAA,MAC3C;AAAA,IACF;AAAA,EACF;AACA,MAAI,IAAI,cAAc,OAAW,cAAa,IAAI,WAAW,wBAAwB;AACvF;AAEA,SAAS,eAAe,QAAqD;AAC3E,MAAI,CAAC,UAAU,OAAO,WAAW;AAC/B,UAAM,IAAI,0BAA0B,mBAAmB,QAAQ;AACjE,QAAM,MAAM;AACZ,4BAA0B,IAAI,YAAY,mBAAmB;AAC7D,4BAA0B,IAAI,YAAY,mBAAmB;AAC7D,MAAI,CAAC,MAAM,QAAQ,IAAI,YAAY,GAAG;AACpC,UAAM,IAAI,0BAA0B,kBAAkB,qBAAqB;AAAA,EAC7E;AACA,aAAW,CAAC,GAAG,GAAG,KAAK,IAAI,aAAa,QAAQ;AAC9C,wBAAoB,KAAK,uBAAuB,CAAC,EAAE;AACrD,MAAI,IAAI,qBAAqB,UAAa,OAAO,IAAI,qBAAqB,WAAW;AACnF,UAAM,IAAI,0BAA0B,oBAAoB,yBAAyB;AAAA,EACnF;AACF;AAEA,SAAS,oBAAoB,KAAc,MAA0C;AACnF,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,OAAM,IAAI,0BAA0B,mBAAmB,IAAI;AAChG,QAAM,MAAM;AACZ,cAAY,IAAI,MAAM,CAAC,QAAQ,SAAS,YAAY,WAAW,QAAQ,GAAY,GAAG,IAAI,OAAO;AACjG,eAAa,IAAI,KAAK,GAAG,IAAI,MAAM;AACnC,MAAI,IAAI,YAAY,OAAW,cAAa,IAAI,SAAS,GAAG,IAAI,UAAU;AAC5E;AAEA,SAAS,WAAW,OAAgB,MAAiD;AACnF,MACE,UAAU,QACV,OAAO,UAAU,YACjB,OAAO,UAAU,aAChB,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,GACnD;AACA;AAAA,EACF;AACA,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,eAAW,CAAC,GAAG,IAAI,KAAK,MAAM,QAAQ,EAAG,YAAW,MAAM,GAAG,IAAI,IAAI,CAAC,EAAE;AACxE;AAAA,EACF;AACA,MAAI,OAAO,UAAU,UAAU;AAC7B,eAAW,CAAC,KAAK,IAAI,KAAK,OAAO,QAAQ,KAAK,GAAG;AAC/C,UAAI,CAAC,IAAK,OAAM,IAAI,0BAA0B,oBAAoB,IAAI;AACtE,iBAAW,MAAM,GAAG,IAAI,IAAI,GAAG,EAAE;AAAA,IACnC;AACA;AAAA,EACF;AACA,QAAM,IAAI,0BAA0B,kCAAkC,IAAI;AAC5E;AAEA,SAAS,uBAAuB,MAA0B;AACxD,MAAI,QAAQ;AACZ,MAAI,KAAK,OAAO,KAAM,UAAS;AAC/B,MAAI,KAAK,OAAO,iBAAkB,UAAS;AAC3C,MAAI,KAAK,OAAO,SAAS,UAAU,KAAK,OAAO,SAAS,UAAW,UAAS;AAAA,WACnE,KAAK,OAAO,MAAM,SAAS,EAAG,UAAS;AAChD,SAAO,QAAQ,KAAK;AACtB;AAEA,SAAS,UAAU,MAAwB;AACzC,QAAM,QAAQ,KACX,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,OAAO;AACjB,MAAI,MAAM,WAAW;AACnB,UAAM,IAAI,0BAA0B,0BAA0B,aAAa;AAC7E,SAAO;AACT;AAEA,SAAS,cACP,OACA,UACA,MACoB;AACpB,MAAI,UAAU,SAAU,OAAM,IAAI,0BAA0B,YAAY,QAAQ,IAAI,IAAI;AAC1F;AAEA,SAAS,aAAa,OAAgB,MAAuC;AAC3E,MAAI,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,WAAW,GAAG;AAC1D,UAAM,IAAI,0BAA0B,6BAA6B,IAAI;AAAA,EACvE;AACF;AAEA,SAAS,eAAe,OAAgB,MAAsB;AAC5D,eAAa,OAAO,IAAI;AACxB,SAAO;AACT;AAEA,SAAS,iBAAiB,OAAgB,MAAuC;AAC/E,MAAI,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,KAAK,QAAQ,KAAK,QAAQ,GAAG;AAClF,UAAM,IAAI,0BAA0B,mCAAmC,IAAI;AAAA,EAC7E;AACF;AAEA,SAAS,kBAAkB,SAAiB,OAAwB;AAClE,QAAM,SAAS,mBAAmB,KAAK;AACvC,QAAM,oBAAoB,QAAQ,QAAQ,SAAS,IAAI;AACvD,SAAO,CAAC,GAAG,kBAAkB,MAAM,QAAQ,GAAG,GAAG,kBAAkB,MAAM,IAAI,CAAC,EAAE;AAAA,IAC9E,CAAC,UAAU,mBAAmB,KAAK,MAAM;AAAA,EAC3C;AACF;AAEA,SAAS,mBAAmB,OAAuB;AACjD,SAAO,MAAM,QAAQ,SAAS,IAAI,EAAE,KAAK;AAC3C;AAEA,SAAS,YACP,OACA,SACA,MAC4B;AAC5B,MAAI,OAAO,UAAU,YAAY,CAAC,QAAQ,SAAS,KAAK,GAAG;AACzD,UAAM,IAAI,0BAA0B,mBAAmB,QAAQ,KAAK,IAAI,CAAC,IAAI,IAAI;AAAA,EACnF;AACF;AAEA,SAAS,kBAAkB,OAAgB,MAAyC;AAClF,MAAI,CAAC,MAAM,QAAQ,KAAK,EAAG,OAAM,IAAI,0BAA0B,kBAAkB,IAAI;AACrF,aAAW,CAAC,GAAG,IAAI,KAAK,MAAM,QAAQ,EAAG,cAAa,MAAM,GAAG,IAAI,IAAI,CAAC,EAAE;AAC5E;AAEA,SAAS,0BAA0B,OAAgB,MAAyC;AAC1F,oBAAkB,OAAO,IAAI;AAC7B,MAAI,MAAM,WAAW,EAAG,OAAM,IAAI,0BAA0B,4BAA4B,IAAI;AAC9F;AAEA,SAAS,aAAa,QAA4B;AAChD,SAAO,CAAC,GAAG,IAAI,IAAI,MAAM,CAAC,EAAE,KAAK;AACnC;AAEA,SAAS,QAAQ,GAAmB;AAClC,MAAI,CAAC,OAAO,SAAS,CAAC,EAAG,QAAO;AAChC,MAAI,IAAI,EAAG,QAAO;AAClB,MAAI,IAAI,EAAG,QAAO;AAClB,SAAO;AACT;;;AC10BO,IAAM,4BAA6C;AAAA,EACxD,SAAS;AAAA,EACT,cAAc;AAAA,EACd,kBAAkB;AAAA,EAClB,cAAc;AAAA,EACd,gBAAgB;AAAA,EAChB,cAAc;AAAA,EACd,aAAa;AAAA,EACb,WAAW;AAAA,EACX,kBAAkB;AAAA,EAClB,SAAS;AAAA,EACT,aAAa;AACf;AAEO,SAAS,kBAAkB,OAAiB,UAAoC,CAAC,GAAW;AACjG,QAAM,IAAI,EAAE,GAAG,2BAA2B,GAAG,QAAQ;AACrD,SACE,EAAE,UAAUC,SAAQ,MAAM,OAAO,IACjC,EAAE,eAAeA,SAAQ,MAAM,YAAY,IAC3C,EAAE,mBAAmBA,SAAQ,MAAM,gBAAgB,IACnD,EAAE,eAAeA,SAAQ,MAAM,YAAY,IAC3C,EAAE,iBAAiBA,SAAQ,MAAM,cAAc,IAC/C,EAAE,eAAeA,SAAQ,MAAM,YAAY,IAC3C,EAAE,cAAcA,SAAQ,MAAM,WAAW,IACzC,EAAE,YAAYA,SAAQ,MAAM,SAAS,IACrC,EAAE,mBAAmBA,SAAQ,MAAM,gBAAgB,IACnD,EAAE,UAAU,KAAK,IAAI,GAAG,aAAa,MAAM,OAAO,CAAC,IACnD,EAAE,cAAc,KAAK,IAAI,GAAG,aAAa,MAAM,WAAW,IAAI,EAAE;AAEpE;AAEO,SAASA,SAAQ,OAAuB;AAC7C,MAAI,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO;AACpC,SAAO,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,KAAK,CAAC;AACvC;AAEA,SAAS,aAAa,OAAuB;AAC3C,SAAO,OAAO,SAAS,KAAK,IAAI,QAAQ;AAC1C;","names":["out","clamp01"]}