modelrig 0.1.0 → 0.2.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.
Files changed (309) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/README.md +41 -11
  3. package/data/PROVENANCE.md +19 -0
  4. package/data/pricing-manual.json +22 -0
  5. package/data/pricing-map.json +46362 -0
  6. package/data/pricing-overlay.json +419 -0
  7. package/dist/adapters/anthropic.d.ts +50 -6
  8. package/dist/adapters/anthropic.d.ts.map +1 -1
  9. package/dist/adapters/anthropic.js +123 -29
  10. package/dist/adapters/build-adapters.d.ts +11 -0
  11. package/dist/adapters/build-adapters.d.ts.map +1 -0
  12. package/dist/adapters/build-adapters.js +45 -0
  13. package/dist/adapters/deepseek.d.ts +6 -0
  14. package/dist/adapters/deepseek.d.ts.map +1 -1
  15. package/dist/adapters/deepseek.js +54 -1
  16. package/dist/adapters/gemini.d.ts.map +1 -1
  17. package/dist/adapters/gemini.js +46 -6
  18. package/dist/adapters/grok.d.ts +9 -0
  19. package/dist/adapters/grok.d.ts.map +1 -1
  20. package/dist/adapters/grok.js +67 -3
  21. package/dist/adapters/openai-compat.d.ts +6 -0
  22. package/dist/adapters/openai-compat.d.ts.map +1 -1
  23. package/dist/adapters/openai-compat.js +58 -1
  24. package/dist/adapters/openai-stream.d.ts +58 -0
  25. package/dist/adapters/openai-stream.d.ts.map +1 -0
  26. package/dist/adapters/openai-stream.js +120 -0
  27. package/dist/adapters/openai.d.ts +6 -0
  28. package/dist/adapters/openai.d.ts.map +1 -1
  29. package/dist/adapters/openai.js +60 -1
  30. package/dist/adapters/sampling.d.ts +28 -0
  31. package/dist/adapters/sampling.d.ts.map +1 -0
  32. package/dist/adapters/sampling.js +34 -0
  33. package/dist/adapters/tool-mapping.d.ts +92 -0
  34. package/dist/adapters/tool-mapping.d.ts.map +1 -0
  35. package/dist/adapters/tool-mapping.js +143 -0
  36. package/dist/artifacts/artifact.d.ts +137 -0
  37. package/dist/artifacts/artifact.d.ts.map +1 -0
  38. package/dist/artifacts/artifact.js +377 -0
  39. package/dist/artifacts/content-uploader.d.ts +87 -0
  40. package/dist/artifacts/content-uploader.d.ts.map +1 -0
  41. package/dist/artifacts/content-uploader.js +124 -0
  42. package/dist/artifacts/index.d.ts +63 -0
  43. package/dist/artifacts/index.d.ts.map +1 -0
  44. package/dist/artifacts/index.js +80 -0
  45. package/dist/artifacts/run-context.d.ts +129 -0
  46. package/dist/artifacts/run-context.d.ts.map +1 -0
  47. package/dist/artifacts/run-context.js +208 -0
  48. package/dist/artifacts/scrub.d.ts +60 -0
  49. package/dist/artifacts/scrub.d.ts.map +1 -0
  50. package/dist/artifacts/scrub.js +139 -0
  51. package/dist/artifacts/step.d.ts +26 -0
  52. package/dist/artifacts/step.d.ts.map +1 -0
  53. package/dist/artifacts/step.js +37 -0
  54. package/dist/artifacts/taint.d.ts +63 -0
  55. package/dist/artifacts/taint.d.ts.map +1 -0
  56. package/dist/artifacts/taint.js +76 -0
  57. package/dist/bakeoff/cache-sim.d.ts +99 -0
  58. package/dist/bakeoff/cache-sim.d.ts.map +1 -0
  59. package/dist/bakeoff/cache-sim.js +126 -0
  60. package/dist/bakeoff/eval-cases.d.ts +96 -0
  61. package/dist/bakeoff/eval-cases.d.ts.map +1 -0
  62. package/dist/bakeoff/eval-cases.js +105 -0
  63. package/dist/bakeoff/report.d.ts +41 -0
  64. package/dist/bakeoff/report.d.ts.map +1 -0
  65. package/dist/bakeoff/report.js +142 -0
  66. package/dist/bakeoff/samples.d.ts +15 -0
  67. package/dist/bakeoff/samples.d.ts.map +1 -1
  68. package/dist/bakeoff/samples.js +20 -0
  69. package/dist/cli.d.ts +1 -3
  70. package/dist/cli.d.ts.map +1 -1
  71. package/dist/cli.js +134 -116
  72. package/dist/config.d.ts +100 -0
  73. package/dist/config.d.ts.map +1 -1
  74. package/dist/config.js +104 -0
  75. package/dist/deprecations.d.ts +32 -0
  76. package/dist/deprecations.d.ts.map +1 -0
  77. package/dist/deprecations.js +59 -0
  78. package/dist/gateway/http-protocol.d.ts +175 -0
  79. package/dist/gateway/http-protocol.d.ts.map +1 -0
  80. package/dist/gateway/http-protocol.js +105 -0
  81. package/dist/index.d.ts +105 -18
  82. package/dist/index.d.ts.map +1 -1
  83. package/dist/index.js +266 -138
  84. package/dist/init/finder.d.ts +15 -0
  85. package/dist/init/finder.d.ts.map +1 -1
  86. package/dist/init/finder.js +46 -0
  87. package/dist/init/init-cli.d.ts +10 -0
  88. package/dist/init/init-cli.d.ts.map +1 -0
  89. package/dist/init/init-cli.js +86 -0
  90. package/dist/init/observe-cli.d.ts +19 -0
  91. package/dist/init/observe-cli.d.ts.map +1 -0
  92. package/dist/init/observe-cli.js +73 -0
  93. package/dist/init/observe.d.ts +81 -0
  94. package/dist/init/observe.d.ts.map +1 -0
  95. package/dist/init/observe.js +245 -0
  96. package/dist/registry/build.d.ts +94 -1
  97. package/dist/registry/build.d.ts.map +1 -1
  98. package/dist/registry/build.js +125 -4
  99. package/dist/registry/cache-policy.d.ts +94 -0
  100. package/dist/registry/cache-policy.d.ts.map +1 -0
  101. package/dist/registry/cache-policy.js +238 -0
  102. package/dist/registry/call-notes.d.ts.map +1 -1
  103. package/dist/registry/call-notes.js +40 -2
  104. package/dist/registry/effective-rate.d.ts +151 -0
  105. package/dist/registry/effective-rate.d.ts.map +1 -0
  106. package/dist/registry/effective-rate.js +156 -0
  107. package/dist/registry/facts.d.ts.map +1 -1
  108. package/dist/registry/facts.js +16 -0
  109. package/dist/registry/parity.d.ts +26 -0
  110. package/dist/registry/parity.d.ts.map +1 -1
  111. package/dist/registry/parity.js +43 -2
  112. package/dist/registry/pricing-shape.d.ts +113 -0
  113. package/dist/registry/pricing-shape.d.ts.map +1 -0
  114. package/dist/registry/pricing-shape.js +123 -0
  115. package/dist/registry/pricing.d.ts +25 -1
  116. package/dist/registry/pricing.d.ts.map +1 -1
  117. package/dist/registry/pricing.js +64 -6
  118. package/dist/rigindex/bootstrap.d.ts +36 -0
  119. package/dist/rigindex/bootstrap.d.ts.map +1 -0
  120. package/dist/rigindex/bootstrap.js +97 -0
  121. package/dist/rigindex/fit.d.ts +24 -0
  122. package/dist/rigindex/fit.d.ts.map +1 -0
  123. package/dist/rigindex/fit.js +76 -0
  124. package/dist/rigindex/gates.d.ts +52 -0
  125. package/dist/rigindex/gates.d.ts.map +1 -0
  126. package/dist/rigindex/gates.js +109 -0
  127. package/dist/rigindex/graph.d.ts +31 -0
  128. package/dist/rigindex/graph.d.ts.map +1 -0
  129. package/dist/rigindex/graph.js +120 -0
  130. package/dist/rigindex/index.d.ts +23 -0
  131. package/dist/rigindex/index.d.ts.map +1 -0
  132. package/dist/rigindex/index.js +112 -0
  133. package/dist/rigindex/types.d.ts +76 -0
  134. package/dist/rigindex/types.d.ts.map +1 -0
  135. package/dist/rigindex/types.js +8 -0
  136. package/dist/routes/cache-lint.d.ts +41 -0
  137. package/dist/routes/cache-lint.d.ts.map +1 -0
  138. package/dist/routes/cache-lint.js +81 -0
  139. package/dist/routes/loader.d.ts +37 -0
  140. package/dist/routes/loader.d.ts.map +1 -1
  141. package/dist/routes/loader.js +138 -0
  142. package/dist/routes/resolved-bundle.d.ts +79 -0
  143. package/dist/routes/resolved-bundle.d.ts.map +1 -0
  144. package/dist/routes/resolved-bundle.js +148 -0
  145. package/dist/routes/sampling-config.d.ts +32 -0
  146. package/dist/routes/sampling-config.d.ts.map +1 -0
  147. package/dist/routes/sampling-config.js +91 -0
  148. package/dist/run/ajv.d.ts +22 -0
  149. package/dist/run/ajv.d.ts.map +1 -0
  150. package/dist/run/ajv.js +32 -0
  151. package/dist/run/cache-handle.d.ts +58 -0
  152. package/dist/run/cache-handle.d.ts.map +1 -0
  153. package/dist/run/cache-handle.js +54 -0
  154. package/dist/run/cache-stamp.d.ts +65 -0
  155. package/dist/run/cache-stamp.d.ts.map +1 -0
  156. package/dist/run/cache-stamp.js +0 -0
  157. package/dist/run/capture-write.d.ts +33 -0
  158. package/dist/run/capture-write.d.ts.map +1 -0
  159. package/dist/run/capture-write.js +55 -0
  160. package/dist/run/cost.d.ts.map +1 -1
  161. package/dist/run/cost.js +1 -1
  162. package/dist/run/dispatch.d.ts +99 -0
  163. package/dist/run/dispatch.d.ts.map +1 -0
  164. package/dist/run/dispatch.js +191 -0
  165. package/dist/run/raw.d.ts +88 -0
  166. package/dist/run/raw.d.ts.map +1 -0
  167. package/dist/run/raw.js +286 -0
  168. package/dist/run/record-attempt.d.ts +40 -0
  169. package/dist/run/record-attempt.d.ts.map +1 -0
  170. package/dist/run/record-attempt.js +99 -0
  171. package/dist/run/resolve.d.ts +75 -2
  172. package/dist/run/resolve.d.ts.map +1 -1
  173. package/dist/run/resolve.js +126 -4
  174. package/dist/run/run.d.ts +14 -0
  175. package/dist/run/run.d.ts.map +1 -1
  176. package/dist/run/run.js +0 -0
  177. package/dist/run/stream.d.ts +49 -0
  178. package/dist/run/stream.d.ts.map +1 -0
  179. package/dist/run/stream.js +87 -0
  180. package/dist/run/validate.d.ts.map +1 -1
  181. package/dist/run/validate.js +6 -7
  182. package/dist/savings/cache-ledger.d.ts +108 -0
  183. package/dist/savings/cache-ledger.d.ts.map +1 -0
  184. package/dist/savings/cache-ledger.js +174 -0
  185. package/dist/savings/cache-reconcile.d.ts +64 -0
  186. package/dist/savings/cache-reconcile.d.ts.map +1 -0
  187. package/dist/savings/cache-reconcile.js +59 -0
  188. package/dist/savings/cache-stats.d.ts +63 -0
  189. package/dist/savings/cache-stats.d.ts.map +1 -0
  190. package/dist/savings/cache-stats.js +108 -0
  191. package/dist/savings/emit.d.ts +13 -0
  192. package/dist/savings/emit.d.ts.map +1 -1
  193. package/dist/savings/emit.js +25 -0
  194. package/dist/savings/swap-execute.d.ts +14 -0
  195. package/dist/savings/swap-execute.d.ts.map +1 -1
  196. package/dist/savings/swap-execute.js +44 -1
  197. package/dist/savings/swaps-client.d.ts +14 -0
  198. package/dist/savings/swaps-client.d.ts.map +1 -1
  199. package/dist/savings/swaps-client.js +16 -2
  200. package/dist/settings/api-fetcher.d.ts +39 -0
  201. package/dist/settings/api-fetcher.d.ts.map +1 -0
  202. package/dist/settings/api-fetcher.js +92 -0
  203. package/dist/settings/index.d.ts +10 -0
  204. package/dist/settings/index.d.ts.map +1 -0
  205. package/dist/settings/index.js +15 -0
  206. package/dist/settings/task-settings.d.ts +91 -0
  207. package/dist/settings/task-settings.d.ts.map +1 -0
  208. package/dist/settings/task-settings.js +116 -0
  209. package/dist/status.d.ts +77 -0
  210. package/dist/status.d.ts.map +1 -0
  211. package/dist/status.js +113 -0
  212. package/dist/telemetry/artifact-store.d.ts +48 -0
  213. package/dist/telemetry/artifact-store.d.ts.map +1 -0
  214. package/dist/telemetry/artifact-store.js +189 -0
  215. package/dist/telemetry/cache-miss-streak.d.ts +79 -0
  216. package/dist/telemetry/cache-miss-streak.d.ts.map +1 -0
  217. package/dist/telemetry/cache-miss-streak.js +99 -0
  218. package/dist/telemetry/capture.d.ts +3 -0
  219. package/dist/telemetry/capture.d.ts.map +1 -1
  220. package/dist/telemetry/capture.js +15 -8
  221. package/dist/telemetry/exporter-setup.d.ts +28 -0
  222. package/dist/telemetry/exporter-setup.d.ts.map +1 -0
  223. package/dist/telemetry/exporter-setup.js +34 -0
  224. package/dist/telemetry/exporter.d.ts +75 -0
  225. package/dist/telemetry/exporter.d.ts.map +1 -1
  226. package/dist/telemetry/exporter.js +324 -47
  227. package/dist/telemetry/health.d.ts +76 -0
  228. package/dist/telemetry/health.d.ts.map +1 -0
  229. package/dist/telemetry/health.js +92 -0
  230. package/dist/telemetry/ingest-protocol.d.ts +152 -3
  231. package/dist/telemetry/ingest-protocol.d.ts.map +1 -1
  232. package/dist/telemetry/ingest-protocol.js +204 -4
  233. package/dist/telemetry/load-error-report.d.ts +24 -0
  234. package/dist/telemetry/load-error-report.d.ts.map +1 -0
  235. package/dist/telemetry/load-error-report.js +50 -0
  236. package/dist/telemetry/otel-map.d.ts +121 -0
  237. package/dist/telemetry/otel-map.d.ts.map +1 -0
  238. package/dist/telemetry/otel-map.js +224 -0
  239. package/dist/telemetry/otel-normalize.d.ts +104 -0
  240. package/dist/telemetry/otel-normalize.d.ts.map +1 -0
  241. package/dist/telemetry/otel-normalize.js +530 -0
  242. package/dist/telemetry/otlp-emitter.d.ts +52 -0
  243. package/dist/telemetry/otlp-emitter.d.ts.map +1 -0
  244. package/dist/telemetry/otlp-emitter.js +144 -0
  245. package/dist/telemetry/otlp-proto.d.ts +29 -0
  246. package/dist/telemetry/otlp-proto.d.ts.map +1 -0
  247. package/dist/telemetry/otlp-proto.js +266 -0
  248. package/dist/telemetry/sinks.d.ts +37 -1
  249. package/dist/telemetry/sinks.d.ts.map +1 -1
  250. package/dist/telemetry/sinks.js +121 -30
  251. package/dist/telemetry/store.d.ts +28 -1
  252. package/dist/telemetry/store.d.ts.map +1 -1
  253. package/dist/telemetry/store.js +218 -3
  254. package/dist/tenancy/api-key-format.d.ts +15 -5
  255. package/dist/tenancy/api-key-format.d.ts.map +1 -1
  256. package/dist/tenancy/api-key-format.js +24 -5
  257. package/dist/types/config-teaching.d.ts +62 -0
  258. package/dist/types/config-teaching.d.ts.map +1 -0
  259. package/dist/types/config-teaching.js +183 -0
  260. package/dist/types/provider.d.ts +122 -1
  261. package/dist/types/provider.d.ts.map +1 -1
  262. package/dist/types/provider.js +21 -1
  263. package/dist/types/route.d.ts +79 -1
  264. package/dist/types/route.d.ts.map +1 -1
  265. package/dist/types/telemetry.d.ts +117 -0
  266. package/dist/types/telemetry.d.ts.map +1 -1
  267. package/dist/validate-cli.d.ts +12 -0
  268. package/dist/validate-cli.d.ts.map +1 -0
  269. package/dist/validate-cli.js +57 -0
  270. package/dist/verify/capture.d.ts +66 -0
  271. package/dist/verify/capture.d.ts.map +1 -0
  272. package/dist/verify/capture.js +180 -0
  273. package/dist/verify/cli-run.d.ts +23 -0
  274. package/dist/verify/cli-run.d.ts.map +1 -0
  275. package/dist/verify/cli-run.js +57 -0
  276. package/dist/verify/diff.d.ts +48 -0
  277. package/dist/verify/diff.d.ts.map +1 -0
  278. package/dist/verify/diff.js +157 -0
  279. package/dist/verify/index.d.ts +17 -0
  280. package/dist/verify/index.d.ts.map +1 -0
  281. package/dist/verify/index.js +24 -0
  282. package/dist/verify/report.d.ts +14 -0
  283. package/dist/verify/report.d.ts.map +1 -0
  284. package/dist/verify/report.js +88 -0
  285. package/dist/verify/types.d.ts +101 -0
  286. package/dist/verify/types.d.ts.map +1 -0
  287. package/dist/verify/types.js +17 -0
  288. package/dist/verify/verify.d.ts +29 -0
  289. package/dist/verify/verify.d.ts.map +1 -0
  290. package/dist/verify/verify.js +148 -0
  291. package/dist/watch/evaluate.d.ts +162 -3
  292. package/dist/watch/evaluate.d.ts.map +1 -1
  293. package/dist/watch/evaluate.js +61 -0
  294. package/dist/watch/monitor.d.ts +116 -0
  295. package/dist/watch/monitor.d.ts.map +1 -0
  296. package/dist/watch/monitor.js +199 -0
  297. package/dist/watch/proposals.d.ts +20 -4
  298. package/dist/watch/proposals.d.ts.map +1 -1
  299. package/dist/watch/proposals.js +31 -3
  300. package/dist/watch/run.d.ts +33 -0
  301. package/dist/watch/run.d.ts.map +1 -1
  302. package/dist/watch/run.js +39 -0
  303. package/package.json +7 -1
  304. package/registry/LICENSE +20 -0
  305. package/registry/leaderboard.json +3639 -0
  306. package/registry/registry.json +7263 -0
  307. package/dist/telemetry/supabase-exporter.d.ts +0 -64
  308. package/dist/telemetry/supabase-exporter.d.ts.map +0 -1
  309. package/dist/telemetry/supabase-exporter.js +0 -211
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ /**
3
+ * `@step` / `step()` — execution scope for NON-LLM stages (spec §1.4, §9).
4
+ *
5
+ * A step is an execution operation ("what happened"). For LLM calls `rig.run()`
6
+ * auto-creates a ground-truth step (its inference row already exists); this
7
+ * wrapper is for the OTHER stages — retrieval, transforms, tool calls, external
8
+ * APIs — which have no inference row.
9
+ *
10
+ * ── WHY THIS WRITES NO STEP ROW THIS MISSION ──────────────────────────────
11
+ * The `steps` CHECK requires EXACTLY ONE reference: ground-truth (an inference)
12
+ * XOR attested (a foreign OTLP span). A pure non-LLM stage has neither — its
13
+ * span reference arrives with OTLP INGEST (E3), and nothing writes `attested`
14
+ * rows this mission. So `step()` establishes a NAMED execution scope inside the
15
+ * active run context (chunk B attributes artifact save/load lineage to it) and
16
+ * runs the body; it does not persist a row until it has a valid reference.
17
+ *
18
+ * Instrumentation never changes the wrapped function's return value, and never
19
+ * throws into it — a disabled or absent run context just runs the body.
20
+ */
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ exports.step = step;
23
+ const run_context_1 = require("./run-context");
24
+ /**
25
+ * Run `fn` as a named execution step within the active run context. Returns the
26
+ * body's value unchanged (a promise if the body is async). Outside a run context
27
+ * (namespace disabled, or no run.start on this path) it simply runs the body.
28
+ */
29
+ function step(key, fn) {
30
+ const ctx = (0, run_context_1.getActiveRunContext)();
31
+ if (ctx === null)
32
+ return fn();
33
+ // Reserve a unique step key so chunk B can attribute this scope's artifact
34
+ // saves/loads. No row is written (see header).
35
+ ctx.nextStepKey(key);
36
+ return fn();
37
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Taint propagation — downstream closure over the artifact graph (Expansion E4
3
+ * chunk C, spec §Chunk C). "If THIS artifact is bad, what derived from it?" —
4
+ * the condemn-or-clear question. Follows lineage edges forward (an edge means
5
+ * `from` was produced from / consumed `to`, so `from` is DOWNSTREAM of `to`) and
6
+ * `supersedes` chains forward (a version that supersedes a tainted one inherits
7
+ * the taint). Served as a bounded DTO the artifact page renders and the replay
8
+ * affordance (chunk A) links into.
9
+ *
10
+ * ── BOUNDED + HONEST ──────────────────────────────────────────────────────
11
+ * Real graphs can be deep, wide, or cyclic (a redaction supersedes an artifact
12
+ * that a later step also consumed). So the closure is:
13
+ * - depth-bounded (default 20) — `depthLimited` says more may exist deeper;
14
+ * - cycle-guarded (a visited set — a node is expanded once);
15
+ * - count-capped (default 500) — `truncated` says "≥ cap", never a silent cut.
16
+ * The production path is a recursive CTE with the SAME bounds (server); this pure
17
+ * function is the algorithm both the CTE golden-tests against and a caller can
18
+ * run over fetched edges without a database.
19
+ */
20
+ /** A stored lineage edge: `from` depends on `to` (kind = how). */
21
+ export interface TaintEdge {
22
+ readonly from: string;
23
+ readonly to: string;
24
+ readonly kind: "consumed" | "derived_from";
25
+ }
26
+ /** A supersession: `artifactId` supersedes `supersedesArtifactId` (a newer
27
+ * version). The superseder is downstream of what it supersedes. */
28
+ export interface Supersession {
29
+ readonly artifactId: string;
30
+ readonly supersedesArtifactId: string;
31
+ }
32
+ export interface TaintNode {
33
+ readonly id: string;
34
+ /** Hops from the root (1 = a direct descendant). */
35
+ readonly depth: number;
36
+ }
37
+ export interface TaintClosure {
38
+ readonly root: string;
39
+ /** Downstream artifacts, breadth-first (nearest first), deduped, capped. */
40
+ readonly downstream: readonly TaintNode[];
41
+ /** `downstream.length` — the count actually returned. */
42
+ readonly count: number;
43
+ /** True when the closure hit the count cap — the honest "≥ count" signal. */
44
+ readonly truncated: boolean;
45
+ /** True when the frontier reached `maxDepth` — more may exist deeper. */
46
+ readonly depthLimited: boolean;
47
+ readonly maxDepth: number;
48
+ readonly cap: number;
49
+ }
50
+ export interface TaintOptions {
51
+ /** Max hops from the root (default 20). */
52
+ readonly maxDepth?: number;
53
+ /** Max downstream nodes returned before truncation (default 500). */
54
+ readonly cap?: number;
55
+ }
56
+ export declare const TAINT_DEFAULT_MAX_DEPTH = 20;
57
+ export declare const TAINT_DEFAULT_CAP = 500;
58
+ /**
59
+ * Downstream closure from `root`. Pure and deterministic. Cycle-guarded via a
60
+ * visited set; depth-bounded; count-capped with an honest `truncated` flag.
61
+ */
62
+ export declare function computeDownstreamClosure(root: string, edges: readonly TaintEdge[], supersessions: readonly Supersession[], options?: TaintOptions): TaintClosure;
63
+ //# sourceMappingURL=taint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"taint.d.ts","sourceRoot":"","sources":["../../src/artifacts/taint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,kEAAkE;AAClE,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,cAAc,CAAC;CAC5C;AAED;mEACmE;AACnE,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;CACvC;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,oDAAoD;IACpD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4EAA4E;IAC5E,QAAQ,CAAC,UAAU,EAAE,SAAS,SAAS,EAAE,CAAC;IAC1C,yDAAyD;IACzD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,6EAA6E;IAC7E,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,yEAAyE;IACzE,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,2CAA2C;IAC3C,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,qEAAqE;IACrE,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAC1C,eAAO,MAAM,iBAAiB,MAAM,CAAC;AAErC;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,SAAS,SAAS,EAAE,EAC3B,aAAa,EAAE,SAAS,YAAY,EAAE,EACtC,OAAO,GAAE,YAAiB,GACzB,YAAY,CA2Cd"}
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ /**
3
+ * Taint propagation — downstream closure over the artifact graph (Expansion E4
4
+ * chunk C, spec §Chunk C). "If THIS artifact is bad, what derived from it?" —
5
+ * the condemn-or-clear question. Follows lineage edges forward (an edge means
6
+ * `from` was produced from / consumed `to`, so `from` is DOWNSTREAM of `to`) and
7
+ * `supersedes` chains forward (a version that supersedes a tainted one inherits
8
+ * the taint). Served as a bounded DTO the artifact page renders and the replay
9
+ * affordance (chunk A) links into.
10
+ *
11
+ * ── BOUNDED + HONEST ──────────────────────────────────────────────────────
12
+ * Real graphs can be deep, wide, or cyclic (a redaction supersedes an artifact
13
+ * that a later step also consumed). So the closure is:
14
+ * - depth-bounded (default 20) — `depthLimited` says more may exist deeper;
15
+ * - cycle-guarded (a visited set — a node is expanded once);
16
+ * - count-capped (default 500) — `truncated` says "≥ cap", never a silent cut.
17
+ * The production path is a recursive CTE with the SAME bounds (server); this pure
18
+ * function is the algorithm both the CTE golden-tests against and a caller can
19
+ * run over fetched edges without a database.
20
+ */
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ exports.TAINT_DEFAULT_CAP = exports.TAINT_DEFAULT_MAX_DEPTH = void 0;
23
+ exports.computeDownstreamClosure = computeDownstreamClosure;
24
+ exports.TAINT_DEFAULT_MAX_DEPTH = 20;
25
+ exports.TAINT_DEFAULT_CAP = 500;
26
+ /**
27
+ * Downstream closure from `root`. Pure and deterministic. Cycle-guarded via a
28
+ * visited set; depth-bounded; count-capped with an honest `truncated` flag.
29
+ */
30
+ function computeDownstreamClosure(root, edges, supersessions, options = {}) {
31
+ const maxDepth = options.maxDepth ?? exports.TAINT_DEFAULT_MAX_DEPTH;
32
+ const cap = options.cap ?? exports.TAINT_DEFAULT_CAP;
33
+ // Adjacency: node → its direct downstream nodes. An edge (from → to) makes
34
+ // `from` downstream of `to`; a supersession makes the superseder downstream of
35
+ // what it supersedes. Both collapse into the same "who depends on X" map.
36
+ const downstreamOf = new Map();
37
+ const add = (parent, child) => {
38
+ const cur = downstreamOf.get(parent);
39
+ if (cur === undefined)
40
+ downstreamOf.set(parent, [child]);
41
+ else
42
+ cur.push(child);
43
+ };
44
+ for (const e of edges)
45
+ add(e.to, e.from);
46
+ for (const s of supersessions)
47
+ add(s.supersedesArtifactId, s.artifactId);
48
+ const visited = new Set([root]); // root is never listed as its own downstream
49
+ const downstream = [];
50
+ let truncated = false;
51
+ let depthLimited = false;
52
+ // BFS by depth so `downstream` is nearest-first and depth is well-defined.
53
+ let frontier = [root];
54
+ for (let depth = 1; depth <= maxDepth && frontier.length > 0; depth++) {
55
+ const next = [];
56
+ for (const node of frontier) {
57
+ for (const child of downstreamOf.get(node) ?? []) {
58
+ if (visited.has(child))
59
+ continue; // cycle / diamond guard: expand once
60
+ visited.add(child);
61
+ if (downstream.length >= cap) {
62
+ truncated = true;
63
+ continue; // keep draining `visited` so we don't re-count, but add no more
64
+ }
65
+ downstream.push({ id: child, depth });
66
+ next.push(child);
67
+ }
68
+ }
69
+ frontier = next;
70
+ }
71
+ // If we stopped because we hit maxDepth but there was still a frontier, more
72
+ // may exist deeper than we looked.
73
+ if (frontier.length > 0)
74
+ depthLimited = true;
75
+ return { root, downstream, count: downstream.length, truncated, depthLimited, maxDepth, cap };
76
+ }
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Virtual cache simulator — cache-aware-routing spec §5.4, [Briefing §26]. PURE:
3
+ * no store/clock/network. THE fix for the independent-turn evaluation fallacy:
4
+ * replay currently scores every sample COLD, so a bake-off report can never see
5
+ * that a candidate would run warm at production cadence. This walks a candidate's
6
+ * call sequence in timestamp order, maintaining a per-candidate VIRTUAL cache
7
+ * from the POLICY REGISTRY (TTL + min-cacheable + refresh-on-read), and emits a
8
+ * per-call {read, write, uncached} token split.
9
+ *
10
+ * ── SIMULATOR HONESTY (spec §5.4, §10 credibility risk) ───────────────────
11
+ * • State comes ONLY from the policy registry — never from a guessed model of
12
+ * how a provider caches. An undocumented TTL uses a CONSERVATIVE fallback
13
+ * (short), so the sim UNDER-credits rather than over-credits warmth.
14
+ * • The output is always LABELLED simulation. The odometer stays measured-only
15
+ * (this module never feeds it) — simulator output is never "verified savings".
16
+ * • Cache identity is the caller-supplied `prefixFingerprint`. A template
17
+ * mutation or a tool-order change is, by construction, a DIFFERENT
18
+ * fingerprint — so a mutation forces a miss/rewrite with no special-casing
19
+ * here (the synthetic tests exercise exactly that).
20
+ *
21
+ * A "switch-and-return" is modelled by a time GAP in this candidate's sequence:
22
+ * if the gap is within TTL the return is a read (still warm); if it exceeds TTL
23
+ * the cache has expired and the return rewrites. Nothing about the OTHER
24
+ * candidate is needed — each candidate is simulated over its own calls.
25
+ */
26
+ import type { CachePolicy } from "../registry/cache-policy";
27
+ import type { EffectiveRates } from "../registry/effective-rate";
28
+ import type { ProviderId } from "../types/provider";
29
+ /** One call in a candidate's sequence. `prefixTokens` is the cacheable prefix
30
+ * length; `promptTokens` the whole input (prefix + volatile tail). */
31
+ export interface SimCall {
32
+ readonly tsMs: number;
33
+ readonly prefixFingerprint: string;
34
+ readonly prefixTokens: number;
35
+ readonly promptTokens: number;
36
+ }
37
+ /** Per-call token split — a disjoint partition of `promptTokens`:
38
+ * `simulated_read + simulated_write + simulated_uncached === promptTokens`. */
39
+ export interface SimCallResult {
40
+ readonly simulated_read: number;
41
+ readonly simulated_write: number;
42
+ readonly simulated_uncached: number;
43
+ /** True when the cacheable prefix was served from a warm virtual cache. */
44
+ readonly hit: boolean;
45
+ }
46
+ /** The policy assumptions the sim ran under — PRINTED with every report so the
47
+ * simulation's basis is never hidden (spec §5.4 "assumptions printed"). */
48
+ export interface CacheSimAssumptions {
49
+ readonly provider: string;
50
+ readonly model: string;
51
+ readonly cacheSupported: boolean;
52
+ /** TTL used to expire virtual entries (seconds). */
53
+ readonly ttlSeconds: number;
54
+ /** false ⇒ `ttlSeconds` is the conservative fallback, not a published value. */
55
+ readonly ttlSourced: boolean;
56
+ readonly minCacheableTokens: number;
57
+ readonly refreshOnRead: boolean;
58
+ }
59
+ export interface CacheSimResult {
60
+ readonly perCall: readonly SimCallResult[];
61
+ readonly totalRead: number;
62
+ readonly totalWrite: number;
63
+ readonly totalUncached: number;
64
+ readonly hitCount: number;
65
+ readonly assumptions: CacheSimAssumptions;
66
+ }
67
+ /**
68
+ * Conservative TTL (seconds) for a provider that publishes none
69
+ * (`default_ttl_seconds: null`). Deliberately SHORT — the shortest real registry
70
+ * TTL (Anthropic 5m) — so the simulator never over-credits warmth for a provider
71
+ * whose true window is longer but undocumented (spec §5.4 honesty).
72
+ */
73
+ export declare const FALLBACK_SIM_TTL_SECONDS = 300;
74
+ export interface CacheSimOptions {
75
+ /** Policy override — injected for tests; defaults to the live registry. */
76
+ readonly policy?: CachePolicy;
77
+ }
78
+ /**
79
+ * Simulate one candidate's cache trajectory over `calls`. Calls are processed in
80
+ * ascending timestamp order (a copy is sorted; the input is not mutated). The
81
+ * per-call results are returned in that same timestamp order.
82
+ */
83
+ export declare function simulateCache(provider: ProviderId, model: string, calls: readonly SimCall[], opts?: CacheSimOptions): CacheSimResult;
84
+ /**
85
+ * Cache-ADJUSTED input cost of a simulated trajectory (spec §5.4): the read /
86
+ * write / uncached token buckets each priced at their own rate, with the
87
+ * fail-closed fallback to the input rate (dev-rule L5) when a cache rate is
88
+ * unverified. This is what the candidate WOULD pay at production cadence — the
89
+ * right-hand column of the bake-off's side-by-side.
90
+ */
91
+ export declare function simulatedInputCostUsd(sim: CacheSimResult, rates: EffectiveRates): number;
92
+ /**
93
+ * Measured-COLD input cost (spec §5.4): every input token at the list input
94
+ * rate — what independent-turn replay actually pays. The left-hand column. This
95
+ * equals `simulatedInputCostUsd` exactly when the sim produced zero reads and
96
+ * zero writes (the regression pin — no cache activity, no divergence).
97
+ */
98
+ export declare function coldInputCostUsd(sim: CacheSimResult, rates: EffectiveRates): number;
99
+ //# sourceMappingURL=cache-sim.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-sim.d.ts","sourceRoot":"","sources":["../../src/bakeoff/cache-sim.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD;sEACsE;AACtE,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED;+EAC+E;AAC/E,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,2EAA2E;IAC3E,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;CACvB;AAED;2EAC2E;AAC3E,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC,oDAAoD;IACpD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,gFAAgF;IAChF,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;CACjC;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,CAAC;IAC3C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,mBAAmB,CAAC;CAC3C;AAED;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAE5C,MAAM,WAAW,eAAe;IAC9B,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;CAC/B;AASD;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,UAAU,EACpB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,SAAS,OAAO,EAAE,EACzB,IAAI,GAAE,eAAoB,GACzB,cAAc,CAkEhB;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAIxF;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAEnF"}
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ /**
3
+ * Virtual cache simulator — cache-aware-routing spec §5.4, [Briefing §26]. PURE:
4
+ * no store/clock/network. THE fix for the independent-turn evaluation fallacy:
5
+ * replay currently scores every sample COLD, so a bake-off report can never see
6
+ * that a candidate would run warm at production cadence. This walks a candidate's
7
+ * call sequence in timestamp order, maintaining a per-candidate VIRTUAL cache
8
+ * from the POLICY REGISTRY (TTL + min-cacheable + refresh-on-read), and emits a
9
+ * per-call {read, write, uncached} token split.
10
+ *
11
+ * ── SIMULATOR HONESTY (spec §5.4, §10 credibility risk) ───────────────────
12
+ * • State comes ONLY from the policy registry — never from a guessed model of
13
+ * how a provider caches. An undocumented TTL uses a CONSERVATIVE fallback
14
+ * (short), so the sim UNDER-credits rather than over-credits warmth.
15
+ * • The output is always LABELLED simulation. The odometer stays measured-only
16
+ * (this module never feeds it) — simulator output is never "verified savings".
17
+ * • Cache identity is the caller-supplied `prefixFingerprint`. A template
18
+ * mutation or a tool-order change is, by construction, a DIFFERENT
19
+ * fingerprint — so a mutation forces a miss/rewrite with no special-casing
20
+ * here (the synthetic tests exercise exactly that).
21
+ *
22
+ * A "switch-and-return" is modelled by a time GAP in this candidate's sequence:
23
+ * if the gap is within TTL the return is a read (still warm); if it exceeds TTL
24
+ * the cache has expired and the return rewrites. Nothing about the OTHER
25
+ * candidate is needed — each candidate is simulated over its own calls.
26
+ */
27
+ Object.defineProperty(exports, "__esModule", { value: true });
28
+ exports.FALLBACK_SIM_TTL_SECONDS = void 0;
29
+ exports.simulateCache = simulateCache;
30
+ exports.simulatedInputCostUsd = simulatedInputCostUsd;
31
+ exports.coldInputCostUsd = coldInputCostUsd;
32
+ const cache_policy_1 = require("../registry/cache-policy");
33
+ /**
34
+ * Conservative TTL (seconds) for a provider that publishes none
35
+ * (`default_ttl_seconds: null`). Deliberately SHORT — the shortest real registry
36
+ * TTL (Anthropic 5m) — so the simulator never over-credits warmth for a provider
37
+ * whose true window is longer but undocumented (spec §5.4 honesty).
38
+ */
39
+ exports.FALLBACK_SIM_TTL_SECONDS = 300;
40
+ /**
41
+ * Simulate one candidate's cache trajectory over `calls`. Calls are processed in
42
+ * ascending timestamp order (a copy is sorted; the input is not mutated). The
43
+ * per-call results are returned in that same timestamp order.
44
+ */
45
+ function simulateCache(provider, model, calls, opts = {}) {
46
+ const policy = opts.policy ?? (0, cache_policy_1.getCachePolicy)(provider);
47
+ const ttlSourced = policy.cache_supported && policy.default_ttl_seconds !== null;
48
+ const ttlSeconds = policy.cache_supported && policy.default_ttl_seconds !== null
49
+ ? policy.default_ttl_seconds
50
+ : exports.FALLBACK_SIM_TTL_SECONDS;
51
+ const ttlMs = ttlSeconds * 1000;
52
+ const minCacheable = policy.min_cacheable_tokens ?? 0;
53
+ const assumptions = {
54
+ provider,
55
+ model,
56
+ cacheSupported: policy.cache_supported,
57
+ ttlSeconds,
58
+ ttlSourced,
59
+ minCacheableTokens: minCacheable,
60
+ refreshOnRead: policy.ttl_refresh_on_read,
61
+ };
62
+ const ordered = [...calls].sort((a, b) => a.tsMs - b.tsMs);
63
+ const cache = new Map();
64
+ const perCall = [];
65
+ let totalRead = 0;
66
+ let totalWrite = 0;
67
+ let totalUncached = 0;
68
+ let hitCount = 0;
69
+ for (const call of ordered) {
70
+ const prompt = Math.max(call.promptTokens, 0);
71
+ const prefix = Math.min(Math.max(call.prefixTokens, 0), prompt);
72
+ // Provider does not cache on our path, or the prefix is below the minimum
73
+ // cacheable size: caching does not engage — the whole prompt is uncached and
74
+ // NO virtual state is written (spec §5.4: min-cacheable gates the write).
75
+ if (!policy.cache_supported || prefix < minCacheable || prefix === 0) {
76
+ perCall.push({ simulated_read: 0, simulated_write: 0, simulated_uncached: prompt, hit: false });
77
+ totalUncached += prompt;
78
+ continue;
79
+ }
80
+ const entry = cache.get(call.prefixFingerprint);
81
+ const warm = entry !== undefined && call.tsMs < entry.expiresAtMs;
82
+ if (warm) {
83
+ // HIT: read the cached prefix; the volatile tail is uncached.
84
+ const read = Math.min(prefix, entry.tokens);
85
+ const uncached = prompt - read;
86
+ perCall.push({ simulated_read: read, simulated_write: 0, simulated_uncached: uncached, hit: true });
87
+ totalRead += read;
88
+ totalUncached += uncached;
89
+ hitCount += 1;
90
+ // TTL base: a read refreshes the window only when the policy says so;
91
+ // otherwise the entry keeps expiring from its WRITE time.
92
+ if (policy.ttl_refresh_on_read)
93
+ entry.expiresAtMs = call.tsMs + ttlMs;
94
+ }
95
+ else {
96
+ // MISS (cold or expired): WRITE the prefix into cache; the tail is uncached.
97
+ const uncached = prompt - prefix;
98
+ perCall.push({ simulated_read: 0, simulated_write: prefix, simulated_uncached: uncached, hit: false });
99
+ totalWrite += prefix;
100
+ totalUncached += uncached;
101
+ cache.set(call.prefixFingerprint, { tokens: prefix, expiresAtMs: call.tsMs + ttlMs });
102
+ }
103
+ }
104
+ return { perCall, totalRead, totalWrite, totalUncached, hitCount, assumptions };
105
+ }
106
+ /**
107
+ * Cache-ADJUSTED input cost of a simulated trajectory (spec §5.4): the read /
108
+ * write / uncached token buckets each priced at their own rate, with the
109
+ * fail-closed fallback to the input rate (dev-rule L5) when a cache rate is
110
+ * unverified. This is what the candidate WOULD pay at production cadence — the
111
+ * right-hand column of the bake-off's side-by-side.
112
+ */
113
+ function simulatedInputCostUsd(sim, rates) {
114
+ const readRate = rates.cacheRead ?? rates.input;
115
+ const writeRate = rates.cacheWrite ?? rates.input;
116
+ return sim.totalRead * readRate + sim.totalWrite * writeRate + sim.totalUncached * rates.input;
117
+ }
118
+ /**
119
+ * Measured-COLD input cost (spec §5.4): every input token at the list input
120
+ * rate — what independent-turn replay actually pays. The left-hand column. This
121
+ * equals `simulatedInputCostUsd` exactly when the sim produced zero reads and
122
+ * zero writes (the regression pin — no cache activity, no divergence).
123
+ */
124
+ function coldInputCostUsd(sim, rates) {
125
+ return (sim.totalRead + sim.totalWrite + sim.totalUncached) * rates.input;
126
+ }
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Bake-off input selection from eval cases — Expansion E4 chunk A (spec §3).
3
+ *
4
+ * `bakeoff --from-eval-cases <task>` replays the promoted regression/golden cases
5
+ * for a task instead of the last-N captures. A case only holds METADATA (a source
6
+ * artifact id); the INPUT to replay lives in a LOCAL capture, reachable through
7
+ * the chain:
8
+ *
9
+ * eval_case → source artifact (run_id, step_key) → step's inference_id → capture
10
+ *
11
+ * Any link may be missing — the source artifact predates local retention, the
12
+ * route never had `capture: true`, the capture was evicted. When it is, the case
13
+ * is UNREHYDRATABLE. The load-bearing doctrine (spec §3.3): the bake-off runs on
14
+ * the rehydratable subset and NAMES every refused case with a reason — NEVER a
15
+ * silent subset. `selectEvalCaseCaptures` returns both halves so the CLI can
16
+ * print the honest-refusal report before it spends a cent.
17
+ */
18
+ import type { CaptureRow, CaptureStore } from "../telemetry/capture";
19
+ import type { ArtifactStore } from "../telemetry/artifact-store";
20
+ import type { ExportableTelemetryStore } from "../telemetry/store";
21
+ import type { EvalCaseRow } from "../types/telemetry";
22
+ import type { RunDeps } from "../run/run";
23
+ import { type BakeoffSamplesSink } from "./engine";
24
+ import type { KeepOutputsMode } from "./samples";
25
+ import type { BakeoffArtifact } from "./types";
26
+ /** The narrow store surface the selection reads — kept minimal so this module
27
+ * depends on four reads, not three whole stores. */
28
+ export interface EvalCaseSelectionDeps {
29
+ readonly readEvalCasesByTask: (task: string) => EvalCaseRow[];
30
+ readonly getArtifactById: (id: string) => {
31
+ runId: string | null;
32
+ stepKey: string | null;
33
+ } | null;
34
+ readonly getStepInferenceId: (runId: string, stepKey: string) => string | null;
35
+ readonly getCaptureByInferenceId: (inferenceId: string) => CaptureRow | null;
36
+ }
37
+ export interface RehydratedCase {
38
+ readonly caseId: string;
39
+ readonly capture: CaptureRow;
40
+ }
41
+ export interface EvalCaseRefusal {
42
+ readonly caseId: string;
43
+ readonly sourceArtifactId: string;
44
+ /** Which link of the chain broke — the honest, actionable reason. */
45
+ readonly reason: string;
46
+ }
47
+ export interface EvalCaseSelection {
48
+ readonly task: string;
49
+ /** Total eval cases found for the task (rehydratable + refused). */
50
+ readonly total: number;
51
+ readonly rehydratable: readonly RehydratedCase[];
52
+ readonly refusals: readonly EvalCaseRefusal[];
53
+ }
54
+ /**
55
+ * Partition a task's eval cases into rehydratable (with their captured input) and
56
+ * refused (named, with a reason). Pure over the injected reads — no network, no
57
+ * spend; the CLI runs this first and reports before replaying.
58
+ */
59
+ export declare function selectEvalCaseCaptures(deps: EvalCaseSelectionDeps, task: string): EvalCaseSelection;
60
+ /**
61
+ * A read-only in-memory CaptureStore over a fixed set of rehydrated captures, so
62
+ * the EXISTING bake-off engine (which reads `captures.readLast(route, n)`) runs
63
+ * over the eval-case selection with NO second actuation path (spec §3.3). Only
64
+ * `readLast` is meaningful; `getByInferenceId` is supported for symmetry; writes
65
+ * throw (this store is a replay lens, never a sink).
66
+ */
67
+ export declare function evalCaseCaptureStore(rows: readonly RehydratedCase[]): CaptureStore;
68
+ export interface EvalCaseBakeoffResult {
69
+ readonly selection: EvalCaseSelection;
70
+ /** The bake-off artifact over the rehydratable subset, or null when NOTHING
71
+ * was rehydratable (the CLI then reports the refusals and exits without spend). */
72
+ readonly artifact: BakeoffArtifact | null;
73
+ }
74
+ /**
75
+ * `bakeoff --from-eval-cases <task>` end to end: select rehydratable cases, then
76
+ * run the EXISTING bake-off engine over exactly those captured inputs (no second
77
+ * actuation path — spec §3.3). Replays every rehydratable case (replayLastN =
78
+ * the rehydratable count). Returns the selection (so the caller reports refusals)
79
+ * alongside the artifact.
80
+ */
81
+ /** The rig-side store bundle the orchestrator adapts into `EvalCaseSelectionDeps`
82
+ * — passed straight from `createRig` so `index.ts` stays a one-line delegate.
83
+ * `artifacts` is null when the namespace is off (nothing to rehydrate); `captures`
84
+ * is a thunk because the rig opens that handle lazily. */
85
+ export interface EvalCaseStores {
86
+ readonly artifacts: Pick<ArtifactStore, "readEvalCasesByTask" | "getArtifactById"> | null;
87
+ readonly telemetry: Pick<ExportableTelemetryStore, "getStepInferenceId">;
88
+ readonly captures: () => Pick<CaptureStore, "getByInferenceId">;
89
+ }
90
+ export declare function runBakeoffFromEvalCases(deps: RunDeps, stores: EvalCaseStores, req: {
91
+ readonly task: string;
92
+ readonly variants: readonly string[];
93
+ readonly envelopeUsdPerVariant: number;
94
+ readonly keepOutputs?: KeepOutputsMode;
95
+ }, samplesSink?: BakeoffSamplesSink): Promise<EvalCaseBakeoffResult>;
96
+ //# sourceMappingURL=eval-cases.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eval-cases.d.ts","sourceRoot":"","sources":["../../src/bakeoff/eval-cases.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+ /**
3
+ * Bake-off input selection from eval cases — Expansion E4 chunk A (spec §3).
4
+ *
5
+ * `bakeoff --from-eval-cases <task>` replays the promoted regression/golden cases
6
+ * for a task instead of the last-N captures. A case only holds METADATA (a source
7
+ * artifact id); the INPUT to replay lives in a LOCAL capture, reachable through
8
+ * the chain:
9
+ *
10
+ * eval_case → source artifact (run_id, step_key) → step's inference_id → capture
11
+ *
12
+ * Any link may be missing — the source artifact predates local retention, the
13
+ * route never had `capture: true`, the capture was evicted. When it is, the case
14
+ * is UNREHYDRATABLE. The load-bearing doctrine (spec §3.3): the bake-off runs on
15
+ * the rehydratable subset and NAMES every refused case with a reason — NEVER a
16
+ * silent subset. `selectEvalCaseCaptures` returns both halves so the CLI can
17
+ * print the honest-refusal report before it spends a cent.
18
+ */
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.selectEvalCaseCaptures = selectEvalCaseCaptures;
21
+ exports.evalCaseCaptureStore = evalCaseCaptureStore;
22
+ exports.runBakeoffFromEvalCases = runBakeoffFromEvalCases;
23
+ const engine_1 = require("./engine");
24
+ /**
25
+ * Partition a task's eval cases into rehydratable (with their captured input) and
26
+ * refused (named, with a reason). Pure over the injected reads — no network, no
27
+ * spend; the CLI runs this first and reports before replaying.
28
+ */
29
+ function selectEvalCaseCaptures(deps, task) {
30
+ const cases = deps.readEvalCasesByTask(task);
31
+ const rehydratable = [];
32
+ const refusals = [];
33
+ for (const c of cases) {
34
+ const refuse = (reason) => {
35
+ refusals.push({ caseId: c.id, sourceArtifactId: c.sourceArtifactId, reason });
36
+ };
37
+ const art = deps.getArtifactById(c.sourceArtifactId);
38
+ if (art === null) {
39
+ refuse("source artifact is not in the local store (promoted on another deployment, or evicted)");
40
+ continue;
41
+ }
42
+ if (art.runId === null || art.stepKey === null) {
43
+ refuse("source artifact was saved outside a step — no inference to key its input");
44
+ continue;
45
+ }
46
+ const inferenceId = deps.getStepInferenceId(art.runId, art.stepKey);
47
+ if (inferenceId === null) {
48
+ refuse("no ground-truth inference recorded for the source step");
49
+ continue;
50
+ }
51
+ const capture = deps.getCaptureByInferenceId(inferenceId);
52
+ if (capture === null) {
53
+ refuse("input not captured locally (route `capture` was off, or the capture was evicted)");
54
+ continue;
55
+ }
56
+ rehydratable.push({ caseId: c.id, capture });
57
+ }
58
+ return { task, total: cases.length, rehydratable, refusals };
59
+ }
60
+ /**
61
+ * A read-only in-memory CaptureStore over a fixed set of rehydrated captures, so
62
+ * the EXISTING bake-off engine (which reads `captures.readLast(route, n)`) runs
63
+ * over the eval-case selection with NO second actuation path (spec §3.3). Only
64
+ * `readLast` is meaningful; `getByInferenceId` is supported for symmetry; writes
65
+ * throw (this store is a replay lens, never a sink).
66
+ */
67
+ function evalCaseCaptureStore(rows) {
68
+ const captures = rows.map((r) => r.capture);
69
+ return {
70
+ record() {
71
+ throw new Error("evalCaseCaptureStore is read-only (a replay lens, not a sink)");
72
+ },
73
+ readLast(route, n) {
74
+ return captures.filter((c) => c.route === route).slice(0, n);
75
+ },
76
+ getByInferenceId(inferenceId) {
77
+ return captures.find((c) => c.inferenceId === inferenceId) ?? null;
78
+ },
79
+ close() { },
80
+ };
81
+ }
82
+ async function runBakeoffFromEvalCases(deps, stores, req, samplesSink) {
83
+ const empty = { task: req.task, total: 0, rehydratable: [], refusals: [] };
84
+ // No artifact namespace ⇒ nothing to rehydrate; honest empty selection.
85
+ if (stores.artifacts === null)
86
+ return { selection: empty, artifact: null };
87
+ const arts = stores.artifacts;
88
+ const caps = stores.captures();
89
+ const selection = selectEvalCaseCaptures({
90
+ readEvalCasesByTask: (t) => arts.readEvalCasesByTask(t),
91
+ getArtifactById: (id) => arts.getArtifactById(id),
92
+ getStepInferenceId: (r, s) => stores.telemetry.getStepInferenceId(r, s),
93
+ getCaptureByInferenceId: (i) => caps.getByInferenceId(i),
94
+ }, req.task);
95
+ if (selection.rehydratable.length === 0)
96
+ return { selection, artifact: null };
97
+ const artifact = await (0, engine_1.runBakeoff)(deps, evalCaseCaptureStore(selection.rehydratable), {
98
+ route: req.task,
99
+ variants: req.variants,
100
+ replayLastN: selection.rehydratable.length, // replay every rehydratable case
101
+ envelopeUsdPerVariant: req.envelopeUsdPerVariant,
102
+ ...(req.keepOutputs !== undefined ? { keepOutputs: req.keepOutputs } : {}),
103
+ }, samplesSink);
104
+ return { selection, artifact };
105
+ }