modelrig 0.1.0 → 0.3.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 (310) hide show
  1. package/CHANGELOG.md +158 -0
  2. package/README.md +53 -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 +10 -0
  17. package/dist/adapters/gemini.d.ts.map +1 -1
  18. package/dist/adapters/gemini.js +78 -7
  19. package/dist/adapters/grok.d.ts +9 -0
  20. package/dist/adapters/grok.d.ts.map +1 -1
  21. package/dist/adapters/grok.js +67 -3
  22. package/dist/adapters/openai-compat.d.ts +6 -0
  23. package/dist/adapters/openai-compat.d.ts.map +1 -1
  24. package/dist/adapters/openai-compat.js +58 -1
  25. package/dist/adapters/openai-stream.d.ts +58 -0
  26. package/dist/adapters/openai-stream.d.ts.map +1 -0
  27. package/dist/adapters/openai-stream.js +120 -0
  28. package/dist/adapters/openai.d.ts +6 -0
  29. package/dist/adapters/openai.d.ts.map +1 -1
  30. package/dist/adapters/openai.js +60 -1
  31. package/dist/adapters/sampling.d.ts +28 -0
  32. package/dist/adapters/sampling.d.ts.map +1 -0
  33. package/dist/adapters/sampling.js +34 -0
  34. package/dist/adapters/tool-mapping.d.ts +92 -0
  35. package/dist/adapters/tool-mapping.d.ts.map +1 -0
  36. package/dist/adapters/tool-mapping.js +143 -0
  37. package/dist/artifacts/artifact.d.ts +137 -0
  38. package/dist/artifacts/artifact.d.ts.map +1 -0
  39. package/dist/artifacts/artifact.js +377 -0
  40. package/dist/artifacts/content-uploader.d.ts +87 -0
  41. package/dist/artifacts/content-uploader.d.ts.map +1 -0
  42. package/dist/artifacts/content-uploader.js +124 -0
  43. package/dist/artifacts/index.d.ts +63 -0
  44. package/dist/artifacts/index.d.ts.map +1 -0
  45. package/dist/artifacts/index.js +80 -0
  46. package/dist/artifacts/run-context.d.ts +129 -0
  47. package/dist/artifacts/run-context.d.ts.map +1 -0
  48. package/dist/artifacts/run-context.js +208 -0
  49. package/dist/artifacts/scrub.d.ts +60 -0
  50. package/dist/artifacts/scrub.d.ts.map +1 -0
  51. package/dist/artifacts/scrub.js +139 -0
  52. package/dist/artifacts/step.d.ts +26 -0
  53. package/dist/artifacts/step.d.ts.map +1 -0
  54. package/dist/artifacts/step.js +37 -0
  55. package/dist/artifacts/taint.d.ts +63 -0
  56. package/dist/artifacts/taint.d.ts.map +1 -0
  57. package/dist/artifacts/taint.js +76 -0
  58. package/dist/bakeoff/cache-sim.d.ts +99 -0
  59. package/dist/bakeoff/cache-sim.d.ts.map +1 -0
  60. package/dist/bakeoff/cache-sim.js +126 -0
  61. package/dist/bakeoff/eval-cases.d.ts +96 -0
  62. package/dist/bakeoff/eval-cases.d.ts.map +1 -0
  63. package/dist/bakeoff/eval-cases.js +105 -0
  64. package/dist/bakeoff/report.d.ts +41 -0
  65. package/dist/bakeoff/report.d.ts.map +1 -0
  66. package/dist/bakeoff/report.js +142 -0
  67. package/dist/bakeoff/samples.d.ts +15 -0
  68. package/dist/bakeoff/samples.d.ts.map +1 -1
  69. package/dist/bakeoff/samples.js +20 -0
  70. package/dist/cli.d.ts +1 -3
  71. package/dist/cli.d.ts.map +1 -1
  72. package/dist/cli.js +134 -116
  73. package/dist/config.d.ts +100 -0
  74. package/dist/config.d.ts.map +1 -1
  75. package/dist/config.js +104 -0
  76. package/dist/deprecations.d.ts +32 -0
  77. package/dist/deprecations.d.ts.map +1 -0
  78. package/dist/deprecations.js +59 -0
  79. package/dist/gateway/http-protocol.d.ts +175 -0
  80. package/dist/gateway/http-protocol.d.ts.map +1 -0
  81. package/dist/gateway/http-protocol.js +105 -0
  82. package/dist/index.d.ts +106 -19
  83. package/dist/index.d.ts.map +1 -1
  84. package/dist/index.js +266 -138
  85. package/dist/init/finder.d.ts +15 -0
  86. package/dist/init/finder.d.ts.map +1 -1
  87. package/dist/init/finder.js +46 -0
  88. package/dist/init/init-cli.d.ts +10 -0
  89. package/dist/init/init-cli.d.ts.map +1 -0
  90. package/dist/init/init-cli.js +86 -0
  91. package/dist/init/observe-cli.d.ts +19 -0
  92. package/dist/init/observe-cli.d.ts.map +1 -0
  93. package/dist/init/observe-cli.js +73 -0
  94. package/dist/init/observe.d.ts +81 -0
  95. package/dist/init/observe.d.ts.map +1 -0
  96. package/dist/init/observe.js +245 -0
  97. package/dist/registry/build.d.ts +94 -1
  98. package/dist/registry/build.d.ts.map +1 -1
  99. package/dist/registry/build.js +125 -4
  100. package/dist/registry/cache-policy.d.ts +94 -0
  101. package/dist/registry/cache-policy.d.ts.map +1 -0
  102. package/dist/registry/cache-policy.js +238 -0
  103. package/dist/registry/call-notes.d.ts.map +1 -1
  104. package/dist/registry/call-notes.js +40 -2
  105. package/dist/registry/effective-rate.d.ts +151 -0
  106. package/dist/registry/effective-rate.d.ts.map +1 -0
  107. package/dist/registry/effective-rate.js +156 -0
  108. package/dist/registry/facts.d.ts.map +1 -1
  109. package/dist/registry/facts.js +16 -0
  110. package/dist/registry/parity.d.ts +26 -0
  111. package/dist/registry/parity.d.ts.map +1 -1
  112. package/dist/registry/parity.js +43 -2
  113. package/dist/registry/pricing-shape.d.ts +113 -0
  114. package/dist/registry/pricing-shape.d.ts.map +1 -0
  115. package/dist/registry/pricing-shape.js +123 -0
  116. package/dist/registry/pricing.d.ts +25 -1
  117. package/dist/registry/pricing.d.ts.map +1 -1
  118. package/dist/registry/pricing.js +64 -6
  119. package/dist/rigindex/bootstrap.d.ts +36 -0
  120. package/dist/rigindex/bootstrap.d.ts.map +1 -0
  121. package/dist/rigindex/bootstrap.js +97 -0
  122. package/dist/rigindex/fit.d.ts +24 -0
  123. package/dist/rigindex/fit.d.ts.map +1 -0
  124. package/dist/rigindex/fit.js +76 -0
  125. package/dist/rigindex/gates.d.ts +52 -0
  126. package/dist/rigindex/gates.d.ts.map +1 -0
  127. package/dist/rigindex/gates.js +109 -0
  128. package/dist/rigindex/graph.d.ts +31 -0
  129. package/dist/rigindex/graph.d.ts.map +1 -0
  130. package/dist/rigindex/graph.js +120 -0
  131. package/dist/rigindex/index.d.ts +23 -0
  132. package/dist/rigindex/index.d.ts.map +1 -0
  133. package/dist/rigindex/index.js +112 -0
  134. package/dist/rigindex/types.d.ts +76 -0
  135. package/dist/rigindex/types.d.ts.map +1 -0
  136. package/dist/rigindex/types.js +8 -0
  137. package/dist/routes/cache-lint.d.ts +41 -0
  138. package/dist/routes/cache-lint.d.ts.map +1 -0
  139. package/dist/routes/cache-lint.js +81 -0
  140. package/dist/routes/loader.d.ts +48 -0
  141. package/dist/routes/loader.d.ts.map +1 -1
  142. package/dist/routes/loader.js +167 -0
  143. package/dist/routes/resolved-bundle.d.ts +79 -0
  144. package/dist/routes/resolved-bundle.d.ts.map +1 -0
  145. package/dist/routes/resolved-bundle.js +148 -0
  146. package/dist/routes/sampling-config.d.ts +32 -0
  147. package/dist/routes/sampling-config.d.ts.map +1 -0
  148. package/dist/routes/sampling-config.js +91 -0
  149. package/dist/run/ajv.d.ts +22 -0
  150. package/dist/run/ajv.d.ts.map +1 -0
  151. package/dist/run/ajv.js +32 -0
  152. package/dist/run/cache-handle.d.ts +58 -0
  153. package/dist/run/cache-handle.d.ts.map +1 -0
  154. package/dist/run/cache-handle.js +54 -0
  155. package/dist/run/cache-stamp.d.ts +65 -0
  156. package/dist/run/cache-stamp.d.ts.map +1 -0
  157. package/dist/run/cache-stamp.js +0 -0
  158. package/dist/run/capture-write.d.ts +33 -0
  159. package/dist/run/capture-write.d.ts.map +1 -0
  160. package/dist/run/capture-write.js +55 -0
  161. package/dist/run/cost.d.ts.map +1 -1
  162. package/dist/run/cost.js +1 -1
  163. package/dist/run/dispatch.d.ts +99 -0
  164. package/dist/run/dispatch.d.ts.map +1 -0
  165. package/dist/run/dispatch.js +191 -0
  166. package/dist/run/raw.d.ts +130 -0
  167. package/dist/run/raw.d.ts.map +1 -0
  168. package/dist/run/raw.js +363 -0
  169. package/dist/run/record-attempt.d.ts +40 -0
  170. package/dist/run/record-attempt.d.ts.map +1 -0
  171. package/dist/run/record-attempt.js +99 -0
  172. package/dist/run/resolve.d.ts +75 -2
  173. package/dist/run/resolve.d.ts.map +1 -1
  174. package/dist/run/resolve.js +126 -4
  175. package/dist/run/run.d.ts +14 -0
  176. package/dist/run/run.d.ts.map +1 -1
  177. package/dist/run/run.js +0 -0
  178. package/dist/run/stream.d.ts +49 -0
  179. package/dist/run/stream.d.ts.map +1 -0
  180. package/dist/run/stream.js +87 -0
  181. package/dist/run/validate.d.ts.map +1 -1
  182. package/dist/run/validate.js +6 -7
  183. package/dist/savings/cache-ledger.d.ts +108 -0
  184. package/dist/savings/cache-ledger.d.ts.map +1 -0
  185. package/dist/savings/cache-ledger.js +174 -0
  186. package/dist/savings/cache-reconcile.d.ts +64 -0
  187. package/dist/savings/cache-reconcile.d.ts.map +1 -0
  188. package/dist/savings/cache-reconcile.js +59 -0
  189. package/dist/savings/cache-stats.d.ts +63 -0
  190. package/dist/savings/cache-stats.d.ts.map +1 -0
  191. package/dist/savings/cache-stats.js +108 -0
  192. package/dist/savings/emit.d.ts +13 -0
  193. package/dist/savings/emit.d.ts.map +1 -1
  194. package/dist/savings/emit.js +25 -0
  195. package/dist/savings/swap-execute.d.ts +14 -0
  196. package/dist/savings/swap-execute.d.ts.map +1 -1
  197. package/dist/savings/swap-execute.js +44 -1
  198. package/dist/savings/swaps-client.d.ts +14 -0
  199. package/dist/savings/swaps-client.d.ts.map +1 -1
  200. package/dist/savings/swaps-client.js +16 -2
  201. package/dist/settings/api-fetcher.d.ts +39 -0
  202. package/dist/settings/api-fetcher.d.ts.map +1 -0
  203. package/dist/settings/api-fetcher.js +92 -0
  204. package/dist/settings/index.d.ts +10 -0
  205. package/dist/settings/index.d.ts.map +1 -0
  206. package/dist/settings/index.js +15 -0
  207. package/dist/settings/task-settings.d.ts +91 -0
  208. package/dist/settings/task-settings.d.ts.map +1 -0
  209. package/dist/settings/task-settings.js +116 -0
  210. package/dist/status.d.ts +77 -0
  211. package/dist/status.d.ts.map +1 -0
  212. package/dist/status.js +113 -0
  213. package/dist/telemetry/artifact-store.d.ts +48 -0
  214. package/dist/telemetry/artifact-store.d.ts.map +1 -0
  215. package/dist/telemetry/artifact-store.js +189 -0
  216. package/dist/telemetry/cache-miss-streak.d.ts +79 -0
  217. package/dist/telemetry/cache-miss-streak.d.ts.map +1 -0
  218. package/dist/telemetry/cache-miss-streak.js +99 -0
  219. package/dist/telemetry/capture.d.ts +3 -0
  220. package/dist/telemetry/capture.d.ts.map +1 -1
  221. package/dist/telemetry/capture.js +15 -8
  222. package/dist/telemetry/exporter-setup.d.ts +28 -0
  223. package/dist/telemetry/exporter-setup.d.ts.map +1 -0
  224. package/dist/telemetry/exporter-setup.js +34 -0
  225. package/dist/telemetry/exporter.d.ts +75 -0
  226. package/dist/telemetry/exporter.d.ts.map +1 -1
  227. package/dist/telemetry/exporter.js +324 -47
  228. package/dist/telemetry/health.d.ts +76 -0
  229. package/dist/telemetry/health.d.ts.map +1 -0
  230. package/dist/telemetry/health.js +92 -0
  231. package/dist/telemetry/ingest-protocol.d.ts +152 -3
  232. package/dist/telemetry/ingest-protocol.d.ts.map +1 -1
  233. package/dist/telemetry/ingest-protocol.js +204 -4
  234. package/dist/telemetry/load-error-report.d.ts +24 -0
  235. package/dist/telemetry/load-error-report.d.ts.map +1 -0
  236. package/dist/telemetry/load-error-report.js +50 -0
  237. package/dist/telemetry/otel-map.d.ts +121 -0
  238. package/dist/telemetry/otel-map.d.ts.map +1 -0
  239. package/dist/telemetry/otel-map.js +224 -0
  240. package/dist/telemetry/otel-normalize.d.ts +104 -0
  241. package/dist/telemetry/otel-normalize.d.ts.map +1 -0
  242. package/dist/telemetry/otel-normalize.js +530 -0
  243. package/dist/telemetry/otlp-emitter.d.ts +52 -0
  244. package/dist/telemetry/otlp-emitter.d.ts.map +1 -0
  245. package/dist/telemetry/otlp-emitter.js +144 -0
  246. package/dist/telemetry/otlp-proto.d.ts +29 -0
  247. package/dist/telemetry/otlp-proto.d.ts.map +1 -0
  248. package/dist/telemetry/otlp-proto.js +266 -0
  249. package/dist/telemetry/sinks.d.ts +37 -1
  250. package/dist/telemetry/sinks.d.ts.map +1 -1
  251. package/dist/telemetry/sinks.js +121 -30
  252. package/dist/telemetry/store.d.ts +28 -1
  253. package/dist/telemetry/store.d.ts.map +1 -1
  254. package/dist/telemetry/store.js +218 -3
  255. package/dist/tenancy/api-key-format.d.ts +15 -5
  256. package/dist/tenancy/api-key-format.d.ts.map +1 -1
  257. package/dist/tenancy/api-key-format.js +24 -5
  258. package/dist/types/config-teaching.d.ts +62 -0
  259. package/dist/types/config-teaching.d.ts.map +1 -0
  260. package/dist/types/config-teaching.js +183 -0
  261. package/dist/types/provider.d.ts +147 -1
  262. package/dist/types/provider.d.ts.map +1 -1
  263. package/dist/types/provider.js +21 -1
  264. package/dist/types/route.d.ts +79 -1
  265. package/dist/types/route.d.ts.map +1 -1
  266. package/dist/types/telemetry.d.ts +117 -0
  267. package/dist/types/telemetry.d.ts.map +1 -1
  268. package/dist/validate-cli.d.ts +12 -0
  269. package/dist/validate-cli.d.ts.map +1 -0
  270. package/dist/validate-cli.js +57 -0
  271. package/dist/verify/capture.d.ts +66 -0
  272. package/dist/verify/capture.d.ts.map +1 -0
  273. package/dist/verify/capture.js +180 -0
  274. package/dist/verify/cli-run.d.ts +23 -0
  275. package/dist/verify/cli-run.d.ts.map +1 -0
  276. package/dist/verify/cli-run.js +57 -0
  277. package/dist/verify/diff.d.ts +48 -0
  278. package/dist/verify/diff.d.ts.map +1 -0
  279. package/dist/verify/diff.js +157 -0
  280. package/dist/verify/index.d.ts +17 -0
  281. package/dist/verify/index.d.ts.map +1 -0
  282. package/dist/verify/index.js +24 -0
  283. package/dist/verify/report.d.ts +14 -0
  284. package/dist/verify/report.d.ts.map +1 -0
  285. package/dist/verify/report.js +88 -0
  286. package/dist/verify/types.d.ts +101 -0
  287. package/dist/verify/types.d.ts.map +1 -0
  288. package/dist/verify/types.js +17 -0
  289. package/dist/verify/verify.d.ts +29 -0
  290. package/dist/verify/verify.d.ts.map +1 -0
  291. package/dist/verify/verify.js +148 -0
  292. package/dist/watch/evaluate.d.ts +162 -3
  293. package/dist/watch/evaluate.d.ts.map +1 -1
  294. package/dist/watch/evaluate.js +61 -0
  295. package/dist/watch/monitor.d.ts +116 -0
  296. package/dist/watch/monitor.d.ts.map +1 -0
  297. package/dist/watch/monitor.js +199 -0
  298. package/dist/watch/proposals.d.ts +20 -4
  299. package/dist/watch/proposals.d.ts.map +1 -1
  300. package/dist/watch/proposals.js +31 -3
  301. package/dist/watch/run.d.ts +33 -0
  302. package/dist/watch/run.d.ts.map +1 -1
  303. package/dist/watch/run.js +39 -0
  304. package/package.json +7 -1
  305. package/registry/LICENSE +20 -0
  306. package/registry/leaderboard.json +3639 -0
  307. package/registry/registry.json +7263 -0
  308. package/dist/telemetry/supabase-exporter.d.ts +0 -64
  309. package/dist/telemetry/supabase-exporter.d.ts.map +0 -1
  310. package/dist/telemetry/supabase-exporter.js +0 -211
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Observed-health aggregation — G-4 (observed-health/spec.md §3.1, §4.1).
3
+ *
4
+ * One home for the per-endpoint latency/TTFB percentile + error-rate + min-n
5
+ * math, shared by the live store reader (store.ts `readEndpointHealth`) and the
6
+ * observed-layer rollup (registry/build-observed.ts). Keeping the math here —
7
+ * not in run.ts (at the 700-LOC cap) — is deliberate.
8
+ *
9
+ * THE RIGINDEX WALL (spec §4.4, load-bearing): this module feeds routing +
10
+ * console surfaces ONLY. It must NEVER be imported by registry/build-leaderboard.ts
11
+ * or the matrix/Bradley-Terry path — production telemetry never produces rank
12
+ * (eval-methodology-and-rigindex.md:19). The import-guard test
13
+ * (health-rigindex-wall.test.ts) is the tripwire; do not weaken it.
14
+ */
15
+ import type { ProviderId } from "../types/provider";
16
+ /** Spec §4.1 example: below 20 samples, percentiles/error-rate surface as null. */
17
+ export declare const DEFAULT_HEALTH_MIN_N = 20;
18
+ export interface HealthPercentiles {
19
+ readonly p50: number;
20
+ readonly p90: number;
21
+ readonly p99: number;
22
+ }
23
+ /**
24
+ * Per-endpoint observed health over a rolling window. Percentiles + errorRate
25
+ * are `null` under the min-n gate (thin data must not read as a confident zero);
26
+ * `sampleN` is always reported so the surface can say "n too low", not "healthy".
27
+ */
28
+ export interface EndpointHealth {
29
+ readonly provider: ProviderId;
30
+ readonly model: string;
31
+ readonly windowStart: string;
32
+ readonly sampleN: number;
33
+ readonly latencyMs: HealthPercentiles | null;
34
+ readonly ttfbMs: HealthPercentiles | null;
35
+ readonly errorRate: number | null;
36
+ }
37
+ /**
38
+ * The minimal row shape the aggregation needs. Both the local SQLite store and
39
+ * the hosted rollup can project their rows onto this, so the math is identical
40
+ * on both lanes (spec §3.1).
41
+ */
42
+ export interface HealthInputRow {
43
+ readonly provider: string;
44
+ readonly model: string;
45
+ readonly ts: string;
46
+ readonly latency_ms: number;
47
+ readonly ttfb_ms: number | null;
48
+ readonly failure_class: string | null;
49
+ }
50
+ /**
51
+ * Nearest-rank percentile over a pre-sorted ascending array. Matches the
52
+ * convention already used by build-observed.ts so the two lanes agree.
53
+ */
54
+ export declare function percentile(sorted: readonly number[], p: number): number | null;
55
+ /**
56
+ * p50/p90/p99 over a set of values, dropping non-positive samples (a zero or
57
+ * negative latency/TTFB is a non-measurement, not a fast call). `null` when no
58
+ * positive sample remains.
59
+ */
60
+ export declare function computePercentiles(values: readonly number[]): HealthPercentiles | null;
61
+ export interface HealthWindowOptions {
62
+ readonly windowMinutes: number;
63
+ readonly minN: number;
64
+ /** Reference "now" in epoch ms — injected for deterministic windowing/tests. */
65
+ readonly now: number;
66
+ }
67
+ /**
68
+ * Aggregate windowed telemetry rows into per-(provider, model) health.
69
+ *
70
+ * Window: rows whose ts is within [now - windowMinutes, now]. Rows with an
71
+ * unparseable ts are dropped, never thrown on. Output is sorted by provider then
72
+ * model for deterministic rollups. Below `minN`, percentiles + errorRate are
73
+ * `null` (min-n gate) while `sampleN` is still reported.
74
+ */
75
+ export declare function aggregateEndpointHealth(rows: readonly HealthInputRow[], opts: HealthWindowOptions): EndpointHealth[];
76
+ //# sourceMappingURL=health.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../src/telemetry/health.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD,mFAAmF;AACnF,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAEvC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAC7C,QAAQ,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAC1C,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CACnC;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CACvC;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAI9E;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,iBAAiB,GAAG,IAAI,CAStF;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,gFAAgF;IAChF,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AAMD;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,SAAS,cAAc,EAAE,EAC/B,IAAI,EAAE,mBAAmB,GACxB,cAAc,EAAE,CAoClB"}
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ /**
3
+ * Observed-health aggregation — G-4 (observed-health/spec.md §3.1, §4.1).
4
+ *
5
+ * One home for the per-endpoint latency/TTFB percentile + error-rate + min-n
6
+ * math, shared by the live store reader (store.ts `readEndpointHealth`) and the
7
+ * observed-layer rollup (registry/build-observed.ts). Keeping the math here —
8
+ * not in run.ts (at the 700-LOC cap) — is deliberate.
9
+ *
10
+ * THE RIGINDEX WALL (spec §4.4, load-bearing): this module feeds routing +
11
+ * console surfaces ONLY. It must NEVER be imported by registry/build-leaderboard.ts
12
+ * or the matrix/Bradley-Terry path — production telemetry never produces rank
13
+ * (eval-methodology-and-rigindex.md:19). The import-guard test
14
+ * (health-rigindex-wall.test.ts) is the tripwire; do not weaken it.
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.DEFAULT_HEALTH_MIN_N = void 0;
18
+ exports.percentile = percentile;
19
+ exports.computePercentiles = computePercentiles;
20
+ exports.aggregateEndpointHealth = aggregateEndpointHealth;
21
+ /** Spec §4.1 example: below 20 samples, percentiles/error-rate surface as null. */
22
+ exports.DEFAULT_HEALTH_MIN_N = 20;
23
+ /**
24
+ * Nearest-rank percentile over a pre-sorted ascending array. Matches the
25
+ * convention already used by build-observed.ts so the two lanes agree.
26
+ */
27
+ function percentile(sorted, p) {
28
+ if (sorted.length === 0)
29
+ return null;
30
+ const index = Math.min(sorted.length - 1, Math.floor((p / 100) * sorted.length));
31
+ return sorted[index];
32
+ }
33
+ /**
34
+ * p50/p90/p99 over a set of values, dropping non-positive samples (a zero or
35
+ * negative latency/TTFB is a non-measurement, not a fast call). `null` when no
36
+ * positive sample remains.
37
+ */
38
+ function computePercentiles(values) {
39
+ const sorted = values.filter((v) => v > 0).sort((a, b) => a - b);
40
+ if (sorted.length === 0)
41
+ return null;
42
+ return {
43
+ // percentile() only returns null for an empty array, ruled out above.
44
+ p50: percentile(sorted, 50),
45
+ p90: percentile(sorted, 90),
46
+ p99: percentile(sorted, 99),
47
+ };
48
+ }
49
+ // Grouping-key separator: neither a ProviderId nor a model id contains a pipe,
50
+ // so the split back into (provider, model) is unambiguous.
51
+ const KEY_SEP = "|";
52
+ /**
53
+ * Aggregate windowed telemetry rows into per-(provider, model) health.
54
+ *
55
+ * Window: rows whose ts is within [now - windowMinutes, now]. Rows with an
56
+ * unparseable ts are dropped, never thrown on. Output is sorted by provider then
57
+ * model for deterministic rollups. Below `minN`, percentiles + errorRate are
58
+ * `null` (min-n gate) while `sampleN` is still reported.
59
+ */
60
+ function aggregateEndpointHealth(rows, opts) {
61
+ const windowStartMs = opts.now - opts.windowMinutes * 60000;
62
+ const windowStart = new Date(windowStartMs).toISOString();
63
+ const byEndpoint = new Map();
64
+ for (const row of rows) {
65
+ const t = Date.parse(row.ts);
66
+ if (Number.isNaN(t) || t < windowStartMs || t > opts.now)
67
+ continue;
68
+ const key = `${row.provider}${KEY_SEP}${row.model}`;
69
+ const list = byEndpoint.get(key) ?? [];
70
+ list.push(row);
71
+ byEndpoint.set(key, list);
72
+ }
73
+ const out = [];
74
+ for (const [key, group] of byEndpoint) {
75
+ const [provider, model] = key.split(KEY_SEP);
76
+ const sampleN = group.length;
77
+ const gated = sampleN < opts.minN;
78
+ const errorCount = group.filter((r) => r.failure_class !== null).length;
79
+ out.push({
80
+ provider: provider,
81
+ model,
82
+ windowStart,
83
+ sampleN,
84
+ latencyMs: gated ? null : computePercentiles(group.map((r) => r.latency_ms)),
85
+ ttfbMs: gated
86
+ ? null
87
+ : computePercentiles(group.map((r) => r.ttfb_ms).filter((t) => t !== null)),
88
+ errorRate: gated ? null : errorCount / sampleN,
89
+ });
90
+ }
91
+ return out.sort((a, b) => a.provider === b.provider ? a.model.localeCompare(b.model) : a.provider.localeCompare(b.provider));
92
+ }
@@ -31,11 +31,19 @@
31
31
  * already holds a credential that can write any row in the database.
