@tangle-network/agent-eval 0.126.6 → 0.127.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +51 -0
- package/README.md +2 -2
- package/dist/analyst/index.d.ts +52 -40
- package/dist/analyst/index.js +4 -4
- package/dist/belief-state/index.d.ts +34 -36
- package/dist/belief-state/index.js +2 -2
- package/dist/benchmarks/index.d.ts +30 -12
- package/dist/benchmarks/index.js +12 -10
- package/dist/builder-eval/index.js +1 -1
- package/dist/campaign/index.d.ts +432 -146
- package/dist/campaign/index.js +11 -9
- package/dist/{chunk-ARU2PZFM.js → chunk-424XHVKV.js} +43 -23
- package/dist/chunk-424XHVKV.js.map +1 -0
- package/dist/{chunk-NUKSVU3W.js → chunk-5DTSBUL2.js} +2 -2
- package/dist/{chunk-BTACVO2W.js → chunk-65QETEKG.js} +542 -258
- package/dist/chunk-65QETEKG.js.map +1 -0
- package/dist/{chunk-CGG5SLH3.js → chunk-ARCFIY7U.js} +2 -2
- package/dist/{chunk-P22LJ3Y2.js → chunk-CLELFRQG.js} +6 -4
- package/dist/chunk-CLELFRQG.js.map +1 -0
- package/dist/{chunk-LUNF2SEL.js → chunk-DJKY2TSY.js} +3 -3
- package/dist/chunk-DPUHNQLN.js +232 -0
- package/dist/chunk-DPUHNQLN.js.map +1 -0
- package/dist/{chunk-IPYXE555.js → chunk-EJGRPCO3.js} +36 -13
- package/dist/chunk-EJGRPCO3.js.map +1 -0
- package/dist/{chunk-K6IAZZ6L.js → chunk-FA3MFYNA.js} +132 -110
- package/dist/chunk-FA3MFYNA.js.map +1 -0
- package/dist/chunk-IG35YDN3.js +614 -0
- package/dist/chunk-IG35YDN3.js.map +1 -0
- package/dist/{chunk-DPZAEKA6.js → chunk-IMGJZBFG.js} +84 -56
- package/dist/chunk-IMGJZBFG.js.map +1 -0
- package/dist/{chunk-H5UD2323.js → chunk-IN455XKS.js} +53 -1
- package/dist/chunk-IN455XKS.js.map +1 -0
- package/dist/{chunk-PJQFMIOX.js → chunk-MHELPNRP.js} +31 -1
- package/dist/chunk-MHELPNRP.js.map +1 -0
- package/dist/{chunk-IILEIWGW.js → chunk-NL33OXEP.js} +41 -36
- package/dist/chunk-NL33OXEP.js.map +1 -0
- package/dist/{chunk-474LBSOX.js → chunk-OD5NQ2CS.js} +41 -16
- package/dist/chunk-OD5NQ2CS.js.map +1 -0
- package/dist/{chunk-QOTFXW5L.js → chunk-P5W7RQKK.js} +2 -2
- package/dist/{chunk-IR3KBHOY.js → chunk-P6FYH6K4.js} +39 -3
- package/dist/chunk-P6FYH6K4.js.map +1 -0
- package/dist/{chunk-J5SQWP6Y.js → chunk-PBE2LOSS.js} +18 -2
- package/dist/chunk-PBE2LOSS.js.map +1 -0
- package/dist/{chunk-E3IADPLG.js → chunk-PRB53E3J.js} +50 -224
- package/dist/chunk-PRB53E3J.js.map +1 -0
- package/dist/{chunk-X4UCIOTZ.js → chunk-S5YLIBFX.js} +2 -2
- package/dist/{chunk-7VYEGHWF.js → chunk-SZLVEKMJ.js} +41 -20
- package/dist/chunk-SZLVEKMJ.js.map +1 -0
- package/dist/{chunk-LKKT3IVV.js → chunk-TSN7JT6D.js} +6 -4
- package/dist/{chunk-LKKT3IVV.js.map → chunk-TSN7JT6D.js.map} +1 -1
- package/dist/{chunk-M7AH34KV.js → chunk-UTA2TGC4.js} +32 -16
- package/dist/chunk-UTA2TGC4.js.map +1 -0
- package/dist/{chunk-MAX3TN3C.js → chunk-UWZZKKU7.js} +7 -19
- package/dist/chunk-UWZZKKU7.js.map +1 -0
- package/dist/{chunk-ZVCHKKOP.js → chunk-WED5KLZQ.js} +39 -15
- package/dist/chunk-WED5KLZQ.js.map +1 -0
- package/dist/{chunk-OCFJACJU.js → chunk-X6VOPMLD.js} +74 -14
- package/dist/chunk-X6VOPMLD.js.map +1 -0
- package/dist/{chunk-MOXWMGPC.js → chunk-Y5CYOL42.js} +159 -70
- package/dist/chunk-Y5CYOL42.js.map +1 -0
- package/dist/{chunk-FO7HEH76.js → chunk-YJBNWCAA.js} +2 -2
- package/dist/chunk-ZPUEEM5U.js +179 -0
- package/dist/chunk-ZPUEEM5U.js.map +1 -0
- package/dist/{chunk-KE2VWPZX.js → chunk-ZUUWPZCV.js} +3 -3
- package/dist/cli.js +4 -4
- package/dist/contract/index.d.ts +464 -126
- package/dist/contract/index.js +425 -211
- package/dist/contract/index.js.map +1 -1
- package/dist/control.d.ts +34 -32
- package/dist/control.js +2 -2
- package/dist/hosted/index.d.ts +142 -41
- package/dist/hosted/index.js +23 -1
- package/dist/index.d.ts +365 -256
- package/dist/index.js +315 -226
- package/dist/index.js.map +1 -1
- package/dist/meta-eval/index.d.ts +32 -32
- package/dist/meta-eval/index.js +2 -2
- package/dist/multishot/index.d.ts +1 -0
- package/dist/openapi.json +1 -1
- package/dist/pipelines/index.js +2 -2
- package/dist/reporting.d.ts +97 -74
- package/dist/reporting.js +5 -4
- package/dist/rl.d.ts +177 -131
- package/dist/rl.js +232 -126
- package/dist/rl.js.map +1 -1
- package/dist/rollout/index.d.ts +53 -63
- package/dist/rollout/index.js +7 -5
- package/dist/{run-campaign-FFRM3RH5.js → run-campaign-3A2EBYBE.js} +4 -3
- package/dist/supervisor-run/index.d.ts +7 -15
- package/dist/supervisor-run/index.js +2 -2
- package/dist/traces.d.ts +66 -41
- package/dist/traces.js +9 -5
- package/dist/wire/index.d.ts +1 -0
- package/dist/wire/index.js +2 -2
- package/docs/concepts.md +18 -1
- package/docs/hosted-ingest-spec.md +37 -40
- package/docs/insight-report.md +57 -7
- package/docs/research-report-methodology.md +20 -21
- package/docs/rollout.md +13 -8
- package/package.json +21 -14
- package/dist/chunk-474LBSOX.js.map +0 -1
- package/dist/chunk-7VYEGHWF.js.map +0 -1
- package/dist/chunk-ARU2PZFM.js.map +0 -1
- package/dist/chunk-BTACVO2W.js.map +0 -1
- package/dist/chunk-DPZAEKA6.js.map +0 -1
- package/dist/chunk-E3IADPLG.js.map +0 -1
- package/dist/chunk-H5UD2323.js.map +0 -1
- package/dist/chunk-IILEIWGW.js.map +0 -1
- package/dist/chunk-IPYXE555.js.map +0 -1
- package/dist/chunk-IR3KBHOY.js.map +0 -1
- package/dist/chunk-J5SQWP6Y.js.map +0 -1
- package/dist/chunk-K6IAZZ6L.js.map +0 -1
- package/dist/chunk-M7AH34KV.js.map +0 -1
- package/dist/chunk-MAX3TN3C.js.map +0 -1
- package/dist/chunk-MOXWMGPC.js.map +0 -1
- package/dist/chunk-OCFJACJU.js.map +0 -1
- package/dist/chunk-P22LJ3Y2.js.map +0 -1
- package/dist/chunk-PJQFMIOX.js.map +0 -1
- package/dist/chunk-ZVCHKKOP.js.map +0 -1
- package/dist/chunk-ZZUXHH3R.js +0 -99
- package/dist/chunk-ZZUXHH3R.js.map +0 -1
- /package/dist/{chunk-NUKSVU3W.js.map → chunk-5DTSBUL2.js.map} +0 -0
- /package/dist/{chunk-CGG5SLH3.js.map → chunk-ARCFIY7U.js.map} +0 -0
- /package/dist/{chunk-LUNF2SEL.js.map → chunk-DJKY2TSY.js.map} +0 -0
- /package/dist/{chunk-QOTFXW5L.js.map → chunk-P5W7RQKK.js.map} +0 -0
- /package/dist/{chunk-X4UCIOTZ.js.map → chunk-S5YLIBFX.js.map} +0 -0
- /package/dist/{chunk-FO7HEH76.js.map → chunk-YJBNWCAA.js.map} +0 -0
- /package/dist/{chunk-KE2VWPZX.js.map → chunk-ZUUWPZCV.js.map} +0 -0
- /package/dist/{run-campaign-FFRM3RH5.js.map → run-campaign-3A2EBYBE.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hosted/types.ts","../src/hosted/schemas.ts","../src/hosted/client.ts"],"sourcesContent":["/**\n * # Hosted-tier wire format — the schema that EVERY orchestrator (ours,\n * a partner's self-hosted one, a future open implementation) must accept.\n *\n * This package implements exactly one wire version. Servers reject every\n * other version instead of translating old payloads.\n *\n * The wire format is two event streams in one transport:\n *\n * 1. **Eval-run events** (`POST /v1/ingest/eval-runs`). Posted when a\n * campaign / improvement-loop completes (or per-generation if\n * streaming). Carries the structured result + per-cell scores +\n * surface diffs the orchestrator stores for the dashboard.\n *\n * 2. **Trace spans** (`POST /v1/ingest/traces`). Standard OTLP-shaped\n * spans with a few additional attributes so the orchestrator can\n * pivot from eval-run → underlying execution. Compatible with any\n * OTel collector.\n *\n * Both endpoints are authenticated with a bearer token + a tenant id\n * header. Tenants isolate everything downstream of ingest; no tenant\n * ever sees another tenant's data.\n */\n\nimport type { GateDecision, MutableSurface } from '../campaign/types'\nimport type { InsightReport } from '../contract/insight-report'\nimport type { RunTerminalOutcome } from '../run-record'\n\n// re-export so wire-format consumers can import the optional payload type\n// from `@tangle-network/agent-eval/hosted` without reaching into /contract.\nexport type { InsightReport } from '../contract/insight-report'\n\nexport const HOSTED_WIRE_VERSION = '2026-07-24.v1' as const\nexport type HostedWireVersion = typeof HOSTED_WIRE_VERSION\n\n// ── Transport headers ───────────────────────────────────────────────\n\n/** Every ingest request carries these. */\nexport interface HostedIngestHeaders {\n /** Bearer token. The orchestrator validates against the tenant key. */\n authorization: `Bearer ${string}`\n /** Stable tenant id (the orchestrator-side primary key for the tenant). */\n 'x-tangle-tenant-id': string\n /** Wire-version pin so the server can reject incompatible payloads. */\n 'x-tangle-wire-version': HostedWireVersion\n /** Stable request key generated once and reused across retries. */\n 'idempotency-key': string\n}\n\n// ── Eval-run event ──────────────────────────────────────────────────\n\n/** Lifecycle stages of an eval-run as the substrate reports them. */\nexport type EvalRunStatus =\n | 'started'\n | 'baseline-complete'\n | 'generation-complete'\n | 'gate-decided'\n | 'finished'\n | 'errored'\n\nexport interface EvalRunCellScore {\n /** Stable scenario id from the consumer's scenario set. */\n scenarioId: string\n /** Repetition index when reps > 1; 0 for the default. */\n rep: number\n /** Composite score across successful judges, or null when unscored. */\n compositeMean: number | null\n /** Per-judge and per-dimension scores; failed or missing judges are absent. */\n dimensions: Record<string, Record<string, number>>\n /** Root execution result, kept separate from task quality. */\n terminalOutcome: RunTerminalOutcome\n /** Canonical execution-error count, or null when the producer did not measure it. */\n executionErrorCount: number | null\n /** Per-cell dispatch or judge error. Missing on success. */\n errorMessage?: string\n}\n\nexport interface EvalRunGenerationSnapshot {\n /** Generation index. 0 is baseline. */\n index: number\n /** Candidate surface fingerprint (stable hash) — pivot key into the\n * trace stream to fetch the underlying execution. */\n surfaceHash: string\n /** The candidate surface itself. May be omitted to avoid PII when the\n * consumer prefers not to ship verbatim prompts. */\n surface?: MutableSurface\n /** Per-cell scores for this generation. */\n cells: EvalRunCellScore[]\n /** Mean across scored cells, or null when no cell has a task-quality label. */\n compositeMean: number | null\n /** Total $ spent across this generation. */\n costUsd: number\n /** Wall-clock duration of this generation. */\n durationMs: number\n}\n\n/**\n * The top-level eval-run event. One ingest call per logical eval-run;\n * generations stream in incrementally via repeated calls with the same\n * `runId`. The orchestrator deduplicates by `(runId, generation.index)`.\n */\nexport interface EvalRunEvent {\n /** Stable run id (the substrate's `runId`). UUID or substrate-generated. */\n runId: string\n /** Where this run was happening — derived from `RunCampaignOptions.runDir`. */\n runDir: string\n /** ISO-8601 timestamp the substrate recorded the event. */\n timestamp: string\n /** Lifecycle stage this event represents. */\n status: EvalRunStatus\n /** Free-form consumer tags (env, branch, model id, etc.). Searchable. */\n labels: Record<string, string>\n /** Baseline campaign snapshot. Present when status >= baseline-complete. */\n baseline?: EvalRunGenerationSnapshot\n /** Per-generation snapshots. Streams in; orchestrator appends. */\n generations: EvalRunGenerationSnapshot[]\n /** Final gate decision. Present when status >= gate-decided. */\n gateDecision?: GateDecision\n /** Held-out lift = winner-on-holdout - baseline-on-holdout. */\n holdoutLift?: number\n /** Total $ spent across baseline + every generation. */\n totalCostUsd: number\n /** Total wall-clock duration. */\n totalDurationMs: number\n /** Error message if status === 'errored'. */\n errorMessage?: string\n /** Rigor packet emitted alongside the run — distributional summary,\n * paired-bootstrap lift CI, judge stats, inter-rater agreement,\n * contamination check, failure clusters (when an analyst is wired),\n * outcome correlation (when downstream signal is supplied), and the\n * recommendations the dashboard surfaces verbatim. */\n insightReport?: InsightReport\n}\n\n// ── Trace span event ────────────────────────────────────────────────\n\n/**\n * Canonical unsigned 64-bit integer encoded as a base-10 string.\n * JSON numbers cannot represent OTLP nanosecond timestamps exactly.\n */\nexport type UnixNanoTimestamp = string\n\n/**\n * OTel-shape span with a few additional attributes for eval-run pivoting.\n * Compatible with any OTLP collector — `name`, `traceId`, `spanId`,\n * `startTimeUnixNano`, `endTimeUnixNano`, `attributes` are stock OTel.\n */\nexport interface TraceSpanEvent {\n traceId: string\n spanId: string\n parentSpanId?: string\n name: string\n startTimeUnixNano: UnixNanoTimestamp\n endTimeUnixNano: UnixNanoTimestamp\n attributes: Record<string, string | number | boolean>\n events?: Array<{\n timeUnixNano: UnixNanoTimestamp\n name: string\n attributes?: Record<string, string | number | boolean>\n }>\n status?: { code: 'OK' | 'ERROR' | 'UNSET'; message?: string }\n /** Pivot back into the eval-run stream. */\n 'tangle.runId'?: string\n /** Pivot to the specific generation. */\n 'tangle.generation'?: number\n /** Pivot to the specific cell. */\n 'tangle.cellId'?: string\n /** Pivot to the specific scenario. */\n 'tangle.scenarioId'?: string\n}\n\n// ── Ingest request bodies ───────────────────────────────────────────\n\nexport interface IngestEvalRunsRequest {\n wireVersion: HostedWireVersion\n events: EvalRunEvent[]\n}\n\nexport interface IngestTracesRequest {\n wireVersion: HostedWireVersion\n spans: TraceSpanEvent[]\n}\n\nexport interface IngestResponse {\n /** Accepted events / spans count. */\n accepted: number\n /** Rejected events with reasons (validation failures, dup idempotency key, etc.). */\n rejected: Array<{ index: number; reason: string }>\n}\n","import { z } from 'zod'\nimport type { MutableSurface } from '../campaign/types'\nimport type { InsightReport } from '../contract/insight-report'\nimport type {\n EvalRunCellScore,\n EvalRunEvent,\n EvalRunGenerationSnapshot,\n IngestEvalRunsRequest,\n IngestResponse,\n IngestTracesRequest,\n TraceSpanEvent,\n UnixNanoTimestamp,\n} from './types'\nimport { HOSTED_WIRE_VERSION } from './types'\n\nconst finiteNumber = z.number().finite()\nconst nonNegativeNumber = finiteNumber.nonnegative()\nconst nonNegativeInteger = z.number().int().nonnegative()\nconst nonEmptyString = z\n .string()\n .min(1)\n .refine((value) => value.trim().length > 0, 'must not be blank')\nconst attributeValue = z.union([z.string(), finiteNumber, z.boolean()])\nconst attributes = z.record(z.string(), attributeValue)\nconst UINT64_MAX = 18_446_744_073_709_551_615n\n\nexport const UnixNanoTimestampSchema: z.ZodType<UnixNanoTimestamp> = z\n .string()\n .regex(/^(0|[1-9][0-9]*)$/, 'expected an unsigned base-10 integer string')\n .refine((value) => BigInt(value) <= UINT64_MAX, 'must fit in an unsigned 64-bit integer')\n\nconst GainDistributionBinSchema = z\n .object({\n lo: finiteNumber,\n hi: finiteNumber,\n count: nonNegativeInteger,\n })\n .strict()\n\nconst ScalarDistributionSchema = z\n .object({\n n: nonNegativeInteger,\n mean: finiteNumber.nullable(),\n p50: finiteNumber.nullable(),\n p95: finiteNumber.nullable(),\n stddev: nonNegativeNumber.nullable(),\n min: finiteNumber.nullable(),\n max: finiteNumber.nullable(),\n histogram: z.array(GainDistributionBinSchema),\n tailRuns: z\n .array(\n z\n .object({\n runId: nonEmptyString,\n score: finiteNumber,\n })\n .strict(),\n )\n .optional(),\n })\n .strict()\n .superRefine((distribution, ctx) => {\n const values = [\n distribution.mean,\n distribution.p50,\n distribution.p95,\n distribution.stddev,\n distribution.min,\n distribution.max,\n ]\n if (distribution.n === 0 && values.some((value) => value !== null)) {\n ctx.addIssue({\n code: 'custom',\n message: 'distribution values must be null when n is 0',\n })\n }\n if (distribution.n > 0 && values.some((value) => value === null)) {\n ctx.addIssue({\n code: 'custom',\n message: 'distribution values must be numbers when n is greater than 0',\n })\n }\n if (\n distribution.min !== null &&\n distribution.max !== null &&\n distribution.min > distribution.max\n ) {\n ctx.addIssue({ code: 'custom', path: ['min'], message: 'min must not exceed max' })\n }\n })\n\nconst TokenUsageInsightSchema = z\n .object({\n input: ScalarDistributionSchema,\n output: ScalarDistributionSchema,\n reasoning: ScalarDistributionSchema,\n cached: ScalarDistributionSchema,\n cacheWrite: ScalarDistributionSchema,\n totals: z\n .object({\n input: nonNegativeNumber,\n output: nonNegativeNumber,\n reasoning: nonNegativeNumber,\n cached: nonNegativeNumber,\n cacheWrite: nonNegativeNumber,\n })\n .strict(),\n })\n .strict()\n\nconst ExecutionErrorOutcomeCellSchema = z\n .object({\n withErrors: nonNegativeInteger,\n withoutErrors: nonNegativeInteger,\n unreported: nonNegativeInteger,\n })\n .strict()\n\nconst ExecutionInsightSchema = z\n .object({\n durationMs: ScalarDistributionSchema,\n queueMs: ScalarDistributionSchema,\n tokenUsage: TokenUsageInsightSchema,\n aggregateUsage: z\n .object({\n runs: nonNegativeInteger,\n tokenUsage: TokenUsageInsightSchema,\n costUsd: ScalarDistributionSchema,\n totalCostUsd: nonNegativeNumber,\n })\n .strict(),\n models: z.array(\n z\n .object({\n model: nonEmptyString,\n runs: nonNegativeInteger,\n })\n .strict(),\n ),\n modelCalls: z\n .object({\n runs: nonNegativeInteger,\n events: nonNegativeInteger,\n reportingRuns: nonNegativeInteger,\n })\n .strict(),\n executionErrors: z\n .object({\n runs: nonNegativeInteger,\n fraction: finiteNumber.min(0).max(1).nullable(),\n events: nonNegativeInteger,\n reportingRuns: nonNegativeInteger,\n errorSpanEvents: nonNegativeInteger,\n errorSpanReportingRuns: nonNegativeInteger,\n byTerminalOutcome: z\n .object({\n succeeded: ExecutionErrorOutcomeCellSchema,\n failed: ExecutionErrorOutcomeCellSchema,\n cancelled: ExecutionErrorOutcomeCellSchema,\n incomplete: ExecutionErrorOutcomeCellSchema,\n unknown: ExecutionErrorOutcomeCellSchema,\n })\n .strict(),\n })\n .strict(),\n terminalOutcomes: z\n .object({\n succeeded: nonNegativeInteger,\n failed: nonNegativeInteger,\n cancelled: nonNegativeInteger,\n incomplete: nonNegativeInteger,\n unknown: nonNegativeInteger,\n })\n .strict(),\n })\n .strict()\n\nconst CostProvenanceSummarySchema = z\n .object({\n observed: z\n .object({\n n: nonNegativeInteger,\n totalUsd: nonNegativeNumber,\n })\n .strict(),\n estimated: z\n .object({\n n: nonNegativeInteger,\n totalUsd: nonNegativeNumber,\n })\n .strict(),\n uncaptured: z.object({ n: nonNegativeInteger }).strict(),\n knownFraction: finiteNumber.min(0).max(1),\n })\n .strict()\n\nconst ParetoFigureSpecSchema = z\n .object({\n kind: z.literal('pareto-cost-quality'),\n split: z.enum(['search', 'holdout']),\n points: z.array(\n z\n .object({\n candidateId: nonEmptyString,\n cost: nonNegativeNumber,\n quality: finiteNumber,\n n: nonNegativeInteger,\n onFrontier: z.boolean(),\n gate: z.enum(['promote', 'reject']).optional(),\n })\n .strict(),\n ),\n axes: z.object({ x: z.literal('costUsd'), y: z.literal('score') }).strict(),\n })\n .strict()\n\nconst ContinuousAgreementSchema = z\n .object({\n weightedKappa: finiteNumber,\n icc: finiteNumber,\n pearson: finiteNumber,\n spearman: finiteNumber,\n ci: z\n .object({\n icc: z.tuple([finiteNumber, finiteNumber]),\n weightedKappa: z.tuple([finiteNumber, finiteNumber]),\n })\n .strict(),\n n: nonNegativeInteger,\n raters: nonNegativeInteger,\n })\n .strict()\n\nconst JudgeInsightSchema = z\n .object({\n n: nonNegativeInteger,\n meanScore: finiteNumber,\n calibration: ContinuousAgreementSchema.optional(),\n positionalBias: finiteNumber.optional(),\n selfPreference: finiteNumber.optional(),\n verbosityBias: finiteNumber.optional(),\n })\n .strict()\n\nconst InterRaterInsightSchema = z\n .object({\n raters: nonNegativeInteger,\n jointlyRated: nonNegativeInteger,\n kappa: finiteNumber,\n icc: finiteNumber,\n pearson: finiteNumber,\n spearman: finiteNumber,\n perPair: z.record(z.string(), finiteNumber),\n disagreementCases: z.array(\n z\n .object({\n runId: nonEmptyString,\n ratings: z.array(\n z\n .object({\n rater: nonEmptyString,\n score: finiteNumber,\n })\n .strict(),\n ),\n range: nonNegativeNumber,\n })\n .strict(),\n ),\n })\n .strict()\n\nconst LiftInsightSchema = z\n .object({\n baselineMean: finiteNumber,\n candidateMean: finiteNumber,\n delta: finiteNumber,\n ci95: z.tuple([finiteNumber, finiteNumber]),\n pValue: finiteNumber.min(0).max(1),\n n: nonNegativeInteger,\n unpairedBaseline: nonNegativeInteger,\n unpairedCandidate: nonNegativeInteger,\n cohensD: finiteNumber.nullable(),\n mde: nonNegativeNumber,\n requiredN: nonNegativeInteger.nullable(),\n })\n .strict()\n\nconst FailureClusterInsightSchema = z\n .object({\n clusters: z.array(\n z\n .object({\n id: nonEmptyString,\n name: nonEmptyString,\n share: finiteNumber.min(0).max(1),\n exemplars: z.array(nonEmptyString).max(5),\n suggestedFix: nonEmptyString.optional(),\n })\n .strict(),\n ),\n totalFailures: nonNegativeInteger,\n })\n .strict()\n\nconst ContaminationInsightSchema = z\n .object({\n leaks: nonNegativeInteger,\n holdoutAuditPassed: z.boolean(),\n details: z\n .array(\n z\n .object({\n runId: nonEmptyString,\n canary: nonEmptyString,\n matched: nonEmptyString,\n })\n .strict(),\n )\n .optional(),\n })\n .strict()\n\nconst OutcomeCorrelationInsightSchema = z\n .object({\n metric: nonEmptyString,\n n: nonNegativeInteger,\n pearson: finiteNumber,\n spearman: finiteNumber,\n rewardModel: z\n .object({\n intercept: finiteNumber,\n slope: finiteNumber,\n r2: finiteNumber,\n })\n .strict()\n .optional(),\n })\n .strict()\n\nconst ReleaseSummarySchema = z\n .object({\n status: z.enum(['pass', 'warn', 'fail']),\n axes: z.array(\n z\n .object({\n name: z.enum(['quality-lift', 'contamination', 'composite-distribution']),\n status: z.enum(['pass', 'warn', 'fail', 'not_evaluated']),\n detail: nonEmptyString,\n })\n .strict(),\n ),\n issues: z.array(z.string()),\n })\n .strict()\n\nconst MetricDeltaSchema = z\n .object({\n current: finiteNumber,\n baseline: finiteNumber,\n delta: finiteNumber,\n ci95: z.tuple([finiteNumber, finiteNumber]),\n pValue: finiteNumber.min(0).max(1),\n cohensD: finiteNumber,\n baselineN: nonNegativeInteger,\n currentN: nonNegativeInteger,\n significant: z.boolean(),\n })\n .strict()\n\nconst PriorPeriodComparisonSchema = z\n .object({\n baselineN: nonNegativeInteger,\n currentN: nonNegativeInteger,\n windowLabel: nonEmptyString.optional(),\n metrics: z.record(z.string(), MetricDeltaSchema),\n regressedMetrics: z.array(nonEmptyString),\n improvedMetrics: z.array(nonEmptyString),\n })\n .strict()\n\nconst RecommendationSchema = z\n .object({\n priority: z.enum(['critical', 'high', 'medium', 'low']),\n kind: z.enum(['ship', 'hold', 'investigate', 'fix', 'recalibrate', 'expand-corpus']),\n title: nonEmptyString,\n detail: nonEmptyString,\n evidencePath: nonEmptyString.optional(),\n })\n .strict()\n\nexport const InsightReportSchema: z.ZodType<InsightReport> = z\n .object({\n n: nonNegativeInteger,\n execution: ExecutionInsightSchema,\n composite: ScalarDistributionSchema,\n perDimension: z.record(z.string(), ScalarDistributionSchema),\n costQuality: z\n .object({\n cost: ScalarDistributionSchema,\n pareto: ParetoFigureSpecSchema,\n provenance: CostProvenanceSummarySchema.optional(),\n degraded: z\n .object({\n cost: nonEmptyString.optional(),\n pareto: nonEmptyString.optional(),\n })\n .strict()\n .optional(),\n })\n .strict(),\n judges: z.record(z.string(), JudgeInsightSchema),\n interRater: InterRaterInsightSchema.optional(),\n lift: LiftInsightSchema.optional(),\n failureClusters: FailureClusterInsightSchema.optional(),\n contamination: ContaminationInsightSchema.optional(),\n outcomeCorrelation: OutcomeCorrelationInsightSchema.optional(),\n release: ReleaseSummarySchema,\n priorPeriodComparison: PriorPeriodComparisonSchema.optional(),\n failureModes: z\n .array(\n z\n .object({\n mode: nonEmptyString,\n count: nonNegativeInteger,\n share: finiteNumber.min(0).max(1),\n })\n .strict(),\n )\n .optional(),\n recommendations: z.array(RecommendationSchema),\n })\n .strict()\n\nconst sha256Digest = z.custom<`sha256:${string}`>(\n (value) => typeof value === 'string' && /^sha256:[0-9a-f]{64}$/.test(value),\n 'expected sha256:<64 lowercase hex characters>',\n)\n\nexport const MutableSurfaceSchema: z.ZodType<MutableSurface> = z.union([\n z.string(),\n z\n .object({\n kind: z.literal('components'),\n components: z.record(z.string(), z.string()),\n })\n .strict(),\n z\n .object({\n kind: z.literal('code'),\n worktreeRef: nonEmptyString,\n baseRef: nonEmptyString,\n baseCommit: nonEmptyString,\n baseTree: nonEmptyString,\n candidateCommit: nonEmptyString,\n candidateTree: nonEmptyString,\n patch: z\n .object({\n format: z.literal('git-diff-binary'),\n sha256: sha256Digest,\n byteLength: nonNegativeInteger,\n })\n .strict(),\n summary: z.string().optional(),\n })\n .strict(),\n])\n\nexport const RunTerminalOutcomeSchema = z.enum([\n 'succeeded',\n 'failed',\n 'cancelled',\n 'incomplete',\n 'unknown',\n])\n\nexport const EvalRunCellScoreSchema: z.ZodType<EvalRunCellScore> = z\n .object({\n scenarioId: nonEmptyString,\n rep: nonNegativeInteger,\n compositeMean: finiteNumber.nullable(),\n dimensions: z.record(z.string(), z.record(z.string(), finiteNumber)),\n terminalOutcome: RunTerminalOutcomeSchema,\n executionErrorCount: nonNegativeInteger.nullable(),\n errorMessage: z.string().optional(),\n })\n .strict()\n\nexport const EvalRunGenerationSnapshotSchema: z.ZodType<EvalRunGenerationSnapshot> = z\n .object({\n index: nonNegativeInteger,\n surfaceHash: nonEmptyString,\n surface: MutableSurfaceSchema.optional(),\n cells: z.array(EvalRunCellScoreSchema),\n compositeMean: finiteNumber.nullable(),\n costUsd: nonNegativeNumber,\n durationMs: nonNegativeNumber,\n })\n .strict()\n\nconst EvalRunStatusSchema = z.enum([\n 'started',\n 'baseline-complete',\n 'generation-complete',\n 'gate-decided',\n 'finished',\n 'errored',\n])\n\nconst GateDecisionSchema = z.enum([\n 'ship',\n 'hold',\n 'need_more_work',\n 'model_ceiling',\n 'arch_ceiling',\n])\n\nexport const EvalRunEventSchema: z.ZodType<EvalRunEvent> = z\n .object({\n runId: nonEmptyString,\n runDir: nonEmptyString,\n timestamp: z.string().datetime({ offset: true }),\n status: EvalRunStatusSchema,\n labels: z.record(z.string(), z.string()),\n baseline: EvalRunGenerationSnapshotSchema.optional(),\n generations: z.array(EvalRunGenerationSnapshotSchema),\n gateDecision: GateDecisionSchema.optional(),\n holdoutLift: finiteNumber.optional(),\n totalCostUsd: nonNegativeNumber,\n totalDurationMs: nonNegativeNumber,\n errorMessage: z.string().optional(),\n insightReport: InsightReportSchema.optional(),\n })\n .strict()\n .superRefine((event, ctx) => {\n if (event.baseline && event.baseline.index !== 0) {\n ctx.addIssue({\n code: 'custom',\n path: ['baseline', 'index'],\n message: 'baseline index must be 0',\n })\n }\n\n const seen = new Set<number>()\n for (let i = 0; i < event.generations.length; i++) {\n const index = event.generations[i]!.index\n if (seen.has(index)) {\n ctx.addIssue({\n code: 'custom',\n path: ['generations', i, 'index'],\n message: `duplicate generation index ${index}`,\n })\n }\n seen.add(index)\n }\n\n if (event.status === 'errored' && !event.errorMessage?.trim()) {\n ctx.addIssue({\n code: 'custom',\n path: ['errorMessage'],\n message: 'errorMessage is required when status is errored',\n })\n }\n })\n\nconst TraceSpanEventEntrySchema = z\n .object({\n timeUnixNano: UnixNanoTimestampSchema,\n name: nonEmptyString,\n attributes: attributes.optional(),\n })\n .strict()\n\nexport const TraceSpanEventSchema: z.ZodType<TraceSpanEvent> = z\n .object({\n traceId: nonEmptyString,\n spanId: nonEmptyString,\n parentSpanId: nonEmptyString.optional(),\n name: nonEmptyString,\n startTimeUnixNano: UnixNanoTimestampSchema,\n endTimeUnixNano: UnixNanoTimestampSchema,\n attributes,\n events: z.array(TraceSpanEventEntrySchema).optional(),\n status: z\n .object({\n code: z.enum(['OK', 'ERROR', 'UNSET']),\n message: z.string().optional(),\n })\n .strict()\n .optional(),\n 'tangle.runId': nonEmptyString.optional(),\n 'tangle.generation': nonNegativeInteger.optional(),\n 'tangle.cellId': nonEmptyString.optional(),\n 'tangle.scenarioId': nonEmptyString.optional(),\n })\n .strict()\n .refine((span) => BigInt(span.endTimeUnixNano) >= BigInt(span.startTimeUnixNano), {\n path: ['endTimeUnixNano'],\n message: 'endTimeUnixNano must be greater than or equal to startTimeUnixNano',\n })\n\nexport const IngestEvalRunsEnvelopeSchema = z\n .object({\n wireVersion: z.literal(HOSTED_WIRE_VERSION),\n events: z.array(z.unknown()),\n })\n .strict()\n\nexport const IngestTracesEnvelopeSchema = z\n .object({\n wireVersion: z.literal(HOSTED_WIRE_VERSION),\n spans: z.array(z.unknown()),\n })\n .strict()\n\nexport const IngestEvalRunsRequestSchema: z.ZodType<IngestEvalRunsRequest> = z\n .object({\n wireVersion: z.literal(HOSTED_WIRE_VERSION),\n events: z.array(EvalRunEventSchema),\n })\n .strict()\n\nexport const IngestTracesRequestSchema: z.ZodType<IngestTracesRequest> = z\n .object({\n wireVersion: z.literal(HOSTED_WIRE_VERSION),\n spans: z.array(TraceSpanEventSchema),\n })\n .strict()\n\nexport const IngestResponseSchema: z.ZodType<IngestResponse> = z\n .object({\n accepted: nonNegativeInteger,\n rejected: z.array(\n z\n .object({\n index: nonNegativeInteger,\n reason: nonEmptyString,\n })\n .strict(),\n ),\n })\n .strict()\n","/**\n * # Hosted-tier ingest client.\n *\n * Ships eval-run events + trace spans to any orchestrator (ours, a\n * partner's self-hosted one, or a future open implementation) that\n * speaks the wire format in `./types.ts`.\n *\n * Three modes:\n * - **Ours:** point at `https://orchestrator.tangle.tools` (the host root —\n * the client appends the versioned `/v1/ingest/...` path itself; a trailing\n * `/v1` on the endpoint is tolerated and normalized away). We handle ingest\n * + storage + dashboard.\n * - **Self-hosted:** point at whatever URL runs the reference receiver\n * from `examples/hosted-ingest-server/`.\n * - **Off (default):** when `hostedTenant` is unset, nothing is sent.\n * Everything stays local.\n */\n\nimport {\n IngestEvalRunsRequestSchema,\n IngestResponseSchema,\n IngestTracesRequestSchema,\n} from './schemas'\nimport {\n type EvalRunEvent,\n HOSTED_WIRE_VERSION,\n type HostedWireVersion,\n type IngestEvalRunsRequest,\n type IngestResponse,\n type IngestTracesRequest,\n type TraceSpanEvent,\n} from './types'\n\nexport interface HostedTenant {\n /** Orchestrator endpoint base URL (no trailing slash). Required. */\n endpoint: string\n /** Bearer token issued by the orchestrator. Required. */\n apiKey: string\n /** Tenant id — the orchestrator's primary key for this consumer. Required. */\n tenantId: string\n /** Optional `fetch` override (auth wrappers, custom agent, test mocks). */\n fetchImpl?: typeof fetch\n /** Per-call timeout in ms. Default 30s. */\n timeoutMs?: number\n /** Retries on 5xx / network errors. Default 2. */\n retries?: number\n}\n\nexport interface HostedClient {\n ingestEvalRun(event: EvalRunEvent, idempotencyKey?: string): Promise<IngestResponse>\n ingestEvalRuns(events: EvalRunEvent[], idempotencyKey?: string): Promise<IngestResponse>\n ingestTraces(spans: TraceSpanEvent[], idempotencyKey?: string): Promise<IngestResponse>\n readonly tenant: HostedTenant\n readonly wireVersion: HostedWireVersion\n}\n\ninterface RequestOptions {\n idempotencyKey?: string\n signal?: AbortSignal\n}\n\nconst MAX_IDEMPOTENCY_KEY_LENGTH = 256\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n\nfunction normalizeHostedBase(endpoint: string): string {\n return endpoint.trim().replace(/\\/+$/, '').replace(/\\/v1$/, '')\n}\n\nfunction resolveIdempotencyKey(key: string | undefined): string {\n const resolved = key ?? globalThis.crypto.randomUUID()\n if (resolved.trim().length === 0) throw new Error('idempotency key must not be blank')\n if (resolved.length > MAX_IDEMPOTENCY_KEY_LENGTH) {\n throw new Error(`idempotency key must be at most ${MAX_IDEMPOTENCY_KEY_LENGTH} characters`)\n }\n return resolved\n}\n\nfunction responseValidationReason(error: {\n issues: Array<{ path: PropertyKey[]; message: string }>\n}) {\n return error.issues\n .map(\n (issue) =>\n `${issue.path.length > 0 ? issue.path.map(String).join('.') : 'value'}: ${issue.message}`,\n )\n .join('; ')\n}\n\nasync function post<TReq>(\n tenant: HostedTenant,\n path: string,\n body: TReq,\n opts: RequestOptions = {},\n): Promise<IngestResponse> {\n const timeoutMs = tenant.timeoutMs ?? 30_000\n const maxRetries = tenant.retries ?? 2\n const f: typeof fetch = tenant.fetchImpl ?? ((...args) => fetch(...args))\n const base = normalizeHostedBase(tenant.endpoint)\n const url = `${base}${path}`\n const idempotencyKey = resolveIdempotencyKey(opts.idempotencyKey)\n const headers: Record<string, string> = {\n 'content-type': 'application/json',\n authorization: `Bearer ${tenant.apiKey}`,\n 'x-tangle-tenant-id': tenant.tenantId,\n 'x-tangle-wire-version': HOSTED_WIRE_VERSION,\n 'idempotency-key': idempotencyKey,\n }\n\n let lastError: unknown\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n const ourTimeout = AbortSignal.timeout(timeoutMs)\n const combinedSignal = opts.signal ? AbortSignal.any([opts.signal, ourTimeout]) : ourTimeout\n let res: Response\n try {\n res = await f(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n signal: combinedSignal,\n })\n } catch (err) {\n if (opts.signal?.aborted) throw err\n lastError = err\n if (attempt === maxRetries) throw err\n await sleep(2 ** attempt * 200 + Math.random() * 200)\n continue\n }\n\n if (!res.ok) {\n const text = await res.text().catch(() => '')\n const error = new Error(`hosted ingest ${url} failed (${res.status}): ${text.slice(0, 500)}`)\n const retryable = res.status >= 500 || res.status === 408 || res.status === 429\n if (!retryable || attempt === maxRetries) throw error\n lastError = error\n await sleep(2 ** attempt * 200 + Math.random() * 200)\n continue\n }\n\n let rawResponse: unknown\n try {\n rawResponse = await res.json()\n } catch (error) {\n throw new Error(`hosted ingest ${url} returned invalid JSON`, { cause: error })\n }\n const parsed = IngestResponseSchema.safeParse(rawResponse)\n if (!parsed.success) {\n throw new Error(\n `hosted ingest ${url} returned an invalid response: ${responseValidationReason(parsed.error)}`,\n )\n }\n return parsed.data\n }\n throw lastError ?? new Error('hosted ingest exhausted retries')\n}\n\nexport function createHostedClient(tenant: HostedTenant): HostedClient {\n if (normalizeHostedBase(tenant.endpoint).length === 0) throw new Error('endpoint is required')\n if (tenant.apiKey.trim().length === 0) throw new Error('apiKey is required')\n if (tenant.tenantId.trim().length === 0) throw new Error('tenantId is required')\n if (\n tenant.timeoutMs !== undefined &&\n (!Number.isFinite(tenant.timeoutMs) || tenant.timeoutMs <= 0)\n ) {\n throw new Error('timeoutMs must be greater than 0')\n }\n if (tenant.retries !== undefined && (!Number.isInteger(tenant.retries) || tenant.retries < 0)) {\n throw new Error('retries must be a non-negative integer')\n }\n\n return {\n tenant,\n wireVersion: HOSTED_WIRE_VERSION,\n\n async ingestEvalRun(event, idempotencyKey) {\n return this.ingestEvalRuns([event], idempotencyKey)\n },\n\n async ingestEvalRuns(events, idempotencyKey) {\n const body: IngestEvalRunsRequest = IngestEvalRunsRequestSchema.parse({\n wireVersion: HOSTED_WIRE_VERSION,\n events,\n })\n return post<IngestEvalRunsRequest>(tenant, '/v1/ingest/eval-runs', body, {\n idempotencyKey,\n })\n },\n\n async ingestTraces(spans, idempotencyKey) {\n const body: IngestTracesRequest = IngestTracesRequestSchema.parse({\n wireVersion: HOSTED_WIRE_VERSION,\n spans,\n })\n return post<IngestTracesRequest>(tenant, '/v1/ingest/traces', body, {\n idempotencyKey,\n })\n },\n }\n}\n\n/**\n * Build a `HostedClient` from environment, or `undefined` when ingest is not\n * configured — the canonical, fail-soft wiring every product uses so eval-run +\n * trace provenance lands in the Intelligence dashboard with ONE call:\n *\n * const hosted = hostedClientFromEnv()\n * // ...run the loop...\n * await emitLoopProvenance({ ..., hostedClient: hosted }) // no-op if undefined\n *\n * Returns `undefined` (NOT an error) when any of endpoint / apiKey / tenantId is\n * missing — so a product wires the ship call unconditionally and it stays a\n * no-op until the env is set. Env precedence:\n * - endpoint: `TANGLE_INGEST_URL` → `TANGLE_ORCHESTRATOR_URL`\n * - apiKey: `TANGLE_INGEST_API_KEY` → `TANGLE_API_KEY`\n * - tenantId: `TANGLE_TENANT_ID`\n * A trailing slash on the endpoint is stripped. Pass `overrides` to supply any\n * field directly (e.g. a fixed `tenantId` per product) — overrides win over env.\n */\n/**\n * Build a {@link HostedTenant} config from env — the input `selfImprove`'s\n * `hostedTenant` and `emitLoopProvenance` take. Same env precedence + overrides\n * as {@link hostedClientFromEnv}; returns `undefined` (not an error) when any of\n * endpoint / apiKey / tenantId is missing, so a product wires\n * `hostedTenant: hostedTenantFromEnv({ tenantId: 'my-agent' })` unconditionally\n * and it stays off until the env is set.\n */\nexport function hostedTenantFromEnv(\n overrides: Partial<HostedTenant> & { env?: Record<string, string | undefined> } = {},\n): HostedTenant | undefined {\n const env = overrides.env ?? process.env\n const endpoint = (\n overrides.endpoint ??\n env.TANGLE_INGEST_URL ??\n env.TANGLE_ORCHESTRATOR_URL\n )?.trim()\n const apiKey = (overrides.apiKey ?? env.TANGLE_INGEST_API_KEY ?? env.TANGLE_API_KEY)?.trim()\n const tenantId = (overrides.tenantId ?? env.TANGLE_TENANT_ID)?.trim()\n if (!endpoint || !apiKey || !tenantId) return undefined\n const tenant: HostedTenant = { endpoint: endpoint.replace(/\\/+$/, ''), apiKey, tenantId }\n if (overrides.fetchImpl) tenant.fetchImpl = overrides.fetchImpl\n if (overrides.timeoutMs !== undefined) tenant.timeoutMs = overrides.timeoutMs\n if (overrides.retries !== undefined) tenant.retries = overrides.retries\n return tenant\n}\n\nexport function hostedClientFromEnv(\n overrides: Partial<HostedTenant> & { env?: Record<string, string | undefined> } = {},\n): HostedClient | undefined {\n const tenant = hostedTenantFromEnv(overrides)\n return tenant ? createHostedClient(tenant) : undefined\n}\n"],"mappings":";AAgCO,IAAM,sBAAsB;;;AChCnC,SAAS,SAAS;AAelB,IAAM,eAAe,EAAE,OAAO,EAAE,OAAO;AACvC,IAAM,oBAAoB,aAAa,YAAY;AACnD,IAAM,qBAAqB,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY;AACxD,IAAM,iBAAiB,EACpB,OAAO,EACP,IAAI,CAAC,EACL,OAAO,CAAC,UAAU,MAAM,KAAK,EAAE,SAAS,GAAG,mBAAmB;AACjE,IAAM,iBAAiB,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,cAAc,EAAE,QAAQ,CAAC,CAAC;AACtE,IAAM,aAAa,EAAE,OAAO,EAAE,OAAO,GAAG,cAAc;AACtD,IAAM,aAAa;AAEZ,IAAM,0BAAwD,EAClE,OAAO,EACP,MAAM,qBAAqB,6CAA6C,EACxE,OAAO,CAAC,UAAU,OAAO,KAAK,KAAK,YAAY,wCAAwC;AAE1F,IAAM,4BAA4B,EAC/B,OAAO;AAAA,EACN,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,OAAO;AACT,CAAC,EACA,OAAO;AAEV,IAAM,2BAA2B,EAC9B,OAAO;AAAA,EACN,GAAG;AAAA,EACH,MAAM,aAAa,SAAS;AAAA,EAC5B,KAAK,aAAa,SAAS;AAAA,EAC3B,KAAK,aAAa,SAAS;AAAA,EAC3B,QAAQ,kBAAkB,SAAS;AAAA,EACnC,KAAK,aAAa,SAAS;AAAA,EAC3B,KAAK,aAAa,SAAS;AAAA,EAC3B,WAAW,EAAE,MAAM,yBAAyB;AAAA,EAC5C,UAAU,EACP;AAAA,IACC,EACG,OAAO;AAAA,MACN,OAAO;AAAA,MACP,OAAO;AAAA,IACT,CAAC,EACA,OAAO;AAAA,EACZ,EACC,SAAS;AACd,CAAC,EACA,OAAO,EACP,YAAY,CAAC,cAAc,QAAQ;AAClC,QAAM,SAAS;AAAA,IACb,aAAa;AAAA,IACb,aAAa;AAAA,IACb,aAAa;AAAA,IACb,aAAa;AAAA,IACb,aAAa;AAAA,IACb,aAAa;AAAA,EACf;AACA,MAAI,aAAa,MAAM,KAAK,OAAO,KAAK,CAAC,UAAU,UAAU,IAAI,GAAG;AAClE,QAAI,SAAS;AAAA,MACX,MAAM;AAAA,MACN,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AACA,MAAI,aAAa,IAAI,KAAK,OAAO,KAAK,CAAC,UAAU,UAAU,IAAI,GAAG;AAChE,QAAI,SAAS;AAAA,MACX,MAAM;AAAA,MACN,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AACA,MACE,aAAa,QAAQ,QACrB,aAAa,QAAQ,QACrB,aAAa,MAAM,aAAa,KAChC;AACA,QAAI,SAAS,EAAE,MAAM,UAAU,MAAM,CAAC,KAAK,GAAG,SAAS,0BAA0B,CAAC;AAAA,EACpF;AACF,CAAC;AAEH,IAAM,0BAA0B,EAC7B,OAAO;AAAA,EACN,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,QAAQ,EACL,OAAO;AAAA,IACN,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,QAAQ;AAAA,IACR,YAAY;AAAA,EACd,CAAC,EACA,OAAO;AACZ,CAAC,EACA,OAAO;AAEV,IAAM,kCAAkC,EACrC,OAAO;AAAA,EACN,YAAY;AAAA,EACZ,eAAe;AAAA,EACf,YAAY;AACd,CAAC,EACA,OAAO;AAEV,IAAM,yBAAyB,EAC5B,OAAO;AAAA,EACN,YAAY;AAAA,EACZ,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,gBAAgB,EACb,OAAO;AAAA,IACN,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,cAAc;AAAA,EAChB,CAAC,EACA,OAAO;AAAA,EACV,QAAQ,EAAE;AAAA,IACR,EACG,OAAO;AAAA,MACN,OAAO;AAAA,MACP,MAAM;AAAA,IACR,CAAC,EACA,OAAO;AAAA,EACZ;AAAA,EACA,YAAY,EACT,OAAO;AAAA,IACN,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,eAAe;AAAA,EACjB,CAAC,EACA,OAAO;AAAA,EACV,iBAAiB,EACd,OAAO;AAAA,IACN,MAAM;AAAA,IACN,UAAU,aAAa,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,IAC9C,QAAQ;AAAA,IACR,eAAe;AAAA,IACf,iBAAiB;AAAA,IACjB,wBAAwB;AAAA,IACxB,mBAAmB,EAChB,OAAO;AAAA,MACN,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,SAAS;AAAA,IACX,CAAC,EACA,OAAO;AAAA,EACZ,CAAC,EACA,OAAO;AAAA,EACV,kBAAkB,EACf,OAAO;AAAA,IACN,WAAW;AAAA,IACX,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,SAAS;AAAA,EACX,CAAC,EACA,OAAO;AACZ,CAAC,EACA,OAAO;AAEV,IAAM,8BAA8B,EACjC,OAAO;AAAA,EACN,UAAU,EACP,OAAO;AAAA,IACN,GAAG;AAAA,IACH,UAAU;AAAA,EACZ,CAAC,EACA,OAAO;AAAA,EACV,WAAW,EACR,OAAO;AAAA,IACN,GAAG;AAAA,IACH,UAAU;AAAA,EACZ,CAAC,EACA,OAAO;AAAA,EACV,YAAY,EAAE,OAAO,EAAE,GAAG,mBAAmB,CAAC,EAAE,OAAO;AAAA,EACvD,eAAe,aAAa,IAAI,CAAC,EAAE,IAAI,CAAC;AAC1C,CAAC,EACA,OAAO;AAEV,IAAM,yBAAyB,EAC5B,OAAO;AAAA,EACN,MAAM,EAAE,QAAQ,qBAAqB;AAAA,EACrC,OAAO,EAAE,KAAK,CAAC,UAAU,SAAS,CAAC;AAAA,EACnC,QAAQ,EAAE;AAAA,IACR,EACG,OAAO;AAAA,MACN,aAAa;AAAA,MACb,MAAM;AAAA,MACN,SAAS;AAAA,MACT,GAAG;AAAA,MACH,YAAY,EAAE,QAAQ;AAAA,MACtB,MAAM,EAAE,KAAK,CAAC,WAAW,QAAQ,CAAC,EAAE,SAAS;AAAA,IAC/C,CAAC,EACA,OAAO;AAAA,EACZ;AAAA,EACA,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,SAAS,GAAG,GAAG,EAAE,QAAQ,OAAO,EAAE,CAAC,EAAE,OAAO;AAC5E,CAAC,EACA,OAAO;AAEV,IAAM,4BAA4B,EAC/B,OAAO;AAAA,EACN,eAAe;AAAA,EACf,KAAK;AAAA,EACL,SAAS;AAAA,EACT,UAAU;AAAA,EACV,IAAI,EACD,OAAO;AAAA,IACN,KAAK,EAAE,MAAM,CAAC,cAAc,YAAY,CAAC;AAAA,IACzC,eAAe,EAAE,MAAM,CAAC,cAAc,YAAY,CAAC;AAAA,EACrD,CAAC,EACA,OAAO;AAAA,EACV,GAAG;AAAA,EACH,QAAQ;AACV,CAAC,EACA,OAAO;AAEV,IAAM,qBAAqB,EACxB,OAAO;AAAA,EACN,GAAG;AAAA,EACH,WAAW;AAAA,EACX,aAAa,0BAA0B,SAAS;AAAA,EAChD,gBAAgB,aAAa,SAAS;AAAA,EACtC,gBAAgB,aAAa,SAAS;AAAA,EACtC,eAAe,aAAa,SAAS;AACvC,CAAC,EACA,OAAO;AAEV,IAAM,0BAA0B,EAC7B,OAAO;AAAA,EACN,QAAQ;AAAA,EACR,cAAc;AAAA,EACd,OAAO;AAAA,EACP,KAAK;AAAA,EACL,SAAS;AAAA,EACT,UAAU;AAAA,EACV,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,YAAY;AAAA,EAC1C,mBAAmB,EAAE;AAAA,IACnB,EACG,OAAO;AAAA,MACN,OAAO;AAAA,MACP,SAAS,EAAE;AAAA,QACT,EACG,OAAO;AAAA,UACN,OAAO;AAAA,UACP,OAAO;AAAA,QACT,CAAC,EACA,OAAO;AAAA,MACZ;AAAA,MACA,OAAO;AAAA,IACT,CAAC,EACA,OAAO;AAAA,EACZ;AACF,CAAC,EACA,OAAO;AAEV,IAAM,oBAAoB,EACvB,OAAO;AAAA,EACN,cAAc;AAAA,EACd,eAAe;AAAA,EACf,OAAO;AAAA,EACP,MAAM,EAAE,MAAM,CAAC,cAAc,YAAY,CAAC;AAAA,EAC1C,QAAQ,aAAa,IAAI,CAAC,EAAE,IAAI,CAAC;AAAA,EACjC,GAAG;AAAA,EACH,kBAAkB;AAAA,EAClB,mBAAmB;AAAA,EACnB,SAAS,aAAa,SAAS;AAAA,EAC/B,KAAK;AAAA,EACL,WAAW,mBAAmB,SAAS;AACzC,CAAC,EACA,OAAO;AAEV,IAAM,8BAA8B,EACjC,OAAO;AAAA,EACN,UAAU,EAAE;AAAA,IACV,EACG,OAAO;AAAA,MACN,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,OAAO,aAAa,IAAI,CAAC,EAAE,IAAI,CAAC;AAAA,MAChC,WAAW,EAAE,MAAM,cAAc,EAAE,IAAI,CAAC;AAAA,MACxC,cAAc,eAAe,SAAS;AAAA,IACxC,CAAC,EACA,OAAO;AAAA,EACZ;AAAA,EACA,eAAe;AACjB,CAAC,EACA,OAAO;AAEV,IAAM,6BAA6B,EAChC,OAAO;AAAA,EACN,OAAO;AAAA,EACP,oBAAoB,EAAE,QAAQ;AAAA,EAC9B,SAAS,EACN;AAAA,IACC,EACG,OAAO;AAAA,MACN,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,SAAS;AAAA,IACX,CAAC,EACA,OAAO;AAAA,EACZ,EACC,SAAS;AACd,CAAC,EACA,OAAO;AAEV,IAAM,kCAAkC,EACrC,OAAO;AAAA,EACN,QAAQ;AAAA,EACR,GAAG;AAAA,EACH,SAAS;AAAA,EACT,UAAU;AAAA,EACV,aAAa,EACV,OAAO;AAAA,IACN,WAAW;AAAA,IACX,OAAO;AAAA,IACP,IAAI;AAAA,EACN,CAAC,EACA,OAAO,EACP,SAAS;AACd,CAAC,EACA,OAAO;AAEV,IAAM,uBAAuB,EAC1B,OAAO;AAAA,EACN,QAAQ,EAAE,KAAK,CAAC,QAAQ,QAAQ,MAAM,CAAC;AAAA,EACvC,MAAM,EAAE;AAAA,IACN,EACG,OAAO;AAAA,MACN,MAAM,EAAE,KAAK,CAAC,gBAAgB,iBAAiB,wBAAwB,CAAC;AAAA,MACxE,QAAQ,EAAE,KAAK,CAAC,QAAQ,QAAQ,QAAQ,eAAe,CAAC;AAAA,MACxD,QAAQ;AAAA,IACV,CAAC,EACA,OAAO;AAAA,EACZ;AAAA,EACA,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC;AAC5B,CAAC,EACA,OAAO;AAEV,IAAM,oBAAoB,EACvB,OAAO;AAAA,EACN,SAAS;AAAA,EACT,UAAU;AAAA,EACV,OAAO;AAAA,EACP,MAAM,EAAE,MAAM,CAAC,cAAc,YAAY,CAAC;AAAA,EAC1C,QAAQ,aAAa,IAAI,CAAC,EAAE,IAAI,CAAC;AAAA,EACjC,SAAS;AAAA,EACT,WAAW;AAAA,EACX,UAAU;AAAA,EACV,aAAa,EAAE,QAAQ;AACzB,CAAC,EACA,OAAO;AAEV,IAAM,8BAA8B,EACjC,OAAO;AAAA,EACN,WAAW;AAAA,EACX,UAAU;AAAA,EACV,aAAa,eAAe,SAAS;AAAA,EACrC,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,iBAAiB;AAAA,EAC/C,kBAAkB,EAAE,MAAM,cAAc;AAAA,EACxC,iBAAiB,EAAE,MAAM,cAAc;AACzC,CAAC,EACA,OAAO;AAEV,IAAM,uBAAuB,EAC1B,OAAO;AAAA,EACN,UAAU,EAAE,KAAK,CAAC,YAAY,QAAQ,UAAU,KAAK,CAAC;AAAA,EACtD,MAAM,EAAE,KAAK,CAAC,QAAQ,QAAQ,eAAe,OAAO,eAAe,eAAe,CAAC;AAAA,EACnF,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,cAAc,eAAe,SAAS;AACxC,CAAC,EACA,OAAO;AAEH,IAAM,sBAAgD,EAC1D,OAAO;AAAA,EACN,GAAG;AAAA,EACH,WAAW;AAAA,EACX,WAAW;AAAA,EACX,cAAc,EAAE,OAAO,EAAE,OAAO,GAAG,wBAAwB;AAAA,EAC3D,aAAa,EACV,OAAO;AAAA,IACN,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,YAAY,4BAA4B,SAAS;AAAA,IACjD,UAAU,EACP,OAAO;AAAA,MACN,MAAM,eAAe,SAAS;AAAA,MAC9B,QAAQ,eAAe,SAAS;AAAA,IAClC,CAAC,EACA,OAAO,EACP,SAAS;AAAA,EACd,CAAC,EACA,OAAO;AAAA,EACV,QAAQ,EAAE,OAAO,EAAE,OAAO,GAAG,kBAAkB;AAAA,EAC/C,YAAY,wBAAwB,SAAS;AAAA,EAC7C,MAAM,kBAAkB,SAAS;AAAA,EACjC,iBAAiB,4BAA4B,SAAS;AAAA,EACtD,eAAe,2BAA2B,SAAS;AAAA,EACnD,oBAAoB,gCAAgC,SAAS;AAAA,EAC7D,SAAS;AAAA,EACT,uBAAuB,4BAA4B,SAAS;AAAA,EAC5D,cAAc,EACX;AAAA,IACC,EACG,OAAO;AAAA,MACN,MAAM;AAAA,MACN,OAAO;AAAA,MACP,OAAO,aAAa,IAAI,CAAC,EAAE,IAAI,CAAC;AAAA,IAClC,CAAC,EACA,OAAO;AAAA,EACZ,EACC,SAAS;AAAA,EACZ,iBAAiB,EAAE,MAAM,oBAAoB;AAC/C,CAAC,EACA,OAAO;AAEV,IAAM,eAAe,EAAE;AAAA,EACrB,CAAC,UAAU,OAAO,UAAU,YAAY,wBAAwB,KAAK,KAAK;AAAA,EAC1E;AACF;AAEO,IAAM,uBAAkD,EAAE,MAAM;AAAA,EACrE,EAAE,OAAO;AAAA,EACT,EACG,OAAO;AAAA,IACN,MAAM,EAAE,QAAQ,YAAY;AAAA,IAC5B,YAAY,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC;AAAA,EAC7C,CAAC,EACA,OAAO;AAAA,EACV,EACG,OAAO;AAAA,IACN,MAAM,EAAE,QAAQ,MAAM;AAAA,IACtB,aAAa;AAAA,IACb,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,iBAAiB;AAAA,IACjB,eAAe;AAAA,IACf,OAAO,EACJ,OAAO;AAAA,MACN,QAAQ,EAAE,QAAQ,iBAAiB;AAAA,MACnC,QAAQ;AAAA,MACR,YAAY;AAAA,IACd,CAAC,EACA,OAAO;AAAA,IACV,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA,EAC/B,CAAC,EACA,OAAO;AACZ,CAAC;AAEM,IAAM,2BAA2B,EAAE,KAAK;AAAA,EAC7C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAEM,IAAM,yBAAsD,EAChE,OAAO;AAAA,EACN,YAAY;AAAA,EACZ,KAAK;AAAA,EACL,eAAe,aAAa,SAAS;AAAA,EACrC,YAAY,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,OAAO,EAAE,OAAO,GAAG,YAAY,CAAC;AAAA,EACnE,iBAAiB;AAAA,EACjB,qBAAqB,mBAAmB,SAAS;AAAA,EACjD,cAAc,EAAE,OAAO,EAAE,SAAS;AACpC,CAAC,EACA,OAAO;AAEH,IAAM,kCAAwE,EAClF,OAAO;AAAA,EACN,OAAO;AAAA,EACP,aAAa;AAAA,EACb,SAAS,qBAAqB,SAAS;AAAA,EACvC,OAAO,EAAE,MAAM,sBAAsB;AAAA,EACrC,eAAe,aAAa,SAAS;AAAA,EACrC,SAAS;AAAA,EACT,YAAY;AACd,CAAC,EACA,OAAO;AAEV,IAAM,sBAAsB,EAAE,KAAK;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,IAAM,qBAAqB,EAAE,KAAK;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAEM,IAAM,qBAA8C,EACxD,OAAO;AAAA,EACN,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,KAAK,CAAC;AAAA,EAC/C,QAAQ;AAAA,EACR,QAAQ,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC;AAAA,EACvC,UAAU,gCAAgC,SAAS;AAAA,EACnD,aAAa,EAAE,MAAM,+BAA+B;AAAA,EACpD,cAAc,mBAAmB,SAAS;AAAA,EAC1C,aAAa,aAAa,SAAS;AAAA,EACnC,cAAc;AAAA,EACd,iBAAiB;AAAA,EACjB,cAAc,EAAE,OAAO,EAAE,SAAS;AAAA,EAClC,eAAe,oBAAoB,SAAS;AAC9C,CAAC,EACA,OAAO,EACP,YAAY,CAAC,OAAO,QAAQ;AAC3B,MAAI,MAAM,YAAY,MAAM,SAAS,UAAU,GAAG;AAChD,QAAI,SAAS;AAAA,MACX,MAAM;AAAA,MACN,MAAM,CAAC,YAAY,OAAO;AAAA,MAC1B,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAEA,QAAM,OAAO,oBAAI,IAAY;AAC7B,WAAS,IAAI,GAAG,IAAI,MAAM,YAAY,QAAQ,KAAK;AACjD,UAAM,QAAQ,MAAM,YAAY,CAAC,EAAG;AACpC,QAAI,KAAK,IAAI,KAAK,GAAG;AACnB,UAAI,SAAS;AAAA,QACX,MAAM;AAAA,QACN,MAAM,CAAC,eAAe,GAAG,OAAO;AAAA,QAChC,SAAS,8BAA8B,KAAK;AAAA,MAC9C,CAAC;AAAA,IACH;AACA,SAAK,IAAI,KAAK;AAAA,EAChB;AAEA,MAAI,MAAM,WAAW,aAAa,CAAC,MAAM,cAAc,KAAK,GAAG;AAC7D,QAAI,SAAS;AAAA,MACX,MAAM;AAAA,MACN,MAAM,CAAC,cAAc;AAAA,MACrB,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AACF,CAAC;AAEH,IAAM,4BAA4B,EAC/B,OAAO;AAAA,EACN,cAAc;AAAA,EACd,MAAM;AAAA,EACN,YAAY,WAAW,SAAS;AAClC,CAAC,EACA,OAAO;AAEH,IAAM,uBAAkD,EAC5D,OAAO;AAAA,EACN,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,cAAc,eAAe,SAAS;AAAA,EACtC,MAAM;AAAA,EACN,mBAAmB;AAAA,EACnB,iBAAiB;AAAA,EACjB;AAAA,EACA,QAAQ,EAAE,MAAM,yBAAyB,EAAE,SAAS;AAAA,EACpD,QAAQ,EACL,OAAO;AAAA,IACN,MAAM,EAAE,KAAK,CAAC,MAAM,SAAS,OAAO,CAAC;AAAA,IACrC,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA,EAC/B,CAAC,EACA,OAAO,EACP,SAAS;AAAA,EACZ,gBAAgB,eAAe,SAAS;AAAA,EACxC,qBAAqB,mBAAmB,SAAS;AAAA,EACjD,iBAAiB,eAAe,SAAS;AAAA,EACzC,qBAAqB,eAAe,SAAS;AAC/C,CAAC,EACA,OAAO,EACP,OAAO,CAAC,SAAS,OAAO,KAAK,eAAe,KAAK,OAAO,KAAK,iBAAiB,GAAG;AAAA,EAChF,MAAM,CAAC,iBAAiB;AAAA,EACxB,SAAS;AACX,CAAC;AAEI,IAAM,+BAA+B,EACzC,OAAO;AAAA,EACN,aAAa,EAAE,QAAQ,mBAAmB;AAAA,EAC1C,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC;AAC7B,CAAC,EACA,OAAO;AAEH,IAAM,6BAA6B,EACvC,OAAO;AAAA,EACN,aAAa,EAAE,QAAQ,mBAAmB;AAAA,EAC1C,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC;AAC5B,CAAC,EACA,OAAO;AAEH,IAAM,8BAAgE,EAC1E,OAAO;AAAA,EACN,aAAa,EAAE,QAAQ,mBAAmB;AAAA,EAC1C,QAAQ,EAAE,MAAM,kBAAkB;AACpC,CAAC,EACA,OAAO;AAEH,IAAM,4BAA4D,EACtE,OAAO;AAAA,EACN,aAAa,EAAE,QAAQ,mBAAmB;AAAA,EAC1C,OAAO,EAAE,MAAM,oBAAoB;AACrC,CAAC,EACA,OAAO;AAEH,IAAM,uBAAkD,EAC5D,OAAO;AAAA,EACN,UAAU;AAAA,EACV,UAAU,EAAE;AAAA,IACV,EACG,OAAO;AAAA,MACN,OAAO;AAAA,MACP,QAAQ;AAAA,IACV,CAAC,EACA,OAAO;AAAA,EACZ;AACF,CAAC,EACA,OAAO;;;ACpkBV,IAAM,6BAA6B;AAEnC,SAAS,MAAM,IAA2B;AACxC,SAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AACzD;AAEA,SAAS,oBAAoB,UAA0B;AACrD,SAAO,SAAS,KAAK,EAAE,QAAQ,QAAQ,EAAE,EAAE,QAAQ,SAAS,EAAE;AAChE;AAEA,SAAS,sBAAsB,KAAiC;AAC9D,QAAM,WAAW,OAAO,WAAW,OAAO,WAAW;AACrD,MAAI,SAAS,KAAK,EAAE,WAAW,EAAG,OAAM,IAAI,MAAM,mCAAmC;AACrF,MAAI,SAAS,SAAS,4BAA4B;AAChD,UAAM,IAAI,MAAM,mCAAmC,0BAA0B,aAAa;AAAA,EAC5F;AACA,SAAO;AACT;AAEA,SAAS,yBAAyB,OAE/B;AACD,SAAO,MAAM,OACV;AAAA,IACC,CAAC,UACC,GAAG,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,IAAI,MAAM,EAAE,KAAK,GAAG,IAAI,OAAO,KAAK,MAAM,OAAO;AAAA,EAC3F,EACC,KAAK,IAAI;AACd;AAEA,eAAe,KACb,QACA,MACA,MACA,OAAuB,CAAC,GACC;AACzB,QAAM,YAAY,OAAO,aAAa;AACtC,QAAM,aAAa,OAAO,WAAW;AACrC,QAAM,IAAkB,OAAO,cAAc,IAAI,SAAS,MAAM,GAAG,IAAI;AACvE,QAAM,OAAO,oBAAoB,OAAO,QAAQ;AAChD,QAAM,MAAM,GAAG,IAAI,GAAG,IAAI;AAC1B,QAAM,iBAAiB,sBAAsB,KAAK,cAAc;AAChE,QAAM,UAAkC;AAAA,IACtC,gBAAgB;AAAA,IAChB,eAAe,UAAU,OAAO,MAAM;AAAA,IACtC,sBAAsB,OAAO;AAAA,IAC7B,yBAAyB;AAAA,IACzB,mBAAmB;AAAA,EACrB;AAEA,MAAI;AACJ,WAAS,UAAU,GAAG,WAAW,YAAY,WAAW;AACtD,UAAM,aAAa,YAAY,QAAQ,SAAS;AAChD,UAAM,iBAAiB,KAAK,SAAS,YAAY,IAAI,CAAC,KAAK,QAAQ,UAAU,CAAC,IAAI;AAClF,QAAI;AACJ,QAAI;AACF,YAAM,MAAM,EAAE,KAAK;AAAA,QACjB,QAAQ;AAAA,QACR;AAAA,QACA,MAAM,KAAK,UAAU,IAAI;AAAA,QACzB,QAAQ;AAAA,MACV,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,UAAI,KAAK,QAAQ,QAAS,OAAM;AAChC,kBAAY;AACZ,UAAI,YAAY,WAAY,OAAM;AAClC,YAAM,MAAM,KAAK,UAAU,MAAM,KAAK,OAAO,IAAI,GAAG;AACpD;AAAA,IACF;AAEA,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC5C,YAAM,QAAQ,IAAI,MAAM,iBAAiB,GAAG,YAAY,IAAI,MAAM,MAAM,KAAK,MAAM,GAAG,GAAG,CAAC,EAAE;AAC5F,YAAM,YAAY,IAAI,UAAU,OAAO,IAAI,WAAW,OAAO,IAAI,WAAW;AAC5E,UAAI,CAAC,aAAa,YAAY,WAAY,OAAM;AAChD,kBAAY;AACZ,YAAM,MAAM,KAAK,UAAU,MAAM,KAAK,OAAO,IAAI,GAAG;AACpD;AAAA,IACF;AAEA,QAAI;AACJ,QAAI;AACF,oBAAc,MAAM,IAAI,KAAK;AAAA,IAC/B,SAAS,OAAO;AACd,YAAM,IAAI,MAAM,iBAAiB,GAAG,0BAA0B,EAAE,OAAO,MAAM,CAAC;AAAA,IAChF;AACA,UAAM,SAAS,qBAAqB,UAAU,WAAW;AACzD,QAAI,CAAC,OAAO,SAAS;AACnB,YAAM,IAAI;AAAA,QACR,iBAAiB,GAAG,kCAAkC,yBAAyB,OAAO,KAAK,CAAC;AAAA,MAC9F;AAAA,IACF;AACA,WAAO,OAAO;AAAA,EAChB;AACA,QAAM,aAAa,IAAI,MAAM,iCAAiC;AAChE;AAEO,SAAS,mBAAmB,QAAoC;AACrE,MAAI,oBAAoB,OAAO,QAAQ,EAAE,WAAW,EAAG,OAAM,IAAI,MAAM,sBAAsB;AAC7F,MAAI,OAAO,OAAO,KAAK,EAAE,WAAW,EAAG,OAAM,IAAI,MAAM,oBAAoB;AAC3E,MAAI,OAAO,SAAS,KAAK,EAAE,WAAW,EAAG,OAAM,IAAI,MAAM,sBAAsB;AAC/E,MACE,OAAO,cAAc,WACpB,CAAC,OAAO,SAAS,OAAO,SAAS,KAAK,OAAO,aAAa,IAC3D;AACA,UAAM,IAAI,MAAM,kCAAkC;AAAA,EACpD;AACA,MAAI,OAAO,YAAY,WAAc,CAAC,OAAO,UAAU,OAAO,OAAO,KAAK,OAAO,UAAU,IAAI;AAC7F,UAAM,IAAI,MAAM,wCAAwC;AAAA,EAC1D;AAEA,SAAO;AAAA,IACL;AAAA,IACA,aAAa;AAAA,IAEb,MAAM,cAAc,OAAO,gBAAgB;AACzC,aAAO,KAAK,eAAe,CAAC,KAAK,GAAG,cAAc;AAAA,IACpD;AAAA,IAEA,MAAM,eAAe,QAAQ,gBAAgB;AAC3C,YAAM,OAA8B,4BAA4B,MAAM;AAAA,QACpE,aAAa;AAAA,QACb;AAAA,MACF,CAAC;AACD,aAAO,KAA4B,QAAQ,wBAAwB,MAAM;AAAA,QACvE;AAAA,MACF,CAAC;AAAA,IACH;AAAA,IAEA,MAAM,aAAa,OAAO,gBAAgB;AACxC,YAAM,OAA4B,0BAA0B,MAAM;AAAA,QAChE,aAAa;AAAA,QACb;AAAA,MACF,CAAC;AACD,aAAO,KAA0B,QAAQ,qBAAqB,MAAM;AAAA,QAClE;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF;AA4BO,SAAS,oBACd,YAAkF,CAAC,GACzD;AAC1B,QAAM,MAAM,UAAU,OAAO,QAAQ;AACrC,QAAM,YACJ,UAAU,YACV,IAAI,qBACJ,IAAI,0BACH,KAAK;AACR,QAAM,UAAU,UAAU,UAAU,IAAI,yBAAyB,IAAI,iBAAiB,KAAK;AAC3F,QAAM,YAAY,UAAU,YAAY,IAAI,mBAAmB,KAAK;AACpE,MAAI,CAAC,YAAY,CAAC,UAAU,CAAC,SAAU,QAAO;AAC9C,QAAM,SAAuB,EAAE,UAAU,SAAS,QAAQ,QAAQ,EAAE,GAAG,QAAQ,SAAS;AACxF,MAAI,UAAU,UAAW,QAAO,YAAY,UAAU;AACtD,MAAI,UAAU,cAAc,OAAW,QAAO,YAAY,UAAU;AACpE,MAAI,UAAU,YAAY,OAAW,QAAO,UAAU,UAAU;AAChE,SAAO;AACT;AAEO,SAAS,oBACd,YAAkF,CAAC,GACzD;AAC1B,QAAM,SAAS,oBAAoB,SAAS;AAC5C,SAAO,SAAS,mBAAmB,MAAM,IAAI;AAC/C;","names":[]}
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
+
import {
|
|
2
|
+
pairRunRecords
|
|
3
|
+
} from "./chunk-DPUHNQLN.js";
|
|
1
4
|
import {
|
|
2
5
|
benjaminiHochberg,
|
|
3
|
-
cohensD,
|
|
4
6
|
confidenceInterval,
|
|
5
7
|
pairedBootstrap,
|
|
8
|
+
pairedCohensDz,
|
|
6
9
|
pairedMde,
|
|
7
10
|
wilcoxonSignedRank
|
|
8
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-MHELPNRP.js";
|
|
9
12
|
import {
|
|
10
13
|
canonicalize,
|
|
11
14
|
hashJson
|
|
@@ -34,14 +37,20 @@ function summaryTable(runs, opts = {}) {
|
|
|
34
37
|
for (const id of candidateIds) {
|
|
35
38
|
const bucket = byCandidate.get(id);
|
|
36
39
|
const ci = confidenceInterval(bucket.scores, confidence);
|
|
37
|
-
let rawP =
|
|
38
|
-
let d =
|
|
40
|
+
let rawP = null;
|
|
41
|
+
let d = null;
|
|
42
|
+
let pairedN = null;
|
|
43
|
+
let unpairedCandidateN = null;
|
|
44
|
+
let unpairedComparatorN = null;
|
|
39
45
|
if (comparator && compRuns && id !== comparator) {
|
|
40
46
|
const paired = pairScoresByKey(bucket.runs, compRuns.runs, scoreField);
|
|
47
|
+
pairedN = paired.before.length;
|
|
48
|
+
unpairedCandidateN = paired.unpairedCandidateN;
|
|
49
|
+
unpairedComparatorN = paired.unpairedComparatorN;
|
|
41
50
|
if (paired.before.length >= 6) {
|
|
42
51
|
rawP = wilcoxonSignedRank(paired.before, paired.after).p;
|
|
43
52
|
}
|
|
44
|
-
d =
|
|
53
|
+
d = pairedCohensDz(paired.before, paired.after);
|
|
45
54
|
}
|
|
46
55
|
tentative.push({
|
|
47
56
|
candidateId: id,
|
|
@@ -51,6 +60,9 @@ function summaryTable(runs, opts = {}) {
|
|
|
51
60
|
ciHigh: ci.upper,
|
|
52
61
|
qValue: rawP,
|
|
53
62
|
cohensD: d,
|
|
63
|
+
pairedN,
|
|
64
|
+
unpairedCandidateN,
|
|
65
|
+
unpairedComparatorN,
|
|
54
66
|
rawP
|
|
55
67
|
});
|
|
56
68
|
}
|
|
@@ -60,7 +72,7 @@ function summaryTable(runs, opts = {}) {
|
|
|
60
72
|
for (let i = 0; i < tentative.length; i++) {
|
|
61
73
|
const r = tentative[i];
|
|
62
74
|
if (r.candidateId === comparator) continue;
|
|
63
|
-
if (!Number.isFinite(r.rawP)) continue;
|
|
75
|
+
if (r.rawP === null || !Number.isFinite(r.rawP)) continue;
|
|
64
76
|
idxs.push(i);
|
|
65
77
|
ps.push(r.rawP);
|
|
66
78
|
}
|
|
@@ -76,38 +88,32 @@ function summaryTable(runs, opts = {}) {
|
|
|
76
88
|
return { rows, comparator, split, markdown };
|
|
77
89
|
}
|
|
78
90
|
function pairScoresByKey(candidate, baseline, scoreField) {
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
for (const r of candidate) {
|
|
89
|
-
const v = r.outcome[scoreField];
|
|
90
|
-
if (typeof v !== "number" || !Number.isFinite(v)) continue;
|
|
91
|
-
const key = `${r.experimentId}::${r.seed}`;
|
|
92
|
-
const b = baseIdx.get(key);
|
|
93
|
-
if (b === void 0) continue;
|
|
94
|
-
before.push(b);
|
|
95
|
-
after.push(v);
|
|
96
|
-
}
|
|
97
|
-
return { before, after };
|
|
91
|
+
const scoredCandidate = candidate.filter((run) => Number.isFinite(run.outcome[scoreField]));
|
|
92
|
+
const scoredBaseline = baseline.filter((run) => Number.isFinite(run.outcome[scoreField]));
|
|
93
|
+
const pairing = pairRunRecords(scoredBaseline, scoredCandidate);
|
|
94
|
+
return {
|
|
95
|
+
before: pairing.pairs.map((pair) => pair.baseline.outcome[scoreField]),
|
|
96
|
+
after: pairing.pairs.map((pair) => pair.treatment.outcome[scoreField]),
|
|
97
|
+
unpairedCandidateN: pairing.unpairedTreatment.length,
|
|
98
|
+
unpairedComparatorN: pairing.unpairedBaseline.length
|
|
99
|
+
};
|
|
98
100
|
}
|
|
99
101
|
function renderSummaryTableMarkdown(rows, comparator, split) {
|
|
100
102
|
const lines = [];
|
|
101
103
|
const cmpLabel = comparator ? ` (vs ${comparator})` : "";
|
|
102
104
|
lines.push(`Summary Table \u2014 ${split} split${cmpLabel}`);
|
|
103
105
|
lines.push("");
|
|
104
|
-
lines.push("| Candidate | N | Mean | 95% CI | q (BH) | Cohen's
|
|
105
|
-
lines.push("
|
|
106
|
+
lines.push("| Candidate | N | Pairs | Unpaired | Mean | 95% CI | q (BH) | Cohen's dz |");
|
|
107
|
+
lines.push("|---|---:|---:|---:|---:|---|---:|---:|");
|
|
106
108
|
for (const r of rows) {
|
|
107
109
|
const ci = `[${fmt(r.ciLow)}, ${fmt(r.ciHigh)}]`;
|
|
108
|
-
const q =
|
|
109
|
-
const d =
|
|
110
|
-
|
|
110
|
+
const q = r.qValue !== null ? r.qValue.toFixed(4) : "\u2014";
|
|
111
|
+
const d = r.cohensD !== null ? r.cohensD.toFixed(3) : "\u2014";
|
|
112
|
+
const paired = r.pairedN ?? "\u2014";
|
|
113
|
+
const unpaired = r.unpairedCandidateN === null || r.unpairedComparatorN === null ? "\u2014" : `${r.unpairedCandidateN}/${r.unpairedComparatorN}`;
|
|
114
|
+
lines.push(
|
|
115
|
+
`| ${r.candidateId} | ${r.n} | ${paired} | ${unpaired} | ${fmt(r.mean)} | ${ci} | ${q} | ${d} |`
|
|
116
|
+
);
|
|
111
117
|
}
|
|
112
118
|
return lines.join("\n");
|
|
113
119
|
}
|
|
@@ -119,6 +125,7 @@ function paretoChart(runs, opts = {}) {
|
|
|
119
125
|
if (r.splitTag !== split) continue;
|
|
120
126
|
const v = r.outcome[scoreField];
|
|
121
127
|
if (typeof v !== "number" || !Number.isFinite(v)) continue;
|
|
128
|
+
if (r.costUsd === null) continue;
|
|
122
129
|
const bucket = buckets.get(r.candidateId) ?? { cost: [], quality: [] };
|
|
123
130
|
bucket.cost.push(r.costUsd);
|
|
124
131
|
bucket.quality.push(v);
|
|
@@ -149,11 +156,7 @@ function dominates(a, b) {
|
|
|
149
156
|
return a.cost <= b.cost && a.quality >= b.quality && (a.cost < b.cost || a.quality > b.quality);
|
|
150
157
|
}
|
|
151
158
|
function gateLabel(d) {
|
|
152
|
-
|
|
153
|
-
if (d.rejectionCode === "few_runs") return "reject_few_runs";
|
|
154
|
-
if (d.rejectionCode === "negative_delta") return "reject_negative_delta";
|
|
155
|
-
if (d.rejectionCode === "overfit_gap") return "reject_overfit_gap";
|
|
156
|
-
return null;
|
|
159
|
+
return d.promote ? "promote" : "reject";
|
|
157
160
|
}
|
|
158
161
|
function gainHistogram(runs, candidateId, comparator, opts = {}) {
|
|
159
162
|
const split = opts.split ?? "holdout";
|
|
@@ -162,7 +165,11 @@ function gainHistogram(runs, candidateId, comparator, opts = {}) {
|
|
|
162
165
|
if (binCount < 1) throw new Error("gainHistogram: bins must be \u2265 1");
|
|
163
166
|
const candidate = runs.filter((r) => r.candidateId === candidateId && r.splitTag === split);
|
|
164
167
|
const baseline = runs.filter((r) => r.candidateId === comparator && r.splitTag === split);
|
|
165
|
-
const { before, after } = pairScoresByKey(
|
|
168
|
+
const { before, after, unpairedCandidateN, unpairedComparatorN } = pairScoresByKey(
|
|
169
|
+
candidate,
|
|
170
|
+
baseline,
|
|
171
|
+
scoreField
|
|
172
|
+
);
|
|
166
173
|
const n = before.length;
|
|
167
174
|
if (n === 0) {
|
|
168
175
|
return {
|
|
@@ -171,9 +178,11 @@ function gainHistogram(runs, candidateId, comparator, opts = {}) {
|
|
|
171
178
|
comparator,
|
|
172
179
|
split,
|
|
173
180
|
n: 0,
|
|
181
|
+
unpairedCandidateN,
|
|
182
|
+
unpairedComparatorN,
|
|
174
183
|
bins: [],
|
|
175
|
-
median:
|
|
176
|
-
ci:
|
|
184
|
+
median: null,
|
|
185
|
+
ci: null
|
|
177
186
|
};
|
|
178
187
|
}
|
|
179
188
|
const deltas = before.map((b, i) => after[i] - b);
|
|
@@ -207,6 +216,8 @@ function gainHistogram(runs, candidateId, comparator, opts = {}) {
|
|
|
207
216
|
comparator,
|
|
208
217
|
split,
|
|
209
218
|
n,
|
|
219
|
+
unpairedCandidateN,
|
|
220
|
+
unpairedComparatorN,
|
|
210
221
|
bins,
|
|
211
222
|
median,
|
|
212
223
|
ci: { low: ci.low, high: ci.high }
|
|
@@ -231,7 +242,7 @@ function pairedPosterior(runs, candidateId, comparator, opts) {
|
|
|
231
242
|
statistic: "median",
|
|
232
243
|
seed: opts.seed
|
|
233
244
|
});
|
|
234
|
-
const meanSamples =
|
|
245
|
+
const meanSamples = bayesianBootstrapMeanSamples(deltas, 2e3, opts.seed);
|
|
235
246
|
const prGreaterThanZero = meanSamples.length === 0 ? 0 : meanSamples.filter((s) => s > 0).length / meanSamples.length;
|
|
236
247
|
const prInRope = opts.rope === null || meanSamples.length === 0 ? null : meanSamples.filter((s) => s >= opts.rope.low && s <= opts.rope.high).length / meanSamples.length;
|
|
237
248
|
const dStandardised = pairedMde({ nPaired: n, alpha: opts.mdeAlpha, power: opts.mdePower });
|
|
@@ -247,16 +258,21 @@ function pairedPosterior(runs, candidateId, comparator, opts) {
|
|
|
247
258
|
mde
|
|
248
259
|
};
|
|
249
260
|
}
|
|
250
|
-
function
|
|
261
|
+
function bayesianBootstrapMeanSamples(deltas, resamples, seed) {
|
|
251
262
|
const n = deltas.length;
|
|
252
263
|
if (n === 0) return [];
|
|
253
264
|
if (n === 1) return new Array(resamples).fill(deltas[0]);
|
|
254
265
|
const rng = seedRng(seed);
|
|
255
266
|
const samples = new Array(resamples);
|
|
256
267
|
for (let b = 0; b < resamples; b++) {
|
|
257
|
-
let
|
|
258
|
-
|
|
259
|
-
|
|
268
|
+
let weightedSum = 0;
|
|
269
|
+
let weightSum = 0;
|
|
270
|
+
for (let k = 0; k < n; k++) {
|
|
271
|
+
const weight = -Math.log(Math.max(Number.MIN_VALUE, rng()));
|
|
272
|
+
weightedSum += deltas[k] * weight;
|
|
273
|
+
weightSum += weight;
|
|
274
|
+
}
|
|
275
|
+
samples[b] = weightedSum / weightSum;
|
|
260
276
|
}
|
|
261
277
|
return samples;
|
|
262
278
|
}
|
|
@@ -331,10 +347,13 @@ async function researchReport(runs, opts = {}) {
|
|
|
331
347
|
const gain = gainByCandidate.get(row.candidateId);
|
|
332
348
|
const point = paretoByCandidate.get(row.candidateId);
|
|
333
349
|
const posterior = posteriorByCandidate.get(row.candidateId) ?? null;
|
|
350
|
+
const gateDecision = opts.gateDecisions?.[row.candidateId];
|
|
334
351
|
const classified = classifyCandidate(row, {
|
|
335
352
|
comparator,
|
|
336
353
|
posterior,
|
|
337
354
|
point,
|
|
355
|
+
gateDecision,
|
|
356
|
+
split,
|
|
338
357
|
fdr,
|
|
339
358
|
minPairs,
|
|
340
359
|
rope
|
|
@@ -356,7 +375,7 @@ async function researchReport(runs, opts = {}) {
|
|
|
356
375
|
prInRope: posterior ? posterior.prInRope : null,
|
|
357
376
|
mde: posterior ? posterior.mde : null,
|
|
358
377
|
onParetoFrontier: point?.onFrontier ?? false,
|
|
359
|
-
gate:
|
|
378
|
+
gate: gateDecision ? gateLabel(gateDecision) : void 0,
|
|
360
379
|
decision: classified.decision,
|
|
361
380
|
decisionReason: classified.reason
|
|
362
381
|
};
|
|
@@ -435,7 +454,7 @@ async function researchReport(runs, opts = {}) {
|
|
|
435
454
|
}
|
|
436
455
|
function buildMethodology(ctx) {
|
|
437
456
|
const assumptions = [
|
|
438
|
-
"Pairs are matched by (experimentId, seed);
|
|
457
|
+
"Pairs are matched by (experimentId, scenarioId, seed); input order does not affect the comparison.",
|
|
439
458
|
"Paired deltas are exchangeable conditional on the matched scenario \u2014 no mid-run distribution shift.",
|
|
440
459
|
`Decisions are pre-specified at fdr=${ctx.fdr}, minPairs=${ctx.minPairs}, confidence=${ctx.confidence}; deviating from these post-hoc invalidates the false-discovery control.`
|
|
441
460
|
];
|
|
@@ -448,8 +467,8 @@ function buildMethodology(ctx) {
|
|
|
448
467
|
assumptions.push("No comparator was configured; this run is descriptive, not causal.");
|
|
449
468
|
}
|
|
450
469
|
const methods = [
|
|
451
|
-
"Marginal scores summarised with BH-FDR-adjusted Wilcoxon signed-rank q-values and Cohen's
|
|
452
|
-
"Paired evidence summarised with bootstrap CI on the median delta and Bayesian
|
|
470
|
+
"Marginal scores summarised with BH-FDR-adjusted Wilcoxon signed-rank q-values and paired Cohen's dz via summaryTable.",
|
|
471
|
+
"Paired evidence summarised with a frequentist bootstrap CI on the median delta and a Dirichlet-weight Bayesian bootstrap for Pr(\u0394>0) and Pr(\u0394\u2208ROPE) on the mean delta.",
|
|
453
472
|
`Minimum detectable effect reported per candidate at \u03B1=${ctx.mdeAlpha} (two-sided), power=${ctx.mdePower}, standardised by the observed paired-delta SD.`,
|
|
454
473
|
"Pareto frontier flagged as a separate axis (cost vs quality); a candidate can be on-frontier without winning the paired test.",
|
|
455
474
|
"Held-out gate decisions, when supplied, override the statistical verdict in the reject direction."
|
|
@@ -482,15 +501,19 @@ function classifyCandidate(row, ctx) {
|
|
|
482
501
|
if (ctx.comparator && row.candidateId === ctx.comparator) {
|
|
483
502
|
return { decision: "hold", reason: "Comparator baseline." };
|
|
484
503
|
}
|
|
504
|
+
if (ctx.gateDecision && !ctx.gateDecision.promote) {
|
|
505
|
+
const code = ctx.gateDecision.rejectionCode ?? "rejected";
|
|
506
|
+
return {
|
|
507
|
+
decision: "reject",
|
|
508
|
+
reason: `Held-out gate returned ${code}: ${ctx.gateDecision.reason}`
|
|
509
|
+
};
|
|
510
|
+
}
|
|
485
511
|
if (!ctx.comparator) {
|
|
486
512
|
return {
|
|
487
513
|
decision: ctx.point?.onFrontier ? "hold" : "needs_more_data",
|
|
488
514
|
reason: "No comparator configured; report ranks candidates but cannot anchor a promotion call."
|
|
489
515
|
};
|
|
490
516
|
}
|
|
491
|
-
if (ctx.point?.gate && ctx.point.gate !== "promote") {
|
|
492
|
-
return { decision: "reject", reason: `Held-out gate returned ${ctx.point.gate}.` };
|
|
493
|
-
}
|
|
494
517
|
if (!ctx.posterior || ctx.posterior.n < RESEARCH_REPORT_HARD_PAIR_FLOOR) {
|
|
495
518
|
return {
|
|
496
519
|
decision: "needs_more_data",
|
|
@@ -498,13 +521,18 @@ function classifyCandidate(row, ctx) {
|
|
|
498
521
|
};
|
|
499
522
|
}
|
|
500
523
|
const ci = ctx.posterior.ci;
|
|
524
|
+
if (ctx.split === "search") {
|
|
525
|
+
return {
|
|
526
|
+
decision: "hold",
|
|
527
|
+
reason: "Search-split evidence is descriptive; release decisions require matched holdout runs."
|
|
528
|
+
};
|
|
529
|
+
}
|
|
501
530
|
if (ctx.rope && ci.low >= ctx.rope.low && ci.high <= ctx.rope.high) {
|
|
502
531
|
return {
|
|
503
532
|
decision: "equivalent",
|
|
504
533
|
reason: `Paired-delta CI [${fmt(ci.low)}, ${fmt(ci.high)}] is fully inside ROPE ${formatRope(ctx.rope)}; candidate is practically equivalent to comparator.`
|
|
505
534
|
};
|
|
506
535
|
}
|
|
507
|
-
const significant = Number.isFinite(row.qValue) && row.qValue <= ctx.fdr;
|
|
508
536
|
const gainPositive = ci.low > 0;
|
|
509
537
|
const gainNegative = ci.high < 0;
|
|
510
538
|
if (gainNegative) {
|
|
@@ -519,7 +547,7 @@ function classifyCandidate(row, ctx) {
|
|
|
519
547
|
reason: `Only ${ctx.posterior.n} paired observations; minimum detectable effect at this N is ${fmt(ctx.posterior.mde)} score units (need \u2265 ${ctx.minPairs} pairs to issue a directional verdict).`
|
|
520
548
|
};
|
|
521
549
|
}
|
|
522
|
-
if (
|
|
550
|
+
if (row.qValue !== null && row.qValue <= ctx.fdr && gainPositive) {
|
|
523
551
|
return {
|
|
524
552
|
decision: "promote",
|
|
525
553
|
reason: `BH-adjusted q=${fmt(row.qValue)} \u2264 ${ctx.fdr} and paired-delta CI [${fmt(ci.low)}, ${fmt(ci.high)}] excludes zero; Pr(\u0394>0)=${fmt(ctx.posterior.prGreaterThanZero)}.`
|
|
@@ -681,14 +709,14 @@ function renderResearchMarkdown(report) {
|
|
|
681
709
|
lines.push("## Candidate Decision Table");
|
|
682
710
|
lines.push("");
|
|
683
711
|
lines.push(
|
|
684
|
-
"| Candidate | Decision | Mean | \u0394\u0304 | Pr(\u0394>0) | q |
|
|
712
|
+
"| Candidate | Decision | Mean | \u0394\u0304 | Pr(\u0394>0) | q | dz | Paired N | Median Gain CI | MDE | Pareto | Gate |"
|
|
685
713
|
);
|
|
686
714
|
lines.push("|---|---|---:|---:|---:|---:|---:|---:|---|---:|---|---|");
|
|
687
715
|
for (const c of report.candidates) {
|
|
688
716
|
const delta = c.meanDeltaVsComparator === null ? "-" : signed(c.meanDeltaVsComparator);
|
|
689
717
|
const prGt = c.prGreaterThanZero === null ? "-" : c.prGreaterThanZero.toFixed(3);
|
|
690
|
-
const q =
|
|
691
|
-
const d =
|
|
718
|
+
const q = c.qValue !== null ? c.qValue.toFixed(4) : "-";
|
|
719
|
+
const d = c.cohensD !== null ? c.cohensD.toFixed(3) : "-";
|
|
692
720
|
const gain = c.gainCi ? `[${fmt(c.gainCi.low)}, ${fmt(c.gainCi.high)}]` : "-";
|
|
693
721
|
const mde = c.mde === null || !Number.isFinite(c.mde) ? "-" : fmt(c.mde);
|
|
694
722
|
lines.push(
|
|
@@ -877,4 +905,4 @@ export {
|
|
|
877
905
|
RESEARCH_REPORT_HARD_PAIR_FLOOR,
|
|
878
906
|
researchReport
|
|
879
907
|
};
|
|
880
|
-
//# sourceMappingURL=chunk-
|
|
908
|
+
//# sourceMappingURL=chunk-IMGJZBFG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/summary-report.ts"],"sourcesContent":["/**\n * Reporting helpers — production summaries and paper-quality figures — sit alongside `reporter.ts` rather\n * than replacing it.\n *\n * Three artefacts:\n *\n * - `summaryTable` Markdown table of per-candidate means,\n * 95% bootstrap CIs, BH-adjusted Wilcoxon\n * p-values, and Cohen's d versus a\n * comparator candidate.\n * - `paretoChart` Abstract spec for a cost vs quality\n * scatter, with gate decisions overlaid.\n * Returns numbers + labels — caller\n * chooses the plotting library.\n * - `gainHistogram`\n * Per-item paired holdout deltas as a\n * histogram spec (bins + counts + median +\n * CI). Same \"data, not images\" contract.\n *\n * The figure types are PlotSpecs — JSON-friendly, library-agnostic.\n * They aren't React components and they aren't PNGs; they are\n * what you'd hand to vega-lite, plotly, matplotlib, or your own\n * Canvas renderer to draw the actual figure.\n */\n\nimport type { GateDecision } from './held-out-gate'\nimport { pairRunRecords } from './paired-arms'\nimport type { FailureClusterReport } from './pipelines/failure-cluster'\nimport { canonicalize, hashJson } from './pre-registration'\nimport type { RunRecord } from './run-record'\nimport {\n benjaminiHochberg,\n confidenceInterval,\n pairedBootstrap,\n pairedCohensDz,\n pairedMde,\n wilcoxonSignedRank,\n} from './statistics'\n\n// ── summaryTable ───────────────────────────────────────────────────────\n\nexport interface SummaryTableOptions {\n /** Comparator candidate id. Wilcoxon + paired Cohen's dz are computed\n * versus this candidate. Required for paired stats columns. */\n comparator?: string\n /** Which split to read scores from. Default 'holdout'. */\n split?: 'search' | 'holdout'\n /** Confidence level for the bootstrap CI on the mean. Default 0.95. */\n confidence?: number\n /** FDR for BH adjustment of the comparison p-values. Default 0.05. */\n fdr?: number\n}\n\nexport interface SummaryTableRow {\n candidateId: string\n n: number\n mean: number\n ciLow: number\n ciHigh: number\n /** BH-adjusted q-value vs comparator, or null when unavailable. */\n qValue: number | null\n /** Paired Cohen's dz vs comparator, or null when the paired variance is zero. */\n cohensD: number | null\n /** Matched observations used for paired comparison, or null on the comparator row. */\n pairedN: number | null\n /** Candidate observations without a comparator match. */\n unpairedCandidateN: number | null\n /** Comparator observations without a candidate match. */\n unpairedComparatorN: number | null\n}\n\nexport interface SummaryTable {\n rows: SummaryTableRow[]\n comparator: string | null\n split: 'search' | 'holdout'\n /** Pre-rendered markdown — drop into a paper or PR. */\n markdown: string\n}\n\n/**\n * Table 1 helper. Buckets runs by `candidateId`, computes mean +\n * bootstrap CI on the chosen split, and (when a comparator is given)\n * BH-adjusted Wilcoxon p + paired Cohen's dz versus that comparator.\n */\nexport function summaryTable(runs: RunRecord[], opts: SummaryTableOptions = {}): SummaryTable {\n const split = opts.split ?? 'holdout'\n const confidence = opts.confidence ?? 0.95\n const fdr = opts.fdr ?? 0.05\n const comparator = opts.comparator ?? null\n const scoreField = split === 'holdout' ? 'holdoutScore' : 'searchScore'\n\n const byCandidate = new Map<string, { runs: RunRecord[]; scores: number[] }>()\n for (const r of runs) {\n if (r.splitTag !== split) continue\n const v = r.outcome[scoreField]\n if (typeof v !== 'number' || !Number.isFinite(v)) continue\n const bucket = byCandidate.get(r.candidateId) ?? { runs: [], scores: [] }\n bucket.runs.push(r)\n bucket.scores.push(v)\n byCandidate.set(r.candidateId, bucket)\n }\n\n const candidateIds = [...byCandidate.keys()].sort()\n const compRuns = comparator ? byCandidate.get(comparator) : undefined\n\n // First pass: per-candidate means + CIs + raw p-values.\n const tentative: Array<SummaryTableRow & { rawP: number | null }> = []\n for (const id of candidateIds) {\n const bucket = byCandidate.get(id)!\n const ci = confidenceInterval(bucket.scores, confidence)\n let rawP: number | null = null\n let d: number | null = null\n let pairedN: number | null = null\n let unpairedCandidateN: number | null = null\n let unpairedComparatorN: number | null = null\n if (comparator && compRuns && id !== comparator) {\n const paired = pairScoresByKey(bucket.runs, compRuns.runs, scoreField)\n pairedN = paired.before.length\n unpairedCandidateN = paired.unpairedCandidateN\n unpairedComparatorN = paired.unpairedComparatorN\n if (paired.before.length >= 6) {\n rawP = wilcoxonSignedRank(paired.before, paired.after).p\n }\n d = pairedCohensDz(paired.before, paired.after)\n }\n tentative.push({\n candidateId: id,\n n: bucket.scores.length,\n mean: ci.mean,\n ciLow: ci.lower,\n ciHigh: ci.upper,\n qValue: rawP,\n cohensD: d,\n pairedN,\n unpairedCandidateN,\n unpairedComparatorN,\n rawP,\n })\n }\n\n // BH-adjust across the comparison set (skip NaN rows / the\n // comparator itself). Adjustment is a no-op when there are 0 or 1\n // comparators.\n if (comparator) {\n const idxs: number[] = []\n const ps: number[] = []\n for (let i = 0; i < tentative.length; i++) {\n const r = tentative[i]!\n if (r.candidateId === comparator) continue\n if (r.rawP === null || !Number.isFinite(r.rawP)) continue\n idxs.push(i)\n ps.push(r.rawP)\n }\n if (ps.length > 0) {\n const { qValues } = benjaminiHochberg(ps, fdr)\n for (let k = 0; k < idxs.length; k++) {\n tentative[idxs[k]!]!.qValue = qValues[k]!\n }\n }\n }\n\n const rows = tentative.map(({ rawP: _rawP, ...rest }) => rest)\n const markdown = renderSummaryTableMarkdown(rows, comparator, split)\n return { rows, comparator, split, markdown }\n}\n\nfunction pairScoresByKey(\n candidate: RunRecord[],\n baseline: RunRecord[],\n scoreField: 'searchScore' | 'holdoutScore',\n): {\n before: number[]\n after: number[]\n unpairedCandidateN: number\n unpairedComparatorN: number\n} {\n const scoredCandidate = candidate.filter((run) => Number.isFinite(run.outcome[scoreField]))\n const scoredBaseline = baseline.filter((run) => Number.isFinite(run.outcome[scoreField]))\n const pairing = pairRunRecords(scoredBaseline, scoredCandidate)\n return {\n before: pairing.pairs.map((pair) => pair.baseline.outcome[scoreField]!),\n after: pairing.pairs.map((pair) => pair.treatment.outcome[scoreField]!),\n unpairedCandidateN: pairing.unpairedTreatment.length,\n unpairedComparatorN: pairing.unpairedBaseline.length,\n }\n}\n\nfunction renderSummaryTableMarkdown(\n rows: SummaryTableRow[],\n comparator: string | null,\n split: 'search' | 'holdout',\n): string {\n const lines: string[] = []\n const cmpLabel = comparator ? ` (vs ${comparator})` : ''\n lines.push(`Summary Table — ${split} split${cmpLabel}`)\n lines.push('')\n lines.push(\"| Candidate | N | Pairs | Unpaired | Mean | 95% CI | q (BH) | Cohen's dz |\")\n lines.push('|---|---:|---:|---:|---:|---|---:|---:|')\n for (const r of rows) {\n const ci = `[${fmt(r.ciLow)}, ${fmt(r.ciHigh)}]`\n const q = r.qValue !== null ? r.qValue.toFixed(4) : '—'\n const d = r.cohensD !== null ? r.cohensD.toFixed(3) : '—'\n const paired = r.pairedN ?? '—'\n const unpaired =\n r.unpairedCandidateN === null || r.unpairedComparatorN === null\n ? '—'\n : `${r.unpairedCandidateN}/${r.unpairedComparatorN}`\n lines.push(\n `| ${r.candidateId} | ${r.n} | ${paired} | ${unpaired} | ${fmt(r.mean)} | ${ci} | ${q} | ${d} |`,\n )\n }\n return lines.join('\\n')\n}\n\n// ── paretoChart ─────────────────────────────────────────────────────\n\nexport interface ParetoPoint {\n candidateId: string\n /** Mean USD cost per run on the chosen split. */\n cost: number\n /** Mean score on the chosen split. */\n quality: number\n /** Number of runs that informed this point. */\n n: number\n /** Whether this candidate is on the Pareto frontier — high\n * quality, low cost, no dominator. */\n onFrontier: boolean\n /** Optional gate verdict for this candidate, if a `GateDecision`\n * for it was passed in. */\n gate?: 'promote' | 'reject'\n}\n\nexport interface ParetoFigureSpec {\n kind: 'pareto-cost-quality'\n split: 'search' | 'holdout'\n points: ParetoPoint[]\n axes: { x: 'costUsd'; y: 'score' }\n}\n\n/**\n * Cost vs quality scatter spec. `gateDecisions` is keyed by\n * candidate id; if present, every point picks up the gate verdict\n * for overlay.\n */\nexport function paretoChart(\n runs: RunRecord[],\n opts: {\n split?: 'search' | 'holdout'\n gateDecisions?: Record<string, GateDecision>\n } = {},\n): ParetoFigureSpec {\n const split = opts.split ?? 'holdout'\n const scoreField = split === 'holdout' ? 'holdoutScore' : 'searchScore'\n\n const buckets = new Map<string, { cost: number[]; quality: number[] }>()\n for (const r of runs) {\n if (r.splitTag !== split) continue\n const v = r.outcome[scoreField]\n if (typeof v !== 'number' || !Number.isFinite(v)) continue\n if (r.costUsd === null) continue\n const bucket = buckets.get(r.candidateId) ?? { cost: [], quality: [] }\n bucket.cost.push(r.costUsd)\n bucket.quality.push(v)\n buckets.set(r.candidateId, bucket)\n }\n\n const points: ParetoPoint[] = []\n for (const [candidateId, bucket] of buckets.entries()) {\n points.push({\n candidateId,\n cost: avg(bucket.cost),\n quality: avg(bucket.quality),\n n: bucket.cost.length,\n onFrontier: false,\n gate: opts.gateDecisions?.[candidateId]\n ? gateLabel(opts.gateDecisions[candidateId]!)\n : undefined,\n })\n }\n\n // Pareto: minimize cost, maximize quality. A point is dominated if\n // some other point has lower-or-equal cost AND higher-or-equal\n // quality with strict inequality somewhere.\n for (const p of points) {\n p.onFrontier = !points.some((q) => q !== p && dominates(q, p))\n }\n\n return {\n kind: 'pareto-cost-quality',\n split,\n axes: { x: 'costUsd', y: 'score' },\n points,\n }\n}\n\nfunction dominates(a: ParetoPoint, b: ParetoPoint): boolean {\n return a.cost <= b.cost && a.quality >= b.quality && (a.cost < b.cost || a.quality > b.quality)\n}\n\nfunction gateLabel(d: GateDecision): ParetoPoint['gate'] {\n return d.promote ? 'promote' : 'reject'\n}\n\n// ── gainHistogram ───────────────────────────────────────────\n\nexport interface GainDistributionBin {\n /** Inclusive lower edge. */\n lo: number\n /** Exclusive upper edge (or inclusive if it's the last bin). */\n hi: number\n /** Number of pairs whose delta lands in this bin. */\n count: number\n}\n\nexport interface GainDistributionFigureSpec {\n kind: 'gain-distribution'\n candidateId: string\n comparator: string\n split: 'search' | 'holdout'\n /** Number of pairs used. */\n n: number\n /** Candidate rows without a comparator match. */\n unpairedCandidateN: number\n /** Comparator rows without a candidate match. */\n unpairedComparatorN: number\n bins: GainDistributionBin[]\n median: number | null\n ci: { low: number; high: number } | null\n}\n\nexport interface GainDistributionOptions {\n /** Number of histogram bins. Default 11 (so the centre is exact at 0). */\n bins?: number\n /** Which split to use. Default 'holdout'. */\n split?: 'search' | 'holdout'\n /** Confidence level for the CI. Default 0.95. */\n confidence?: number\n /** Bootstrap resamples. Default 2000. */\n resamples?: number\n /** Deterministic seed. */\n seed?: number\n}\n\n/**\n * Held-out improvement distribution: per-pair delta (candidate −\n * comparator), histogrammed. Includes the bootstrap CI on the median\n * delta — same primitive the promotion gate uses.\n */\nexport function gainHistogram(\n runs: RunRecord[],\n candidateId: string,\n comparator: string,\n opts: GainDistributionOptions = {},\n): GainDistributionFigureSpec {\n const split = opts.split ?? 'holdout'\n const scoreField = split === 'holdout' ? 'holdoutScore' : 'searchScore'\n const binCount = opts.bins ?? 11\n if (binCount < 1) throw new Error('gainHistogram: bins must be ≥ 1')\n\n const candidate = runs.filter((r) => r.candidateId === candidateId && r.splitTag === split)\n const baseline = runs.filter((r) => r.candidateId === comparator && r.splitTag === split)\n const { before, after, unpairedCandidateN, unpairedComparatorN } = pairScoresByKey(\n candidate,\n baseline,\n scoreField,\n )\n const n = before.length\n\n if (n === 0) {\n return {\n kind: 'gain-distribution',\n candidateId,\n comparator,\n split,\n n: 0,\n unpairedCandidateN,\n unpairedComparatorN,\n bins: [],\n median: null,\n ci: null,\n }\n }\n\n const deltas = before.map((b, i) => after[i]! - b)\n const sortedDeltas = [...deltas].sort((a, b) => a - b)\n const median = medianOfSorted(sortedDeltas)\n const min = sortedDeltas[0]!\n const max = sortedDeltas[sortedDeltas.length - 1]!\n\n // Symmetric bins around the wider of (|min|, |max|) so the chart\n // visually centres on zero without dropping outliers.\n const bound = Math.max(Math.abs(min), Math.abs(max), 1e-6)\n const lo = -bound\n const hi = bound\n const width = (hi - lo) / binCount\n const bins: GainDistributionBin[] = []\n for (let i = 0; i < binCount; i++) {\n bins.push({ lo: lo + i * width, hi: lo + (i + 1) * width, count: 0 })\n }\n for (const d of deltas) {\n let idx = Math.floor((d - lo) / width)\n if (idx < 0) idx = 0\n if (idx >= binCount) idx = binCount - 1\n bins[idx]!.count += 1\n }\n\n const ci = pairedBootstrap(before, after, {\n confidence: opts.confidence ?? 0.95,\n resamples: opts.resamples ?? 2000,\n statistic: 'median',\n seed: opts.seed,\n })\n\n return {\n kind: 'gain-distribution',\n candidateId,\n comparator,\n split,\n n,\n unpairedCandidateN,\n unpairedComparatorN,\n bins,\n median,\n ci: { low: ci.low, high: ci.high },\n }\n}\n\n// ── researchReport ───────────────────────────────────────────────────\n\nexport type ResearchReportDecision =\n | 'promote'\n | 'hold'\n | 'reject'\n | 'equivalent'\n | 'needs_more_data'\n\n/**\n * Hard floor below which a paired comparison is treated as uninformative\n * regardless of `minPairs`. Mirrors the lower limit on Wilcoxon signed-rank\n * exact tables; below this the test has no power to separate effect sizes.\n */\nexport const RESEARCH_REPORT_HARD_PAIR_FLOOR = 6\n\nexport interface ResearchReportOptions {\n /** Human-readable report title. */\n title?: string\n /** Comparator candidate id. Required for statistical decision guidance. */\n comparator?: string\n /** Which split to use for the primary decision. Default 'holdout'. */\n split?: 'search' | 'holdout'\n /** Confidence level used by lower-level report helpers. Default 0.95. */\n confidence?: number\n /** FDR threshold for q-values. Default 0.05. */\n fdr?: number\n /**\n * Soft floor on paired observations before issuing a directional\n * promote / reject. Below this we report `needs_more_data` and surface the\n * minimum detectable effect at the current N. Default 20 — chosen so the\n * Wilcoxon signed-rank approximation is reasonable and so the paired\n * bootstrap CI has non-degenerate coverage. Hard floor is enforced at\n * `RESEARCH_REPORT_HARD_PAIR_FLOOR` (6) regardless of this value.\n */\n minPairs?: number\n /**\n * Region of Practical Equivalence on the paired delta. When a candidate's\n * paired-delta CI is fully contained in `[low, high]`, the decision is\n * `equivalent` rather than `hold`. Sourced from the domain owner — there is\n * no statistically-defensible default.\n */\n rope?: { low: number; high: number }\n /**\n * Power for the minimum detectable effect (MDE) reported on each candidate.\n * Default 0.8.\n */\n mdePower?: number\n /**\n * Two-sided alpha for the MDE. Default matches `fdr` so the reported MDE\n * lines up with the test the report actually runs.\n */\n mdeAlpha?: number\n /** Optional held-out gate decisions keyed by candidate id. */\n gateDecisions?: Record<string, GateDecision>\n /** Optional failure clusters from failureClusterView. */\n failureClusters?: FailureClusterReport\n /** Build gain histograms for these candidates. Defaults to all non-comparator candidates. */\n candidateIds?: string[]\n /** Deterministic bootstrap seed passed to gainHistogram and the posterior helper. */\n seed?: number\n /** Report timestamp. Defaults to current time. */\n generatedAt?: string\n /**\n * Hash of a preregistered protocol (e.g. `signManifest({...}).contentHash`).\n * Embedded verbatim in the report so the analysis can be cited as the\n * preregistered one rather than a post-hoc fishing expedition.\n */\n preregistrationHash?: string\n}\n\nexport interface ResearchReportRecommendation {\n decision: ResearchReportDecision\n candidateId: string | null\n rationale: string[]\n risks: string[]\n nextActions: string[]\n}\n\nexport interface ResearchReportCandidate {\n candidateId: string\n n: number\n mean: number\n ciLow: number\n ciHigh: number\n qValue: number | null\n cohensD: number | null\n meanDeltaVsComparator: number | null\n pairedN: number\n medianGain: number | null\n meanGain: number | null\n gainCi: { low: number; high: number } | null\n /**\n * Bayesian-bootstrap posterior summaries on the paired mean delta.\n * Dirichlet(1, ..., 1) weights represent uncertainty over the empirical\n * distribution of matched deltas.\n */\n prGreaterThanZero: number | null\n prInRope: number | null\n /**\n * Minimum detectable effect (in score units) at the candidate's paired N,\n * the configured power, and the configured alpha. Standardised by the\n * observed paired-delta SD and inverted via `requiredSampleSize`. Reported\n * for every candidate so a `needs_more_data` verdict is actionable.\n */\n mde: number | null\n onParetoFrontier: boolean\n gate?: ParetoPoint['gate']\n decision: ResearchReportDecision\n decisionReason: string\n}\n\nexport interface ResearchReportMethodology {\n /**\n * Plain-language assumptions the report depends on. Read these first when\n * deciding whether the verdict is load-bearing for a launch decision.\n */\n assumptions: string[]\n /** Tests and estimators the verdict was computed from. */\n methods: string[]\n /** Alternatives the author considered and why this report didn't take them. */\n alternatives: string[]\n /** Failure modes — when this report should NOT drive a decision. */\n whenNotToApply: string[]\n /** Citations for the methodological choices above. */\n citations: string[]\n}\n\nexport interface ResearchReport {\n kind: 'agent-eval-research-report'\n title: string\n generatedAt: string\n split: 'search' | 'holdout'\n comparator: string | null\n /**\n * SHA-256 over the canonicalised set of `(runId, candidateId, split)` triples\n * the report was computed from, plus the comparator and split. Stable across\n * key insertion order; recomputable by the reader to verify provenance.\n */\n runFingerprint: string\n preregistrationHash: string | null\n rope: { low: number; high: number } | null\n executiveSummary: string[]\n recommendation: ResearchReportRecommendation\n candidates: ResearchReportCandidate[]\n summary: SummaryTable\n charts: {\n pareto: ParetoFigureSpec\n gains: GainDistributionFigureSpec[]\n }\n methodology: ResearchReportMethodology\n failureClusters?: FailureClusterReport\n markdown: string\n html: string\n}\n\n/**\n * Internal: paired posterior summary on (candidate − comparator) deltas.\n *\n * Returns the bootstrap CI on the median (matching `gainHistogram`) plus\n * Bayesian-bootstrap posterior summaries Pr(Δ>0) and Pr(Δ∈ROPE) computed\n * from Dirichlet(1, ..., 1) weights on the observed paired deltas\n * (Rubin 1981), and the\n * minimum detectable paired effect at the configured power and α.\n *\n * `null` is returned when no paired observations exist; callers must\n * gate on `n` before consuming the bootstrap statistics.\n */\nfunction pairedPosterior(\n runs: RunRecord[],\n candidateId: string,\n comparator: string,\n opts: {\n split: 'search' | 'holdout'\n confidence: number\n seed?: number\n rope: { low: number; high: number } | null\n mdePower: number\n mdeAlpha: number\n },\n): {\n n: number\n meanDelta: number\n medianDelta: number\n sdDelta: number\n ci: { low: number; high: number }\n prGreaterThanZero: number\n prInRope: number | null\n mde: number\n} | null {\n const scoreField = opts.split === 'holdout' ? 'holdoutScore' : 'searchScore'\n const candidate = runs.filter((r) => r.candidateId === candidateId && r.splitTag === opts.split)\n const baseline = runs.filter((r) => r.candidateId === comparator && r.splitTag === opts.split)\n const { before, after } = pairScoresByKey(candidate, baseline, scoreField)\n const n = before.length\n if (n === 0) return null\n\n const deltas = before.map((b, i) => after[i]! - b)\n const meanDelta = deltas.reduce((s, x) => s + x, 0) / n\n const sortedDeltas = [...deltas].sort((a, b) => a - b)\n const medianDelta = medianOfSorted(sortedDeltas)\n const sdDelta = stdev(deltas, meanDelta)\n\n const ci = pairedBootstrap(before, after, {\n confidence: opts.confidence,\n resamples: 2000,\n statistic: 'median',\n seed: opts.seed,\n })\n\n const meanSamples = bayesianBootstrapMeanSamples(deltas, 2000, opts.seed)\n const prGreaterThanZero =\n meanSamples.length === 0 ? 0 : meanSamples.filter((s) => s > 0).length / meanSamples.length\n const prInRope =\n opts.rope === null || meanSamples.length === 0\n ? null\n : meanSamples.filter((s) => s >= opts.rope!.low && s <= opts.rope!.high).length /\n meanSamples.length\n\n const dStandardised = pairedMde({ nPaired: n, alpha: opts.mdeAlpha, power: opts.mdePower })\n const mde = sdDelta === 0 ? 0 : dStandardised * sdDelta\n\n return {\n n,\n meanDelta,\n medianDelta,\n sdDelta,\n ci: { low: ci.low, high: ci.high },\n prGreaterThanZero,\n prInRope,\n mde,\n }\n}\n\nfunction bayesianBootstrapMeanSamples(\n deltas: number[],\n resamples: number,\n seed?: number,\n): number[] {\n const n = deltas.length\n if (n === 0) return []\n if (n === 1) return new Array<number>(resamples).fill(deltas[0]!)\n const rng = seedRng(seed)\n const samples = new Array<number>(resamples)\n for (let b = 0; b < resamples; b++) {\n let weightedSum = 0\n let weightSum = 0\n for (let k = 0; k < n; k++) {\n const weight = -Math.log(Math.max(Number.MIN_VALUE, rng()))\n weightedSum += deltas[k]! * weight\n weightSum += weight\n }\n samples[b] = weightedSum / weightSum\n }\n return samples\n}\n\nfunction seedRng(seed?: number): () => number {\n if (seed === undefined) return Math.random\n let s = seed >>> 0\n return () => {\n s = (s + 0x6d2b79f5) >>> 0\n let t = s\n t = Math.imul(t ^ (t >>> 15), t | 1)\n t ^= t + Math.imul(t ^ (t >>> 7), t | 61)\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296\n }\n}\n\nfunction stdev(xs: number[], mean: number): number {\n if (xs.length < 2) return 0\n let sse = 0\n for (const x of xs) sse += (x - mean) ** 2\n return Math.sqrt(sse / (xs.length - 1))\n}\n\n/**\n * Executive research report for CPO / AI-lead / launch-review consumption.\n *\n * Composes:\n * - `summaryTable` marginal stats with BH-FDR-adjusted q-values\n * - `paretoChart` cost-vs-quality frontier with gate overlay\n * - `gainHistogram` per-candidate paired-delta distribution\n * - paired posterior (this file): bootstrap CI on median, Bayesian-bootstrap Pr(Δ>0),\n * Pr(Δ∈ROPE), MDE at the configured power\n *\n * Decisions are made on paired evidence — never on marginal means alone —\n * and respect any held-out gate decision the caller passes through. The\n * report embeds a SHA-256 fingerprint of the input run set and, optionally,\n * the hash of a preregistered protocol so a downstream reader can verify\n * provenance and that the analysis was the preregistered one.\n *\n * Async because the fingerprint uses Web Crypto via `hashJson`; deterministic\n * for any fixed `runs`, `seed`, and ROPE.\n */\nexport async function researchReport(\n runs: RunRecord[],\n opts: ResearchReportOptions = {},\n): Promise<ResearchReport> {\n const split = opts.split ?? 'holdout'\n const comparator = opts.comparator ?? null\n const confidence = opts.confidence ?? 0.95\n const fdr = opts.fdr ?? 0.05\n const minPairs = Math.max(opts.minPairs ?? 20, RESEARCH_REPORT_HARD_PAIR_FLOOR)\n const rope = opts.rope ?? null\n const mdePower = opts.mdePower ?? 0.8\n const mdeAlpha = opts.mdeAlpha ?? fdr\n const title = opts.title ?? 'Agent Evaluation Research Report'\n const generatedAt = opts.generatedAt ?? new Date().toISOString()\n const preregistrationHash = opts.preregistrationHash ?? null\n\n if (rope && !(Number.isFinite(rope.low) && Number.isFinite(rope.high) && rope.low <= rope.high)) {\n throw new Error(\n `researchReport: rope must satisfy low ≤ high with finite bounds, got ${JSON.stringify(rope)}`,\n )\n }\n\n const summary = summaryTable(runs, {\n comparator: comparator ?? undefined,\n split,\n confidence,\n fdr,\n })\n const pareto = paretoChart(runs, { split, gateDecisions: opts.gateDecisions })\n const candidateIds =\n opts.candidateIds ?? summary.rows.map((r) => r.candidateId).filter((id) => id !== comparator)\n const gains = comparator\n ? candidateIds.map((id) =>\n gainHistogram(runs, id, comparator, {\n split,\n confidence,\n seed: opts.seed,\n }),\n )\n : []\n\n const gainByCandidate = new Map(gains.map((g) => [g.candidateId, g]))\n const paretoByCandidate = new Map(pareto.points.map((p) => [p.candidateId, p]))\n const posteriorByCandidate = new Map<string, ReturnType<typeof pairedPosterior>>()\n if (comparator) {\n for (const id of candidateIds) {\n posteriorByCandidate.set(\n id,\n pairedPosterior(runs, id, comparator, {\n split,\n confidence,\n seed: opts.seed,\n rope,\n mdePower,\n mdeAlpha,\n }),\n )\n }\n }\n\n const candidates = summary.rows\n .map((row) => {\n const gain = gainByCandidate.get(row.candidateId)\n const point = paretoByCandidate.get(row.candidateId)\n const posterior = posteriorByCandidate.get(row.candidateId) ?? null\n const gateDecision = opts.gateDecisions?.[row.candidateId]\n const classified = classifyCandidate(row, {\n comparator,\n posterior,\n point,\n gateDecision,\n split,\n fdr,\n minPairs,\n rope,\n })\n return {\n candidateId: row.candidateId,\n n: row.n,\n mean: row.mean,\n ciLow: row.ciLow,\n ciHigh: row.ciHigh,\n qValue: row.qValue,\n cohensD: row.cohensD,\n meanDeltaVsComparator: posterior ? posterior.meanDelta : null,\n pairedN: posterior?.n ?? gain?.n ?? 0,\n medianGain: posterior ? posterior.medianDelta : gain ? gain.median : null,\n meanGain: posterior ? posterior.meanDelta : null,\n gainCi: posterior ? posterior.ci : gain ? gain.ci : null,\n prGreaterThanZero: posterior ? posterior.prGreaterThanZero : null,\n prInRope: posterior ? posterior.prInRope : null,\n mde: posterior ? posterior.mde : null,\n onParetoFrontier: point?.onFrontier ?? false,\n gate: gateDecision ? gateLabel(gateDecision) : undefined,\n decision: classified.decision,\n decisionReason: classified.reason,\n } satisfies ResearchReportCandidate\n })\n .sort((a, b) => {\n const decisionRank = decisionWeight(b.decision) - decisionWeight(a.decision)\n if (decisionRank !== 0) return decisionRank\n return b.mean - a.mean\n })\n\n const recommendation = buildRecommendation(candidates, {\n comparator,\n failureClusters: opts.failureClusters,\n rope,\n minPairs,\n preregistrationHash,\n })\n const executiveSummary = buildExecutiveSummary(candidates, recommendation, {\n comparator,\n split,\n failureClusters: opts.failureClusters,\n preregistrationHash,\n })\n const methodology = buildMethodology({\n split,\n comparator,\n fdr,\n minPairs,\n rope,\n confidence,\n mdePower,\n mdeAlpha,\n })\n\n const runFingerprint = await hashJson(\n canonicalize({\n triples: runs\n .filter((r) => r.splitTag === split)\n .map((r) => ({ runId: r.runId, candidateId: r.candidateId, splitTag: r.splitTag }))\n .sort((a, b) => a.runId.localeCompare(b.runId)),\n comparator,\n split,\n }),\n )\n\n const markdown = renderResearchMarkdown({\n title,\n generatedAt,\n split,\n comparator,\n rope,\n runFingerprint,\n preregistrationHash,\n executiveSummary,\n recommendation,\n candidates,\n summary,\n pareto,\n gains,\n methodology,\n failureClusters: opts.failureClusters,\n })\n const html = renderResearchHtml(markdown, title)\n\n return {\n kind: 'agent-eval-research-report',\n title,\n generatedAt,\n split,\n comparator,\n runFingerprint,\n preregistrationHash,\n rope,\n executiveSummary,\n recommendation,\n candidates,\n summary,\n charts: { pareto, gains },\n methodology,\n failureClusters: opts.failureClusters,\n markdown,\n html,\n }\n}\n\nfunction buildMethodology(ctx: {\n split: 'search' | 'holdout'\n comparator: string | null\n fdr: number\n minPairs: number\n rope: { low: number; high: number } | null\n confidence: number\n mdePower: number\n mdeAlpha: number\n}): ResearchReportMethodology {\n const assumptions: string[] = [\n 'Pairs are matched by (experimentId, scenarioId, seed); input order does not affect the comparison.',\n 'Paired deltas are exchangeable conditional on the matched scenario — no mid-run distribution shift.',\n `Decisions are pre-specified at fdr=${ctx.fdr}, minPairs=${ctx.minPairs}, confidence=${ctx.confidence}; deviating from these post-hoc invalidates the false-discovery control.`,\n ]\n if (ctx.rope) {\n assumptions.push(\n `The Region of Practical Equivalence ${formatRope(ctx.rope)} is supplied by the domain owner; equivalent verdicts are only meaningful if that range is treated as the standing definition of \"no material difference.\"`,\n )\n }\n if (ctx.comparator === null) {\n assumptions.push('No comparator was configured; this run is descriptive, not causal.')\n }\n const methods: string[] = [\n \"Marginal scores summarised with BH-FDR-adjusted Wilcoxon signed-rank q-values and paired Cohen's dz via summaryTable.\",\n 'Paired evidence summarised with a frequentist bootstrap CI on the median delta and a Dirichlet-weight Bayesian bootstrap for Pr(Δ>0) and Pr(Δ∈ROPE) on the mean delta.',\n `Minimum detectable effect reported per candidate at α=${ctx.mdeAlpha} (two-sided), power=${ctx.mdePower}, standardised by the observed paired-delta SD.`,\n 'Pareto frontier flagged as a separate axis (cost vs quality); a candidate can be on-frontier without winning the paired test.',\n 'Held-out gate decisions, when supplied, override the statistical verdict in the reject direction.',\n ]\n const alternatives: string[] = [\n 'Paired t-test rejected: not robust to the heavy-tailed score distributions common in agent benchmarks.',\n 'Unpaired Mann–Whitney rejected: matched scenarios make pairing free; unpaired throws away that variance reduction.',\n 'Sequential / always-valid inference (e-values, mSPRT) is the right tool for iterative sweeps and is out of scope for this single-look report — preregister and run once, or wrap this report in an alpha-spending schedule.',\n 'Hierarchical Bayesian shrinkage across many candidates is future work; the current ranking uses raw paired statistics.',\n ]\n const whenNotToApply: string[] = [\n `Paired N below ${RESEARCH_REPORT_HARD_PAIR_FLOOR} on any candidate — the bootstrap CI is degenerate.`,\n 'Comparator chosen post-hoc by inspecting the same data; q-values are no longer false-discovery-controlled.',\n 'Scenarios not drawn under a stable preregistered protocol; the report can describe the data but cannot anchor a launch decision.',\n 'Score distributions with mid-run shift (judge model swap, rubric change, infra outage) — pair exchangeability is violated.',\n ]\n const citations: string[] = [\n 'Benjamini, Y. & Hochberg, Y. (1995). Controlling the false discovery rate: a practical and powerful approach to multiple testing. JRSS B, 57(1), 289–300.',\n 'Wilcoxon, F. (1945). Individual comparisons by ranking methods. Biometrics Bulletin, 1(6), 80–83.',\n 'Efron, B. (1979). Bootstrap methods: another look at the jackknife. Annals of Statistics, 7(1), 1–26.',\n 'Rubin, D. B. (1981). The Bayesian bootstrap. Annals of Statistics, 9(1), 130–134.',\n 'Kruschke, J. K. (2018). Rejecting or accepting parameter values in Bayesian estimation. Advances in Methods and Practices in Psychological Science, 1(2), 270–280. (ROPE.)',\n ]\n return { assumptions, methods, alternatives, whenNotToApply, citations }\n}\n\nfunction formatRope(rope: { low: number; high: number }): string {\n return `[${fmt(rope.low)}, ${fmt(rope.high)}]`\n}\n\nfunction classifyCandidate(\n row: SummaryTableRow,\n ctx: {\n comparator: string | null\n posterior: ReturnType<typeof pairedPosterior> | null\n point?: ParetoPoint\n gateDecision?: GateDecision\n split: 'search' | 'holdout'\n fdr: number\n minPairs: number\n rope: { low: number; high: number } | null\n },\n): { decision: ResearchReportDecision; reason: string } {\n if (ctx.comparator && row.candidateId === ctx.comparator) {\n return { decision: 'hold', reason: 'Comparator baseline.' }\n }\n if (ctx.gateDecision && !ctx.gateDecision.promote) {\n const code = ctx.gateDecision.rejectionCode ?? 'rejected'\n return {\n decision: 'reject',\n reason: `Held-out gate returned ${code}: ${ctx.gateDecision.reason}`,\n }\n }\n if (!ctx.comparator) {\n return {\n decision: ctx.point?.onFrontier ? 'hold' : 'needs_more_data',\n reason:\n 'No comparator configured; report ranks candidates but cannot anchor a promotion call.',\n }\n }\n if (!ctx.posterior || ctx.posterior.n < RESEARCH_REPORT_HARD_PAIR_FLOOR) {\n return {\n decision: 'needs_more_data',\n reason: `Only ${ctx.posterior?.n ?? 0} paired observations; below hard floor of ${RESEARCH_REPORT_HARD_PAIR_FLOOR} for any paired inference.`,\n }\n }\n const ci = ctx.posterior.ci\n if (ctx.split === 'search') {\n return {\n decision: 'hold',\n reason:\n 'Search-split evidence is descriptive; release decisions require matched holdout runs.',\n }\n }\n if (ctx.rope && ci.low >= ctx.rope.low && ci.high <= ctx.rope.high) {\n return {\n decision: 'equivalent',\n reason: `Paired-delta CI [${fmt(ci.low)}, ${fmt(ci.high)}] is fully inside ROPE ${formatRope(ctx.rope)}; candidate is practically equivalent to comparator.`,\n }\n }\n const gainPositive = ci.low > 0\n const gainNegative = ci.high < 0\n if (gainNegative) {\n return {\n decision: 'reject',\n reason: `Paired-delta CI [${fmt(ci.low)}, ${fmt(ci.high)}] lies entirely below zero.`,\n }\n }\n if (ctx.posterior.n < ctx.minPairs) {\n return {\n decision: 'needs_more_data',\n reason: `Only ${ctx.posterior.n} paired observations; minimum detectable effect at this N is ${fmt(ctx.posterior.mde)} score units (need ≥ ${ctx.minPairs} pairs to issue a directional verdict).`,\n }\n }\n if (row.qValue !== null && row.qValue <= ctx.fdr && gainPositive) {\n return {\n decision: 'promote',\n reason: `BH-adjusted q=${fmt(row.qValue)} ≤ ${ctx.fdr} and paired-delta CI [${fmt(ci.low)}, ${fmt(ci.high)}] excludes zero; Pr(Δ>0)=${fmt(ctx.posterior.prGreaterThanZero)}.`,\n }\n }\n return {\n decision: 'hold',\n reason: `Pr(Δ>0)=${fmt(ctx.posterior.prGreaterThanZero)} but CI [${fmt(ci.low)}, ${fmt(ci.high)}] crosses zero; effect not decisive at fdr=${ctx.fdr}.`,\n }\n}\n\nfunction buildRecommendation(\n candidates: ResearchReportCandidate[],\n ctx: {\n comparator: string | null\n failureClusters?: FailureClusterReport\n rope: { low: number; high: number } | null\n minPairs: number\n preregistrationHash: string | null\n },\n): ResearchReportRecommendation {\n const nonComparator = candidates.filter((c) => c.candidateId !== ctx.comparator)\n const bestPromote = nonComparator.find((c) => c.decision === 'promote')\n const bestEquivalent = nonComparator.find((c) => c.decision === 'equivalent')\n const chosen = bestPromote ?? bestEquivalent ?? nonComparator[0] ?? null\n const decision: ResearchReportDecision = bestPromote\n ? 'promote'\n : nonComparator.some((c) => c.decision === 'needs_more_data')\n ? 'needs_more_data'\n : bestEquivalent\n ? 'equivalent'\n : nonComparator.some((c) => c.decision === 'hold')\n ? 'hold'\n : 'reject'\n\n const rationale: string[] = []\n const risks: string[] = []\n const nextActions: string[] = []\n\n if (chosen) {\n rationale.push(`${chosen.candidateId}: ${chosen.decisionReason}`)\n if (chosen.gainCi) {\n const probSummary =\n chosen.prGreaterThanZero !== null ? `, Pr(Δ>0)=${fmt(chosen.prGreaterThanZero)}` : ''\n rationale.push(\n `Median paired gain CI: [${fmt(chosen.gainCi.low)}, ${fmt(chosen.gainCi.high)}]${probSummary}.`,\n )\n }\n if (chosen.mde !== null && Number.isFinite(chosen.mde)) {\n rationale.push(`MDE at current paired N=${chosen.pairedN}: ${fmt(chosen.mde)} score units.`)\n }\n }\n if (!ctx.comparator) {\n risks.push('No comparator was configured; verdict is descriptive, not causal.')\n nextActions.push('Re-run with a stable comparator candidate for paired inference.')\n }\n if (!ctx.preregistrationHash) {\n risks.push(\n 'No preregistration hash supplied; readers cannot verify the analysis was specified before data inspection.',\n )\n nextActions.push(\n 'Sign a HypothesisManifest before the next sweep and pass `preregistrationHash` so the report cites it.',\n )\n }\n if (ctx.rope === null && nonComparator.length > 0) {\n risks.push(\n 'No ROPE configured; the report cannot distinguish \"equivalent\" from \"inconclusive\".',\n )\n nextActions.push(\n 'Define a domain-specific Region of Practical Equivalence and pass it to lock in the equivalence threshold.',\n )\n }\n const inconclusive = nonComparator.filter((c) => c.decision === 'needs_more_data')\n if (inconclusive.length > 0) {\n const worst = inconclusive.reduce((a, b) => (b.pairedN < a.pairedN ? b : a))\n risks.push(\n `${inconclusive.length} candidate(s) below soft floor (${ctx.minPairs} pairs); thinnest is ${worst.candidateId} with ${worst.pairedN}.`,\n )\n nextActions.push(\n `Collect at least ${ctx.minPairs - worst.pairedN} more matched holdout runs for ${worst.candidateId}.`,\n )\n }\n const rejected = nonComparator.filter((c) => c.decision === 'reject')\n if (rejected.length > 0) {\n risks.push(\n `${rejected.length} candidate(s) failed the paired test or held-out gate; do not ship those variants.`,\n )\n }\n if (ctx.failureClusters && ctx.failureClusters.clusters.length > 0) {\n const top = ctx.failureClusters.clusters[0]!\n risks.push(`Top failure cluster: ${top.failureClass} across ${top.runCount} run(s).`)\n nextActions.push('Prioritize the largest failure cluster before broad rollout.')\n }\n if (decision === 'promote') {\n nextActions.push('Ship behind the existing promotion gate and monitor canaries.')\n } else if (decision === 'hold') {\n nextActions.push('Keep current production candidate while expanding holdout evidence.')\n } else if (decision === 'equivalent') {\n nextActions.push(\n 'Either keep the comparator (no quality regression) or promote on cost/latency grounds — equivalence does not justify either; the choice is a product decision, not a stats one.',\n )\n } else if (decision === 'reject') {\n nextActions.push(\n 'Do not promote this sweep; inspect failures and generate a revised candidate.',\n )\n }\n\n return {\n decision,\n candidateId: chosen?.candidateId ?? null,\n rationale,\n risks,\n nextActions,\n }\n}\n\nfunction buildExecutiveSummary(\n candidates: ResearchReportCandidate[],\n recommendation: ResearchReportRecommendation,\n ctx: {\n comparator: string | null\n split: 'search' | 'holdout'\n failureClusters?: FailureClusterReport\n preregistrationHash: string | null\n },\n): string[] {\n const lines: string[] = []\n const nonComparator = candidates.filter((c) => c.candidateId !== ctx.comparator)\n lines.push(\n `Evaluated ${nonComparator.length} candidate(s) on the ${ctx.split} split${ctx.comparator ? ` against ${ctx.comparator}` : ''}.`,\n )\n lines.push(\n `Recommendation: ${recommendation.decision}${recommendation.candidateId ? ` ${recommendation.candidateId}` : ''}.`,\n )\n const promoted = nonComparator.filter((c) => c.decision === 'promote').length\n const held = nonComparator.filter((c) => c.decision === 'hold').length\n const equivalent = nonComparator.filter((c) => c.decision === 'equivalent').length\n const rejected = nonComparator.filter((c) => c.decision === 'reject').length\n const more = nonComparator.filter((c) => c.decision === 'needs_more_data').length\n lines.push(\n `Decision mix: ${promoted} promote, ${equivalent} equivalent, ${held} hold, ${rejected} reject, ${more} need more data.`,\n )\n const frontier = nonComparator.filter((c) => c.onParetoFrontier).map((c) => c.candidateId)\n if (frontier.length > 0) lines.push(`Pareto-frontier candidates: ${frontier.join(', ')}.`)\n if (ctx.failureClusters) {\n lines.push(\n `Failure clustering found ${ctx.failureClusters.totalFailures}/${ctx.failureClusters.totalRuns} failed runs across ${ctx.failureClusters.clusters.length} reportable cluster(s).`,\n )\n }\n lines.push(\n ctx.preregistrationHash\n ? `Preregistered analysis: ${ctx.preregistrationHash.slice(0, 12)}…`\n : 'Analysis is post-hoc — no preregistration hash supplied.',\n )\n return lines\n}\n\nfunction renderResearchMarkdown(report: {\n title: string\n generatedAt: string\n split: 'search' | 'holdout'\n comparator: string | null\n executiveSummary: string[]\n recommendation: ResearchReportRecommendation\n candidates: ResearchReportCandidate[]\n summary: SummaryTable\n pareto: ParetoFigureSpec\n gains: GainDistributionFigureSpec[]\n rope: { low: number; high: number } | null\n runFingerprint: string\n preregistrationHash: string | null\n methodology: ResearchReportMethodology\n failureClusters?: FailureClusterReport\n}): string {\n const lines: string[] = []\n lines.push(`# ${report.title}`)\n lines.push('')\n lines.push(`**Generated:** ${report.generatedAt}`)\n lines.push(`**Primary split:** ${report.split}`)\n lines.push(`**Comparator:** ${report.comparator ?? 'not configured'}`)\n lines.push(`**ROPE:** ${report.rope ? formatRope(report.rope) : 'not configured'}`)\n lines.push(`**Run fingerprint:** \\`${report.runFingerprint}\\``)\n lines.push(\n `**Preregistration:** ${report.preregistrationHash ? `\\`${report.preregistrationHash}\\`` : 'none'}`,\n )\n lines.push('')\n lines.push('## Executive Summary')\n lines.push('')\n for (const item of report.executiveSummary) lines.push(`- ${item}`)\n lines.push('')\n lines.push('## Recommendation')\n lines.push('')\n lines.push(`**Decision:** ${report.recommendation.decision}`)\n lines.push(`**Candidate:** ${report.recommendation.candidateId ?? 'N/A'}`)\n lines.push('')\n lines.push('### Rationale')\n lines.push('')\n for (const item of report.recommendation.rationale) lines.push(`- ${item}`)\n lines.push('')\n lines.push('### Risks')\n lines.push('')\n for (const item of report.recommendation.risks.length\n ? report.recommendation.risks\n : ['No material report-level risks detected.']) {\n lines.push(`- ${item}`)\n }\n lines.push('')\n lines.push('### Next Actions')\n lines.push('')\n for (const item of report.recommendation.nextActions) lines.push(`- ${item}`)\n lines.push('')\n lines.push('## Candidate Decision Table')\n lines.push('')\n lines.push(\n '| Candidate | Decision | Mean | Δ̄ | Pr(Δ>0) | q | dz | Paired N | Median Gain CI | MDE | Pareto | Gate |',\n )\n lines.push('|---|---|---:|---:|---:|---:|---:|---:|---|---:|---|---|')\n for (const c of report.candidates) {\n const delta = c.meanDeltaVsComparator === null ? '-' : signed(c.meanDeltaVsComparator)\n const prGt = c.prGreaterThanZero === null ? '-' : c.prGreaterThanZero.toFixed(3)\n const q = c.qValue !== null ? c.qValue.toFixed(4) : '-'\n const d = c.cohensD !== null ? c.cohensD.toFixed(3) : '-'\n const gain = c.gainCi ? `[${fmt(c.gainCi.low)}, ${fmt(c.gainCi.high)}]` : '-'\n const mde = c.mde === null || !Number.isFinite(c.mde) ? '-' : fmt(c.mde)\n lines.push(\n `| ${c.candidateId} | ${c.decision} | ${fmt(c.mean)} | ${delta} | ${prGt} | ${q} | ${d} | ${c.pairedN} | ${gain} | ${mde} | ${c.onParetoFrontier ? 'yes' : 'no'} | ${c.gate ?? '-'} |`,\n )\n }\n lines.push('')\n lines.push('## Statistical Summary')\n lines.push('')\n lines.push(report.summary.markdown)\n lines.push('')\n lines.push('## Methodology')\n lines.push('')\n lines.push('### Assumptions')\n lines.push('')\n for (const item of report.methodology.assumptions) lines.push(`- ${item}`)\n lines.push('')\n lines.push('### Methods')\n lines.push('')\n for (const item of report.methodology.methods) lines.push(`- ${item}`)\n lines.push('')\n lines.push('### Alternatives Considered')\n lines.push('')\n for (const item of report.methodology.alternatives) lines.push(`- ${item}`)\n lines.push('')\n lines.push('### When NOT To Apply')\n lines.push('')\n for (const item of report.methodology.whenNotToApply) lines.push(`- ${item}`)\n lines.push('')\n lines.push('### Citations')\n lines.push('')\n for (const item of report.methodology.citations) lines.push(`- ${item}`)\n lines.push('')\n lines.push('## Chart Specs')\n lines.push('')\n lines.push(\n 'The report carries JSON chart specs for Pareto cost/quality and paired gain histograms.',\n )\n lines.push('')\n lines.push('```json')\n lines.push(JSON.stringify({ pareto: report.pareto, gains: report.gains }, null, 2))\n lines.push('```')\n if (report.failureClusters) {\n lines.push('')\n lines.push('## Failure Clusters')\n lines.push('')\n lines.push('| Failure Class | Runs | Scenarios | Tool | Example |')\n lines.push('|---|---:|---:|---|---|')\n for (const c of report.failureClusters.clusters.slice(0, 10)) {\n lines.push(\n `| ${c.failureClass} | ${c.runCount} | ${c.scenarioIds.length} | ${c.toolName ?? '-'} | ${escapePipes(c.exampleError ?? c.exampleRunId)} |`,\n )\n }\n }\n return lines.join('\\n')\n}\n\nfunction renderResearchHtml(markdown: string, title: string): string {\n const body = markdownToHtml(markdown)\n return [\n '<!doctype html>',\n '<html lang=\"en\">',\n '<head>',\n '<meta charset=\"utf-8\">',\n '<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">',\n `<title>${escapeHtml(title)}</title>`,\n '<style>',\n 'body{font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif;margin:0;color:#172026;background:#f7f8f8;}',\n 'main{max-width:1080px;margin:0 auto;padding:40px 24px 64px;background:#fff;min-height:100vh;}',\n 'h1{font-size:34px;line-height:1.15;margin:0 0 20px;}h2{margin-top:34px;border-top:1px solid #d9dfdf;padding-top:22px;}h3{margin-top:22px;}',\n 'p,li{line-height:1.55;}table{border-collapse:collapse;width:100%;margin:16px 0;font-size:14px;}th,td{border:1px solid #d9dfdf;padding:8px;text-align:left;}th{background:#eef2f2;}',\n 'code,pre{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;}pre{overflow:auto;background:#111827;color:#f9fafb;padding:16px;border-radius:6px;}',\n '</style>',\n '</head>',\n '<body><main>',\n body,\n '</main></body></html>',\n ].join('\\n')\n}\n\nfunction markdownToHtml(markdown: string): string {\n const lines = markdown.split('\\n')\n const html: string[] = []\n let inList = false\n let inCode = false\n let code: string[] = []\n let table: string[] = []\n\n const flushList = () => {\n if (inList) {\n html.push('</ul>')\n inList = false\n }\n }\n const flushTable = () => {\n if (table.length === 0) return\n html.push(renderMarkdownTable(table))\n table = []\n }\n\n for (const line of lines) {\n if (line.startsWith('```')) {\n if (inCode) {\n html.push(`<pre><code>${escapeHtml(code.join('\\n'))}</code></pre>`)\n code = []\n inCode = false\n } else {\n flushList()\n flushTable()\n inCode = true\n }\n continue\n }\n if (inCode) {\n code.push(line)\n continue\n }\n if (line.startsWith('|')) {\n flushList()\n table.push(line)\n continue\n }\n flushTable()\n if (line.startsWith('- ')) {\n if (!inList) {\n html.push('<ul>')\n inList = true\n }\n html.push(`<li>${inlineMarkdown(line.slice(2))}</li>`)\n continue\n }\n flushList()\n if (line.startsWith('# ')) html.push(`<h1>${inlineMarkdown(line.slice(2))}</h1>`)\n else if (line.startsWith('## ')) html.push(`<h2>${inlineMarkdown(line.slice(3))}</h2>`)\n else if (line.startsWith('### ')) html.push(`<h3>${inlineMarkdown(line.slice(4))}</h3>`)\n else if (line.trim() === '') html.push('')\n else html.push(`<p>${inlineMarkdown(line)}</p>`)\n }\n flushList()\n flushTable()\n return html.join('\\n')\n}\n\nfunction renderMarkdownTable(lines: string[]): string {\n const rows = lines\n .filter((line) => !/^\\|[-:\\s|]+\\|$/.test(line))\n .map((line) =>\n line\n .slice(1, -1)\n .split('|')\n .map((cell) => inlineMarkdown(cell.trim())),\n )\n if (rows.length === 0) return ''\n const [head, ...body] = rows\n const th = head!.map((cell) => `<th>${cell}</th>`).join('')\n const trs = body\n .map((row) => `<tr>${row.map((cell) => `<td>${cell}</td>`).join('')}</tr>`)\n .join('\\n')\n return `<table><thead><tr>${th}</tr></thead><tbody>${trs}</tbody></table>`\n}\n\nfunction inlineMarkdown(s: string): string {\n return escapeHtml(s).replace(/\\*\\*([^*]+)\\*\\*/g, '<strong>$1</strong>')\n}\n\nfunction escapeHtml(s: string): string {\n return s\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n}\n\nfunction escapePipes(s: string): string {\n return s.replace(/\\|/g, '\\\\|')\n}\n\nfunction decisionWeight(decision: ResearchReportDecision): number {\n if (decision === 'promote') return 5\n if (decision === 'equivalent') return 4\n if (decision === 'hold') return 3\n if (decision === 'needs_more_data') return 2\n return 1\n}\n\nfunction signed(x: number): string {\n return `${x >= 0 ? '+' : ''}${fmt(x)}`\n}\n\n// ── tiny helpers ─────────────────────────────────────────────────────\n\nfunction avg(xs: number[]): number {\n if (xs.length === 0) return Number.NaN\n return xs.reduce((s, x) => s + x, 0) / xs.length\n}\n\nfunction medianOfSorted(sorted: number[]): number {\n if (sorted.length === 0) return 0\n const mid = Math.floor(sorted.length / 2)\n return sorted.length % 2 === 0 ? (sorted[mid - 1]! + sorted[mid]!) / 2 : sorted[mid]!\n}\n\nfunction fmt(x: number): string {\n if (!Number.isFinite(x)) return String(x)\n return x.toFixed(4)\n}\n"],"mappings":"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","names":[]}
|