32
32
  */
33
33
  import type { FailureClass } from "../types/failures";
34
+ import type { ResolvedRouteBundle } from "../routes/resolved-bundle";
34
35
  /** The path the api sink posts to. Spelled once. */
35
36
  export declare const INGEST_PATH = "/v1/ingest";
36
- /** Bumped only for a breaking change to the shapes below. A server that does
37
- * not recognise the version refuses the batch rather than guessing. */
38
- export declare const INGEST_PROTOCOL_VERSION = 1;
37
+ /** The CURRENT protocol version — the maximum the SDK can speak (ruling d).
38
+ * v2 = v1 + the optional runs/steps/artifacts sections. A server that does not
39
+ * recognise the version refuses the batch rather than guessing; the SDK sends
40
+ * v2 ONLY when the artifact namespace is active (spec §1.3), otherwise it keeps
41
+ * sending v1 batches byte-identical to before v2 existed. */
42
+ export declare const INGEST_PROTOCOL_VERSION = 2;
43
+ /** The v1 baseline — batches carrying only the pre-artifact sections announce
44
+ * this and remain valid forever. The exporter uses it when artifacts are
45
+ * disabled (byte-identical to today) and for the pre-loop load-error batch. */
46
+ export declare const INGEST_PROTOCOL_VERSION_V1 = 1;
39
47
  /** Default ingest host — telemetry-ingestion-boundary.md §2.1. */
40
48
  export declare const DEFAULT_INGEST_URL = "https://api.modelrig.ai";
41
49
  /** The tenant stamped onto every row. Resolved from the principal, never from
@@ -67,6 +75,14 @@ export interface WireInference {
67
75
  readonly served_variant: string | null;
68
76
  readonly repaired_by: string | null;
69
77
  readonly pricing_missing: boolean;
78
+ /** Cache accounting (WS-A). OPTIONAL on the wire: an older SDK never sends
79
+ * them, and the server defaults writes to 0 and key/fingerprint to null on
80
+ * absence (ingest.validInference). A current SDK always sends them; they map
81
+ * straight into `inferences.tokens_cache_write` etc. (migration 00049). */
82
+ readonly tokens_cache_write?: number;
83
+ readonly tokens_cache_write_1h?: number;
84
+ readonly cache_key?: string | null;
85
+ readonly prefix_fingerprint?: string | null;
70
86
  }
71
87
  export interface WireFeedback {
72
88
  readonly id: string;
@@ -98,12 +114,135 @@ export interface WireRoute {
98
114
  readonly model: string;
99
115
  }>;
100
116
  readonly capture: boolean;
117
+ /** Phase D (spec §13.1 rule 3): the RESOLVED bundle, file references followed
118
+ * and inlined at sync time, mirrored so the hosted gateway can serve the
119
+ * route without the customer's filesystem. Optional so a pre-Phase-D SDK
120
+ * still validates; a batch WITHOUT it leaves any existing mirrored bundle
121
+ * untouched (the routes upsert only writes the columns it carries). */
122
+ readonly bundle?: ResolvedRouteBundle;
123
+ /** Content hash of the canonical bundle (`sha256:<hex>`). Present iff
124
+ * `bundle` is. */
125
+ readonly bundle_sha?: string;
126
+ }
127
+ /** A run — one execution of a customer pipeline (protocol v2, spec §1.1).
128
+ * A mutable projection (start → end); `source_id` and `org_id` are stamped from
129
+ * the principal, never carried on the wire (T3). */
130
+ export interface WireRun {
131
+ readonly id: string;
132
+ readonly pipeline: string;
133
+ readonly pipeline_version: string | null;
134
+ readonly environment: string | null;
135
+ readonly episode_key: string | null;
136
+ readonly started_at: string;
137
+ readonly ended_at: string | null;
138
+ readonly status: "running" | "succeeded" | "failed" | "abandoned";
139
+ }
140
+ /** A step — one ATTEMPT of one operation within a run (protocol v2, spec §1.1).
141
+ * A fact (merge=false). Provenance is ground-truth (an `inferences` reference)
142
+ * XOR attested (a foreign OTLP span); the server drops a reference shape that
143
+ * violates the exactly-one CHECK (`bad_step_reference`) rather than guessing.
144
+ * `org_id` is stamped from the principal; for a ground-truth step
145
+ * `inference_source_id` is stamped to the principal's source_id. */
146
+ export interface WireStep {
147
+ readonly run_id: string;
148
+ readonly step_key: string;
149
+ readonly attempt_no: number;
150
+ readonly task: string | null;
151
+ readonly provenance: "ground-truth" | "attested";
152
+ readonly inference_id: string | null;
153
+ readonly span_trace_id: string | null;
154
+ readonly span_id: string | null;
155
+ readonly started_at: string;
156
+ readonly ended_at: string | null;
157
+ readonly failure_class: string | null;
158
+ /**
159
+ * The normalized OTLP envelope for an ATTESTED step (Expansion E3-Harvest,
160
+ * migration 00038: `steps.attested jsonb`). Carries `{model, provider,
161
+ * operation, usage{…}, scope}` for foreign-span-ingested steps; steps
162
+ * deliberately have no usage COLUMNS because ground-truth usage lives on
163
+ * `inferences`, and attested usage has nowhere else honest to live. Absent on
164
+ * every SDK-emitted (ground-truth) step, so v1/v2 SDK batches are unchanged
165
+ * and the `steps.attested` CHECK (`provenance='attested' or attested is null`)
166
+ * holds. Opaque `unknown` at the wire boundary (T7 — jsonb is not proof of
167
+ * shape); the strongly-typed producer is `otel-normalize.ts`.
168
+ */
169
+ readonly attested?: unknown;
170
+ }
171
+ /** An artifact — durable work product METADATA (protocol v2, spec §2). Content
172
+ * is NEVER carried: `content_ref` is the customer's opaque URI, `content_hash`
173
+ * a sha256 of bytes hashed-then-discarded. A fact (merge=false). */
174
+ export interface WireArtifact {
175
+ readonly id: string;
176
+ readonly run_id: string | null;
177
+ readonly step_key: string | null;
178
+ readonly attempt_no: number | null;
179
+ readonly task: string | null;
180
+ readonly name: string;
181
+ readonly type: string;
182
+ readonly version: number;
183
+ readonly supersedes_artifact_id: string | null;
184
+ readonly declared_schema: unknown;
185
+ readonly fingerprint_cell: string | null;
186
+ readonly content_ref: string | null;
187
+ readonly content_hash: string | null;
188
+ readonly content_bytes: number | null;
189
+ readonly data_classification: string | null;
190
+ readonly expires_at: string | null;
191
+ /** Per-row scrub findings (E4 chunk 0, migration 00039) — counts-by-type +
192
+ * mode, NEVER matched values. Optional: absent ⇒ scrub did not run (older SDK,
193
+ * custody off), and the column stays null via spread-iff-present in stampBatch
194
+ * so v1/older artifact rows are byte-identical. Opaque here (T7). */
195
+ readonly scrub?: unknown;
196
+ }
197
+ /** A lineage edge between two artifacts (spec §2). A fact. */
198
+ export interface WireArtifactEdge {
199
+ readonly from_artifact_id: string;
200
+ readonly to_artifact_id: string;
201
+ readonly kind: "consumed" | "derived_from";
202
+ }
203
+ /** A promoted eval case (E4 chunk A). `corrected_artifact_id` null ⇒ a
204
+ * failure-case; set ⇒ a golden-corrected pair. A fact on ingest (create via
205
+ * promote); edits/deletes are server-side, never on this stream. `org_id` is
206
+ * stamped from the principal (T3), `created_by` when an authenticated user
207
+ * promoted it. */
208
+ export interface WireEvalCase {
209
+ readonly id: string;
210
+ readonly task: string;
211
+ readonly source_artifact_id: string;
212
+ readonly corrected_artifact_id: string | null;
213
+ readonly origin: "promoted-failure" | "human-corrected" | "manual";
214
+ readonly note: string | null;
215
+ readonly created_by: string | null;
216
+ }
217
+ /** An evaluation recorded against a SUBJECT — one of an artifact or a whole run
218
+ * (E5 chunk V, subject-one-of per trajectory F3). Exactly one of artifact_id /
219
+ * run_id is non-null; the server's validEvaluation enforces the XOR and the
220
+ * 00051 CHECK is the backstop. A fact. */
221
+ export interface WireArtifactEvaluation {
222
+ readonly id: string;
223
+ /** The artifact subject — null when this evaluates a whole run instead. */
224
+ readonly artifact_id: string | null;
225
+ /** The run subject (E5 V) — null when this evaluates an artifact. */
226
+ readonly run_id?: string | null;
227
+ readonly evaluator: string;
228
+ readonly evaluator_kind: "deterministic" | "model-judge" | "human";
229
+ readonly score: number | null;
230
+ readonly passed: boolean | null;
231
+ readonly label: string | null;
232
+ readonly explanation: string | null;
233
+ readonly created_by: string | null;
101
234
  }
102
235
  export interface WireManifest {
103
236
  readonly name: string;
104
237
  readonly dimensions: unknown;
105
238
  readonly contribution: boolean;
106
239
  readonly serveability: readonly unknown[];
240
+ /** Load-time RouteConfigError diagnostics for THIS deployment (follow-up #2)
241
+ * — the describeConfigFailure shape, names/diagnostics only. Optional so a
242
+ * pre-load_errors SDK still validates; absent is treated as `[]` (clean). A
243
+ * successful load sends `[]`, which clears a stale failure from a prior
244
+ * broken deploy. */
245
+ readonly load_errors?: readonly unknown[];
107
246
  }
108
247
  /** One flush's worth of telemetry. Every collection is optional — a flush that
109
248
  * has only inferences sends only inferences. */
@@ -117,6 +256,16 @@ export interface IngestBatch {
117
256
  readonly feedback?: readonly WireFeedback[];
118
257
  readonly envelopes?: readonly WireEnvelope[];
119
258
  readonly routes?: readonly WireRoute[];
259
+ /** Protocol v2 (spec §1). Present only when the SDK's artifact namespace is
260
+ * active; a v1 batch never carries them and is byte-identical to before. */
261
+ readonly runs?: readonly WireRun[];
262
+ readonly steps?: readonly WireStep[];
263
+ readonly artifacts?: readonly WireArtifact[];
264
+ readonly artifact_edges?: readonly WireArtifactEdge[];
265
+ readonly artifact_evaluations?: readonly WireArtifactEvaluation[];
266
+ /** Promoted eval cases (E4 chunk A). Present only when the SDK's artifact
267
+ * namespace is active and a promote happened; a v1 batch never carries them. */
268
+ readonly eval_cases?: readonly WireEvalCase[];
120
269
  readonly manifest?: WireManifest;
121
270
  /** Present when this batch advanced the exporter's inference watermark. */
122
271
  readonly watermarkAt?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"ingest-protocol.d.ts","sourceRoot":"","sources":["../../src/telemetry/ingest-protocol.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ingest-protocol.d.ts","sourceRoot":"","sources":["../../src/telemetry/ingest-protocol.ts"],"names":[],"mappings":"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"}
@@ -32,14 +32,22 @@
32
32
  * already holds a credential that can write any row in the database.
33
33
  */
34
34
  Object.defineProperty(exports, "__esModule", { value: true });
35
- exports.DEFAULT_INGEST_URL = exports.INGEST_PROTOCOL_VERSION = exports.INGEST_PATH = void 0;
35
+ exports.DEFAULT_INGEST_URL = exports.INGEST_PROTOCOL_VERSION_V1 = exports.INGEST_PROTOCOL_VERSION = exports.INGEST_PATH = void 0;
36
36
  exports.stampBatch = stampBatch;
37
37
  exports.foreignOrgFeedback = foreignOrgFeedback;
38
+ const fingerprint_1 = require("../registry/fingerprint");
38
39
  /** The path the api sink posts to. Spelled once. */
39
40
  exports.INGEST_PATH = "/v1/ingest";
40
- /** Bumped only for a breaking change to the shapes below. A server that does
41
- * not recognise the version refuses the batch rather than guessing. */
42
- exports.INGEST_PROTOCOL_VERSION = 1;
41
+ /** The CURRENT protocol version — the maximum the SDK can speak (ruling d).
42
+ * v2 = v1 + the optional runs/steps/artifacts sections. A server that does not
43
+ * recognise the version refuses the batch rather than guessing; the SDK sends
44
+ * v2 ONLY when the artifact namespace is active (spec §1.3), otherwise it keeps
45
+ * sending v1 batches byte-identical to before v2 existed. */
46
+ exports.INGEST_PROTOCOL_VERSION = 2;
47
+ /** The v1 baseline — batches carrying only the pre-artifact sections announce
48
+ * this and remain valid forever. The exporter uses it when artifacts are
49
+ * disabled (byte-identical to today) and for the pre-loop load-error batch. */
50
+ exports.INGEST_PROTOCOL_VERSION_V1 = 1;
43
51
  /** Default ingest host — telemetry-ingestion-boundary.md §2.1. */
44
52
  exports.DEFAULT_INGEST_URL = "https://api.modelrig.ai";
45
53
  /**
@@ -53,6 +61,46 @@ function stampBatch(batch, tenancy) {
53
61
  const writes = [];
54
62
  const now = new Date().toISOString();
55
63
  if (batch.routes !== undefined && batch.routes.length > 0) {
64
+ // Derive task identities BEFORE the routes write (spec §4, ruling a): a
65
+ // route implies a task. This is the derivation, not FK surgery — `routes`
66
+ // is untouched; the identity is a projection kept current by the same sync.
67
+ const seenTasks = new Set();
68
+ const taskRows = [];
69
+ for (const r of batch.routes) {
70
+ // Dedup by route NAME: a batch may carry several versions of one route,
71
+ // but they share a single task identity ((org_id, task) PK).
72
+ if (seenTasks.has(r.route))
73
+ continue;
74
+ seenTasks.add(r.route);
75
+ // Built field-by-field from the PRINCIPAL's tenancy, never spread from
76
+ // wire input — a client-supplied org_id must not ride in (ingest.ts:170
77
+ // doctrine, T3).
78
+ const taskRow = {
79
+ org_id: tenancy.org_id,
80
+ task: r.route,
81
+ source: "route-sync",
82
+ };
83
+ // spread-iff-bundle-present (the 00027 pattern): a metadata-only sync
84
+ // (no bundle) must NOT null-out an existing schema/fingerprint — a merge
85
+ // upsert touches only the columns present in the row. The fingerprint is
86
+ // computed server-side from the inlined schema with the SAME pure
87
+ // function the loader uses (fingerprintSchema); the parity golden locks
88
+ // the two call sites to identical output.
89
+ if (r.bundle !== undefined) {
90
+ taskRow.declared_schema = r.bundle.outputSchema ?? null;
91
+ taskRow.fingerprint_cell = (0, fingerprint_1.fingerprintSchema)(r.bundle.outputSchema)?.cellKey ?? null;
92
+ taskRow.updated_at = now;
93
+ }
94
+ taskRows.push(taskRow);
95
+ }
96
+ writes.push({
97
+ // merge=true: a task row is a mutable projection of the latest sync, like
98
+ // the routes mirror, unlike inferences. onConflict on the PK (S5).
99
+ table: "tasks",
100
+ onConflict: "org_id,task",
101
+ merge: true,
102
+ rows: taskRows,
103
+ });
56
104
  writes.push({
57
105
  table: "routes",
58
106
  onConflict: "org_id,route,version",
@@ -64,6 +112,155 @@ function stampBatch(batch, tenancy) {
64
112
  candidates: r.candidates,
65
113
  capture: r.capture,
66
114
  updated_at: now,
115
+ // Phase D (spec §13): the resolved bundle + its hash + the sync stamp.
116
+ // Written only when the batch carries a bundle, so an older SDK's
117
+ // metadata-only mirror leaves any existing bundle intact (a merge
118
+ // upsert touches only the columns present in the row). `synced_at` is
119
+ // the BUNDLE's timestamp — stamped iff a bundle synced — distinct from
120
+ // `updated_at`, the row's metadata timestamp.
121
+ ...(r.bundle !== undefined
122
+ ? { bundle: r.bundle, bundle_sha: r.bundle_sha ?? null, synced_at: now }
123
+ : {}),
124
+ })),
125
+ });
126
+ }
127
+ if (batch.runs !== undefined && batch.runs.length > 0) {
128
+ writes.push({
129
+ // runs are a mutable projection (start → end): merge=true on the PK (S5).
130
+ table: "runs",
131
+ onConflict: "org_id,id",
132
+ merge: true,
133
+ rows: batch.runs.map((r) => ({
134
+ // Field-by-field from the principal's tenancy — org_id and source_id are
135
+ // NEVER carried on the wire (T3). source_id identifies the rig instance,
136
+ // exactly like inferences.
137
+ org_id: tenancy.org_id,
138
+ id: r.id,
139
+ source_id: tenancy.source_id,
140
+ pipeline: r.pipeline,
141
+ pipeline_version: r.pipeline_version,
142
+ environment: r.environment,
143
+ episode_key: r.episode_key,
144
+ started_at: r.started_at,
145
+ ended_at: r.ended_at,
146
+ status: r.status,
147
+ updated_at: now,
148
+ })),
149
+ });
150
+ }
151
+ if (batch.steps !== undefined && batch.steps.length > 0) {
152
+ writes.push({
153
+ // A step is a FACT (one row per attempt): merge=false on the composite PK.
154
+ // A retry must not rewrite what already landed.
155
+ table: "steps",
156
+ onConflict: "org_id,run_id,step_key,attempt_no",
157
+ merge: false,
158
+ rows: batch.steps.map((s) => ({
159
+ org_id: tenancy.org_id,
160
+ run_id: s.run_id,
161
+ step_key: s.step_key,
162
+ attempt_no: s.attempt_no,
163
+ task: s.task,
164
+ provenance: s.provenance,
165
+ // A ground-truth step joins inferences(org_id, source_id, id); the
166
+ // source_id is the principal's, stamped here (not trusted from the wire).
167
+ // Attested steps carry no inference reference (span only) — null.
168
+ inference_source_id: s.provenance === "ground-truth" ? tenancy.source_id : null,
169
+ inference_id: s.inference_id,
170
+ span_trace_id: s.span_trace_id,
171
+ span_id: s.span_id,
172
+ started_at: s.started_at,
173
+ ended_at: s.ended_at,
174
+ failure_class: s.failure_class,
175
+ // spread-iff-present (the 00027 pattern): the attested envelope is
176
+ // written ONLY for attested steps (OTLP ingest, E3-Harvest). A
177
+ // ground-truth step carries no `attested`, so the column stays null and
178
+ // the 00038 CHECK holds. An SDK batch never sets it → byte-identical.
179
+ ...(s.attested !== undefined ? { attested: s.attested } : {}),
180
+ })),
181
+ });
182
+ }
183
+ if (batch.artifacts !== undefined && batch.artifacts.length > 0) {
184
+ writes.push({
185
+ table: "artifacts",
186
+ onConflict: "org_id,id",
187
+ merge: false, // an artifact metadata row is a fact; a retry must not rewrite it
188
+ rows: batch.artifacts.map((a) => ({
189
+ org_id: tenancy.org_id,
190
+ id: a.id,
191
+ run_id: a.run_id,
192
+ step_key: a.step_key,
193
+ attempt_no: a.attempt_no,
194
+ task: a.task,
195
+ name: a.name,
196
+ type: a.type,
197
+ version: a.version,
198
+ supersedes_artifact_id: a.supersedes_artifact_id,
199
+ declared_schema: a.declared_schema,
200
+ fingerprint_cell: a.fingerprint_cell,
201
+ content_ref: a.content_ref,
202
+ content_hash: a.content_hash,
203
+ content_bytes: a.content_bytes,
204
+ data_classification: a.data_classification,
205
+ expires_at: a.expires_at,
206
+ // spread-iff-present (00027 pattern): a batch without `scrub` (older SDK,
207
+ // custody off) leaves the column null rather than overwriting it.
208
+ ...(a.scrub !== undefined ? { scrub: a.scrub } : {}),
209
+ })),
210
+ });
211
+ }
212
+ if (batch.artifact_edges !== undefined && batch.artifact_edges.length > 0) {
213
+ writes.push({
214
+ table: "artifact_edges",
215
+ onConflict: "org_id,from_artifact_id,to_artifact_id,kind",
216
+ merge: false,
217
+ rows: batch.artifact_edges.map((e) => ({
218
+ org_id: tenancy.org_id,
219
+ from_artifact_id: e.from_artifact_id,
220
+ to_artifact_id: e.to_artifact_id,
221
+ kind: e.kind,
222
+ })),
223
+ });
224
+ }
225
+ if (batch.artifact_evaluations !== undefined && batch.artifact_evaluations.length > 0) {
226
+ writes.push({
227
+ table: "artifact_evaluations",
228
+ onConflict: "org_id,id",
229
+ merge: false,
230
+ rows: batch.artifact_evaluations.map((v) => ({
231
+ org_id: tenancy.org_id,
232
+ id: v.id,
233
+ artifact_id: v.artifact_id,
234
+ // Subject-one-of (00051): a run-subject row carries run_id, artifact_id
235
+ // null; the CHECK rejects a row that sets both or neither.
236
+ run_id: v.run_id ?? null,
237
+ evaluator: v.evaluator,
238
+ evaluator_kind: v.evaluator_kind,
239
+ score: v.score,
240
+ passed: v.passed,
241
+ label: v.label,
242
+ explanation: v.explanation,
243
+ created_by: v.created_by,
244
+ })),
245
+ });
246
+ }
247
+ if (batch.eval_cases !== undefined && batch.eval_cases.length > 0) {
248
+ writes.push({
249
+ // An eval case is created once (via promote); a retry must not rewrite it.
250
+ // Edits/deletes are a server-side owner action, not this stream. merge=false
251
+ // on the PK — idempotent by doing nothing (S5).
252
+ table: "eval_cases",
253
+ onConflict: "org_id,id",
254
+ merge: false,
255
+ rows: batch.eval_cases.map((c) => ({
256
+ org_id: tenancy.org_id,
257
+ id: c.id,
258
+ task: c.task,
259
+ source_artifact_id: c.source_artifact_id,
260
+ corrected_artifact_id: c.corrected_artifact_id,
261
+ origin: c.origin,
262
+ note: c.note,
263
+ created_by: c.created_by,
67
264
  })),
68
265
  });
69
266
  }
@@ -80,6 +277,9 @@ function stampBatch(batch, tenancy) {
80
277
  dimensions: batch.manifest.dimensions,
81
278
  contribution: batch.manifest.contribution,
82
279
  serveability: batch.manifest.serveability,
280
+ // [] when the load was clean — a merge upsert then clears any stale
281
+ // failure a prior broken deploy left on the row (follow-up #2).
282
+ load_errors: batch.manifest.load_errors ?? [],
83
283
  updated_at: now,
84
284
  },
85
285
  ],
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Load-error console reporting — follow-up #2.
3
+ *
4
+ * When a route bundle fails to load, tell the console WHY. `loadRoutes` throws
5
+ * before the exporter loop exists, so the run-loop path never gets a chance to
6
+ * mirror the failure. This sends a one-shot manifest batch carrying
7
+ * `describeConfigFailure(error)` onto `rig_manifests.load_errors`, which drives
8
+ * /setup's "Define your first route" step to `failed` with what we looked for
9
+ * and what we found.
10
+ *
11
+ * Fire-and-forget, and best-effort by design: `createRig` is synchronous and
12
+ * re-throws (the rig genuinely cannot run), and telemetry must never change the
13
+ * error the caller sees. KNOWN LIMITATION: this only reaches the console once
14
+ * the app runs with an export configured (MODELRIG_API_KEY / direct creds); a
15
+ * first-timer who hasn't connected yet still gets the full diagnosis at their
16
+ * terminal via `renderConfigErrorForTerminal`.
17
+ *
18
+ * Lives here rather than in index.ts to keep that file under the LOC cap (P1) —
19
+ * the same relocation `buildTelemetrySink` made to `sinks.ts`.
20
+ */
21
+ import type { RigConfig } from "../config";
22
+ import { RouteConfigError } from "../routes/loader";
23
+ export declare function reportLoadError(config: RigConfig, error: RouteConfigError): void;
24
+ //# sourceMappingURL=load-error-report.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load-error-report.d.ts","sourceRoot":"","sources":["../../src/telemetry/load-error-report.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAOpD,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAoBhF"}