modelrig 0.8.1 → 0.10.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 (382) hide show
  1. package/CHANGELOG.md +324 -0
  2. package/README.md +55 -29
  3. package/data/PROVENANCE.md +14 -0
  4. package/data/pricing-map.json +26 -0
  5. package/dist/adapters/attempt-meta-map.d.ts +8 -1
  6. package/dist/adapters/attempt-meta-map.d.ts.map +1 -1
  7. package/dist/adapters/attempt-meta-map.js +4 -0
  8. package/dist/adapters/build-adapters.js +2 -2
  9. package/dist/adapters/gemini.d.ts +28 -1
  10. package/dist/adapters/gemini.d.ts.map +1 -1
  11. package/dist/adapters/gemini.js +38 -9
  12. package/dist/adapters/grok.d.ts.map +1 -1
  13. package/dist/adapters/grok.js +53 -4
  14. package/dist/adapters/openai-compat.d.ts +8 -2
  15. package/dist/adapters/openai-compat.d.ts.map +1 -1
  16. package/dist/adapters/openai-compat.js +8 -1
  17. package/dist/adapters/openai-stream.d.ts +10 -2
  18. package/dist/adapters/openai-stream.d.ts.map +1 -1
  19. package/dist/adapters/openai-stream.js +5 -1
  20. package/dist/adapters/openai.d.ts +10 -1
  21. package/dist/adapters/openai.d.ts.map +1 -1
  22. package/dist/adapters/openai.js +57 -7
  23. package/dist/adapters/responses-api.d.ts +61 -0
  24. package/dist/adapters/responses-api.d.ts.map +1 -0
  25. package/dist/adapters/responses-api.js +124 -0
  26. package/dist/artifacts/artifact.d.ts +31 -12
  27. package/dist/artifacts/artifact.d.ts.map +1 -1
  28. package/dist/artifacts/artifact.js +218 -188
  29. package/dist/artifacts/index.d.ts +15 -5
  30. package/dist/artifacts/index.d.ts.map +1 -1
  31. package/dist/artifacts/index.js +19 -5
  32. package/dist/artifacts/run-context.d.ts +3 -24
  33. package/dist/artifacts/run-context.d.ts.map +1 -1
  34. package/dist/artifacts/run-context.js +3 -39
  35. package/dist/bakeoff/engine.d.ts +29 -0
  36. package/dist/bakeoff/engine.d.ts.map +1 -1
  37. package/dist/bakeoff/engine.js +47 -0
  38. package/dist/bakeoff/eval-cases-rig.d.ts +36 -0
  39. package/dist/bakeoff/eval-cases-rig.d.ts.map +1 -0
  40. package/dist/bakeoff/eval-cases-rig.js +27 -0
  41. package/dist/bakeoff/eval-cases.d.ts +19 -0
  42. package/dist/bakeoff/eval-cases.d.ts.map +1 -1
  43. package/dist/bakeoff/eval-cases.js +18 -1
  44. package/dist/bakeoff/golden-judge.d.ts +47 -0
  45. package/dist/bakeoff/golden-judge.d.ts.map +1 -0
  46. package/dist/bakeoff/golden-judge.js +49 -0
  47. package/dist/bakeoff/groundedness.d.ts +58 -0
  48. package/dist/bakeoff/groundedness.d.ts.map +1 -0
  49. package/dist/bakeoff/groundedness.js +165 -0
  50. package/dist/bakeoff/quality.d.ts +43 -0
  51. package/dist/bakeoff/quality.d.ts.map +1 -1
  52. package/dist/bakeoff/quality.js +31 -0
  53. package/dist/bakeoff/replay.d.ts +11 -0
  54. package/dist/bakeoff/replay.d.ts.map +1 -1
  55. package/dist/bakeoff/replay.js +12 -0
  56. package/dist/bakeoff/report.d.ts +7 -0
  57. package/dist/bakeoff/report.d.ts.map +1 -1
  58. package/dist/bakeoff/report.js +26 -0
  59. package/dist/cli-judge.d.ts +12 -0
  60. package/dist/cli-judge.d.ts.map +1 -0
  61. package/dist/cli-judge.js +48 -0
  62. package/dist/cli.d.ts.map +1 -1
  63. package/dist/cli.js +22 -42
  64. package/dist/config.d.ts +85 -29
  65. package/dist/config.d.ts.map +1 -1
  66. package/dist/config.js +109 -47
  67. package/dist/detect/detector.d.ts +4 -2
  68. package/dist/detect/detector.d.ts.map +1 -1
  69. package/dist/detect/safe-harbor.d.ts +57 -0
  70. package/dist/detect/safe-harbor.d.ts.map +1 -0
  71. package/dist/detect/safe-harbor.js +278 -0
  72. package/dist/doctor-cli.d.ts +7 -0
  73. package/dist/doctor-cli.d.ts.map +1 -0
  74. package/dist/doctor-cli.js +67 -0
  75. package/dist/doctor-preflight.d.ts +52 -0
  76. package/dist/doctor-preflight.d.ts.map +1 -0
  77. package/dist/doctor-preflight.js +112 -0
  78. package/dist/feedback/coverage.d.ts +11 -8
  79. package/dist/feedback/coverage.d.ts.map +1 -1
  80. package/dist/feedback/coverage.js +29 -16
  81. package/dist/feedback/grade.d.ts +58 -0
  82. package/dist/feedback/grade.d.ts.map +1 -0
  83. package/dist/feedback/grade.js +67 -0
  84. package/dist/gateway/http-protocol.d.ts +20 -1
  85. package/dist/gateway/http-protocol.d.ts.map +1 -1
  86. package/dist/gateway/http-protocol.js +1 -1
  87. package/dist/gateway/serve-client.d.ts +99 -0
  88. package/dist/gateway/serve-client.d.ts.map +1 -0
  89. package/dist/gateway/serve-client.js +360 -0
  90. package/dist/index.d.ts +68 -29
  91. package/dist/index.d.ts.map +1 -1
  92. package/dist/index.js +167 -46
  93. package/dist/init/observe-cli.js +1 -1
  94. package/dist/init/observe.d.ts +2 -2
  95. package/dist/init/observe.d.ts.map +1 -1
  96. package/dist/init/observe.js +11 -7
  97. package/dist/mask/arm.d.ts +112 -0
  98. package/dist/mask/arm.d.ts.map +1 -0
  99. package/dist/mask/arm.js +179 -0
  100. package/dist/mask/index.d.ts +2 -0
  101. package/dist/mask/index.d.ts.map +1 -1
  102. package/dist/mask/index.js +7 -1
  103. package/dist/mask/mask-policy-cli.d.ts +12 -4
  104. package/dist/mask/mask-policy-cli.d.ts.map +1 -1
  105. package/dist/mask/mask-policy-cli.js +34 -9
  106. package/dist/prompts/manifest.d.ts +128 -0
  107. package/dist/prompts/manifest.d.ts.map +1 -0
  108. package/dist/prompts/manifest.js +214 -0
  109. package/dist/prompts/prompt-fetch.d.ts +84 -0
  110. package/dist/prompts/prompt-fetch.d.ts.map +1 -0
  111. package/dist/prompts/prompt-fetch.js +120 -0
  112. package/dist/prompts/prompt-resolve.d.ts +76 -0
  113. package/dist/prompts/prompt-resolve.d.ts.map +1 -0
  114. package/dist/prompts/prompt-resolve.js +93 -0
  115. package/dist/prompts/slot-resolver.d.ts +91 -0
  116. package/dist/prompts/slot-resolver.d.ts.map +1 -0
  117. package/dist/prompts/slot-resolver.js +135 -0
  118. package/dist/prompts/tools-register.d.ts +60 -0
  119. package/dist/prompts/tools-register.d.ts.map +1 -0
  120. package/dist/prompts/tools-register.js +48 -0
  121. package/dist/prompts/version-hash.d.ts +29 -0
  122. package/dist/prompts/version-hash.d.ts.map +1 -0
  123. package/dist/prompts/version-hash.js +54 -0
  124. package/dist/prompts-export-cli.d.ts +21 -0
  125. package/dist/prompts-export-cli.d.ts.map +1 -0
  126. package/dist/prompts-export-cli.js +130 -0
  127. package/dist/query/grammar.d.ts +2 -2
  128. package/dist/query/grammar.d.ts.map +1 -1
  129. package/dist/query/grammar.js +3 -1
  130. package/dist/query/saved.d.ts +13 -0
  131. package/dist/query/saved.d.ts.map +1 -1
  132. package/dist/query/saved.js +67 -1
  133. package/dist/receipts/types.d.ts +1 -1
  134. package/dist/receipts/types.d.ts.map +1 -1
  135. package/dist/receipts/types.js +1 -0
  136. package/dist/registry/build.d.ts +43 -1
  137. package/dist/registry/build.d.ts.map +1 -1
  138. package/dist/registry/build.js +52 -0
  139. package/dist/registry/cache-policy.d.ts.map +1 -1
  140. package/dist/registry/cache-policy.js +22 -0
  141. package/dist/registry/pricing-shape.d.ts +67 -8
  142. package/dist/registry/pricing-shape.d.ts.map +1 -1
  143. package/dist/registry/pricing-shape.js +50 -13
  144. package/dist/registry/pricing.d.ts +6 -5
  145. package/dist/registry/pricing.d.ts.map +1 -1
  146. package/dist/registry/pricing.js +9 -5
  147. package/dist/registry/rows.d.ts.map +1 -1
  148. package/dist/registry/rows.js +1 -0
  149. package/dist/registry/select.d.ts +6 -1
  150. package/dist/registry/select.d.ts.map +1 -1
  151. package/dist/registry/select.js +11 -0
  152. package/dist/routes/call-config.d.ts +199 -0
  153. package/dist/routes/call-config.d.ts.map +1 -0
  154. package/dist/routes/call-config.js +343 -0
  155. package/dist/routes/destination-loader.d.ts +16 -0
  156. package/dist/routes/destination-loader.d.ts.map +1 -0
  157. package/dist/routes/destination-loader.js +29 -0
  158. package/dist/routes/draft.d.ts +7 -0
  159. package/dist/routes/draft.d.ts.map +1 -1
  160. package/dist/routes/draft.js +12 -0
  161. package/dist/routes/loader.d.ts.map +1 -1
  162. package/dist/routes/loader.js +31 -56
  163. package/dist/routes/objective-loader.d.ts +12 -0
  164. package/dist/routes/objective-loader.d.ts.map +1 -1
  165. package/dist/routes/objective-loader.js +82 -0
  166. package/dist/routes/output-loader.d.ts +42 -0
  167. package/dist/routes/output-loader.d.ts.map +1 -0
  168. package/dist/routes/output-loader.js +112 -0
  169. package/dist/routes/prompt-loader.d.ts +49 -0
  170. package/dist/routes/prompt-loader.d.ts.map +1 -0
  171. package/dist/routes/prompt-loader.js +113 -0
  172. package/dist/routes/resolved-bundle.d.ts +69 -0
  173. package/dist/routes/resolved-bundle.d.ts.map +1 -1
  174. package/dist/routes/resolved-bundle.js +76 -0
  175. package/dist/routes/screen-loader.d.ts +29 -1
  176. package/dist/routes/screen-loader.d.ts.map +1 -1
  177. package/dist/routes/screen-loader.js +59 -1
  178. package/dist/run/accept.d.ts +5 -0
  179. package/dist/run/accept.d.ts.map +1 -1
  180. package/dist/run/accept.js +2 -0
  181. package/dist/run/cost.d.ts +31 -2
  182. package/dist/run/cost.d.ts.map +1 -1
  183. package/dist/run/cost.js +57 -2
  184. package/dist/run/destination.d.ts +39 -0
  185. package/dist/run/destination.d.ts.map +1 -0
  186. package/dist/run/destination.js +45 -0
  187. package/dist/run/dispatch.d.ts +67 -16
  188. package/dist/run/dispatch.d.ts.map +1 -1
  189. package/dist/run/dispatch.js +133 -29
  190. package/dist/run/grounding.d.ts +28 -2
  191. package/dist/run/grounding.d.ts.map +1 -1
  192. package/dist/run/grounding.js +54 -10
  193. package/dist/run/raw-defaults.d.ts.map +1 -1
  194. package/dist/run/raw-defaults.js +6 -1
  195. package/dist/run/raw-finalize.d.ts +50 -0
  196. package/dist/run/raw-finalize.d.ts.map +1 -0
  197. package/dist/run/raw-finalize.js +266 -0
  198. package/dist/run/raw-knobs.d.ts +36 -0
  199. package/dist/run/raw-knobs.d.ts.map +1 -0
  200. package/dist/run/raw-knobs.js +99 -0
  201. package/dist/run/raw-request.d.ts +24 -0
  202. package/dist/run/raw-request.d.ts.map +1 -0
  203. package/dist/run/raw-request.js +69 -0
  204. package/dist/run/raw-schema.d.ts +76 -0
  205. package/dist/run/raw-schema.d.ts.map +1 -0
  206. package/dist/run/raw-schema.js +166 -0
  207. package/dist/run/raw-search-cost.d.ts +48 -0
  208. package/dist/run/raw-search-cost.d.ts.map +1 -0
  209. package/dist/run/raw-search-cost.js +78 -0
  210. package/dist/run/raw-support.d.ts +64 -6
  211. package/dist/run/raw-support.d.ts.map +1 -1
  212. package/dist/run/raw-support.js +84 -11
  213. package/dist/run/raw.d.ts +25 -26
  214. package/dist/run/raw.d.ts.map +1 -1
  215. package/dist/run/raw.js +30 -351
  216. package/dist/run/record-attempt.d.ts +12 -0
  217. package/dist/run/record-attempt.d.ts.map +1 -1
  218. package/dist/run/record-attempt.js +30 -12
  219. package/dist/run/repair-attempt.d.ts +68 -0
  220. package/dist/run/repair-attempt.d.ts.map +1 -0
  221. package/dist/run/repair-attempt.js +180 -0
  222. package/dist/run/resolve-destination.d.ts +49 -0
  223. package/dist/run/resolve-destination.d.ts.map +1 -0
  224. package/dist/run/resolve-destination.js +76 -0
  225. package/dist/run/routing-docs.d.ts.map +1 -1
  226. package/dist/run/routing-docs.js +10 -5
  227. package/dist/run/run.d.ts +30 -1
  228. package/dist/run/run.d.ts.map +1 -1
  229. package/dist/run/run.js +94 -172
  230. package/dist/run/telemetry-row.d.ts +92 -0
  231. package/dist/run/telemetry-row.d.ts.map +1 -0
  232. package/dist/run/telemetry-row.js +81 -0
  233. package/dist/run/validate.d.ts +12 -1
  234. package/dist/run/validate.d.ts.map +1 -1
  235. package/dist/run/validate.js +31 -0
  236. package/dist/run/web-search-pricing.d.ts +43 -0
  237. package/dist/run/web-search-pricing.d.ts.map +1 -0
  238. package/dist/run/web-search-pricing.js +56 -0
  239. package/dist/savings/odometer.d.ts +4 -0
  240. package/dist/savings/odometer.d.ts.map +1 -1
  241. package/dist/savings/odometer.js +20 -1
  242. package/dist/savings/swap-execute.d.ts +7 -0
  243. package/dist/savings/swap-execute.d.ts.map +1 -1
  244. package/dist/savings/swap-execute.js +30 -0
  245. package/dist/savings/swaps-client.d.ts +10 -0
  246. package/dist/savings/swaps-client.d.ts.map +1 -1
  247. package/dist/screen/fixtures/probe-corpora.d.ts +23 -0
  248. package/dist/screen/fixtures/probe-corpora.d.ts.map +1 -0
  249. package/dist/screen/fixtures/probe-corpora.js +69 -0
  250. package/dist/screen/index.d.ts +21 -0
  251. package/dist/screen/index.d.ts.map +1 -0
  252. package/dist/screen/index.js +64 -0
  253. package/dist/screen/model-screen.d.ts +111 -0
  254. package/dist/screen/model-screen.d.ts.map +1 -0
  255. package/dist/screen/model-screen.js +178 -0
  256. package/dist/screen/patterns.d.ts +51 -0
  257. package/dist/screen/patterns.d.ts.map +1 -0
  258. package/dist/screen/patterns.js +151 -0
  259. package/dist/screen/phi-gate.d.ts +52 -0
  260. package/dist/screen/phi-gate.d.ts.map +1 -0
  261. package/dist/screen/phi-gate.js +86 -0
  262. package/dist/screen/policy.d.ts +71 -0
  263. package/dist/screen/policy.d.ts.map +1 -0
  264. package/dist/screen/policy.js +334 -0
  265. package/dist/screen/probe.d.ts +118 -0
  266. package/dist/screen/probe.d.ts.map +1 -0
  267. package/dist/screen/probe.js +132 -0
  268. package/dist/screen/screen-cli.d.ts +16 -0
  269. package/dist/screen/screen-cli.d.ts.map +1 -0
  270. package/dist/screen/screen-cli.js +178 -0
  271. package/dist/screen/screen.d.ts +99 -0
  272. package/dist/screen/screen.d.ts.map +1 -0
  273. package/dist/screen/screen.js +0 -0
  274. package/dist/screen/sidecar.d.ts +69 -0
  275. package/dist/screen/sidecar.d.ts.map +1 -0
  276. package/dist/screen/sidecar.js +145 -0
  277. package/dist/screen/types.d.ts +43 -2
  278. package/dist/screen/types.d.ts.map +1 -1
  279. package/dist/settings/api-fetcher.d.ts +14 -3
  280. package/dist/settings/api-fetcher.d.ts.map +1 -1
  281. package/dist/settings/api-fetcher.js +61 -10
  282. package/dist/settings/grading-guide.d.ts +102 -0
  283. package/dist/settings/grading-guide.d.ts.map +1 -0
  284. package/dist/settings/grading-guide.js +23 -0
  285. package/dist/settings/index.d.ts +3 -2
  286. package/dist/settings/index.d.ts.map +1 -1
  287. package/dist/settings/index.js +2 -1
  288. package/dist/settings/task-settings.d.ts +44 -7
  289. package/dist/settings/task-settings.d.ts.map +1 -1
  290. package/dist/settings/task-settings.js +53 -12
  291. package/dist/status.d.ts +32 -20
  292. package/dist/status.d.ts.map +1 -1
  293. package/dist/status.js +51 -30
  294. package/dist/telemetry/artifact-store.d.ts +5 -6
  295. package/dist/telemetry/artifact-store.d.ts.map +1 -1
  296. package/dist/telemetry/artifact-store.js +21 -24
  297. package/dist/telemetry/attempt-meta.d.ts +76 -1
  298. package/dist/telemetry/attempt-meta.d.ts.map +1 -1
  299. package/dist/telemetry/attempt-meta.js +33 -1
  300. package/dist/telemetry/exporter-setup.d.ts +2 -0
  301. package/dist/telemetry/exporter-setup.d.ts.map +1 -1
  302. package/dist/telemetry/exporter-setup.js +6 -1
  303. package/dist/telemetry/exporter.d.ts +2 -2
  304. package/dist/telemetry/exporter.d.ts.map +1 -1
  305. package/dist/telemetry/exporter.js +22 -41
  306. package/dist/telemetry/grade-mappers.d.ts +25 -0
  307. package/dist/telemetry/grade-mappers.d.ts.map +1 -0
  308. package/dist/telemetry/grade-mappers.js +64 -0
  309. package/dist/telemetry/grades-sql.d.ts +56 -0
  310. package/dist/telemetry/grades-sql.d.ts.map +1 -0
  311. package/dist/telemetry/grades-sql.js +156 -0
  312. package/dist/telemetry/ingest-protocol.d.ts +55 -48
  313. package/dist/telemetry/ingest-protocol.d.ts.map +1 -1
  314. package/dist/telemetry/ingest-protocol.js +48 -83
  315. package/dist/telemetry/org-identity.d.ts +44 -0
  316. package/dist/telemetry/org-identity.d.ts.map +1 -0
  317. package/dist/telemetry/org-identity.js +98 -0
  318. package/dist/telemetry/route-registration.d.ts +48 -0
  319. package/dist/telemetry/route-registration.d.ts.map +1 -0
  320. package/dist/telemetry/route-registration.js +77 -0
  321. package/dist/telemetry/sinks.d.ts +10 -1
  322. package/dist/telemetry/sinks.d.ts.map +1 -1
  323. package/dist/telemetry/sinks.js +52 -6
  324. package/dist/telemetry/store-migrations.d.ts.map +1 -1
  325. package/dist/telemetry/store-migrations.js +64 -0
  326. package/dist/telemetry/store.d.ts +9 -6
  327. package/dist/telemetry/store.d.ts.map +1 -1
  328. package/dist/telemetry/store.js +8 -50
  329. package/dist/tenancy/api-key-format.d.ts +45 -1
  330. package/dist/tenancy/api-key-format.d.ts.map +1 -1
  331. package/dist/tenancy/api-key-format.js +50 -1
  332. package/dist/types/provider.d.ts +23 -3
  333. package/dist/types/provider.d.ts.map +1 -1
  334. package/dist/types/provider.js +26 -0
  335. package/dist/types/route.d.ts +154 -2
  336. package/dist/types/route.d.ts.map +1 -1
  337. package/dist/types/telemetry.d.ts +73 -48
  338. package/dist/types/telemetry.d.ts.map +1 -1
  339. package/dist/validate-cli.d.ts.map +1 -1
  340. package/dist/validate-cli.js +32 -0
  341. package/dist/verify/verify.d.ts +5 -0
  342. package/dist/verify/verify.d.ts.map +1 -1
  343. package/dist/verify/verify.js +2 -0
  344. package/dist/watch/evaluate.d.ts +84 -1
  345. package/dist/watch/evaluate.d.ts.map +1 -1
  346. package/dist/watch/evaluate.js +28 -0
  347. package/dist/watch/monitor.d.ts +8 -2
  348. package/dist/watch/monitor.d.ts.map +1 -1
  349. package/dist/watch/monitor.js +13 -5
  350. package/dist/watch/regret-monitor-cli.d.ts +16 -0
  351. package/dist/watch/regret-monitor-cli.d.ts.map +1 -0
  352. package/dist/watch/regret-monitor-cli.js +137 -0
  353. package/dist/watch/regret-monitor.d.ts +168 -0
  354. package/dist/watch/regret-monitor.d.ts.map +1 -0
  355. package/dist/watch/regret-monitor.js +304 -0
  356. package/dist/watch/upgrade.d.ts +62 -0
  357. package/dist/watch/upgrade.d.ts.map +1 -0
  358. package/dist/watch/upgrade.js +57 -0
  359. package/docs/agent-skill/SKILL.md +181 -9
  360. package/docs/how-it-fits.md +3 -2
  361. package/docs/instrumentation.md +219 -46
  362. package/docs/llms.txt +158 -35
  363. package/docs/managed-serving.md +150 -0
  364. package/docs/mcp-oracle.md +322 -0
  365. package/docs/migration-playbook.md +5 -4
  366. package/docs/quickstart.md +263 -89
  367. package/package.json +18 -4
  368. package/registry/leaderboard.json +74 -0
  369. package/registry/registry.json +232 -112
  370. package/screen.d.ts +16 -0
  371. package/templates/call-disposition-qa.json +77 -0
  372. package/templates/catalog-cleansing.json +77 -0
  373. package/templates/compliance-review.json +76 -0
  374. package/templates/cs-next-action.json +76 -0
  375. package/templates/document-extraction.json +78 -0
  376. package/templates/financial-classification.json +75 -0
  377. package/templates/lead-qualification.json +76 -0
  378. package/templates/medical-classification.json +71 -0
  379. package/templates/ticket-triage.json +76 -0
  380. package/dist/feedback/feedback.d.ts +0 -35
  381. package/dist/feedback/feedback.d.ts.map +0 -1
  382. package/dist/feedback/feedback.js +0 -39
@@ -6,7 +6,7 @@ ModelRig sits between your code and the model providers. Your repo declares **ro
6
6
 
7
7
  Every call writes one telemetry row to a local SQLite file first — the run never blocks on the cloud. An async exporter mirrors rows to the console you are reading. Provider keys are the one thing that deliberately never leaves your machine; the exporter has no code path to them.
8
8
 
9
- Routing is half of it. ModelRig also records the **execution graph** of your workstream — the run, its steps, and the artifacts each step produced — versioned, hashed, and lineaged, into an inspectable store you can open: when someone asks why the pipeline did that, you pull up the run and show them. This is the **standard path**, not an add-on: every pipeline execution is a run — wrap it in `run.start` / `run.scope`, name a step per model-call family (name it with `stepKey`), save the work products, and inside that context the SDK auto-stamps `run_id` for you, so the run appears on `/runs` and `/projects` with no hand-written id (the SDK auto-stamps the `step` key inside a run context too; the `stepKey` you pass names the step, and always wins). The namespace is **on by default once a control plane is configured** (a ModelRig API key, or the self-host Supabase pair); a pure local-only rig stays off, and `MODELRIG_ARTIFACTS=0` opts out anywhere. Artifact *metadata and lineage* ride the same exporter; artifact *content* moves only with per-route consent, off by default. A failing artifact can be promoted to a regression case and replayed as a bake-off — see → prove → save is the loop, and the store is where seeing happens.
9
+ Routing is half of it. ModelRig also records the **execution graph** of your workstream — the run, its steps, and the artifacts each step produced — versioned, hashed, and lineaged, into an inspectable store you can open: when someone asks why the pipeline did that, you pull up the run and show them. This is the **standard path**, not an add-on: every pipeline execution is a run — wrap it in `run.start` / `run.scope`, name a step per model-call family (name it with `stepKey`), save the work products, and inside that context the SDK auto-stamps `run_id` for you, so the run appears on `/runs` and `/projects` with no hand-written id (the SDK auto-stamps the `step` key inside a run context too; the `stepKey` you pass names the step, and always wins). The namespace is **on by default once a control plane is configured** (a ModelRig API key, or the self-host Supabase pair); a pure local-only rig stays off; to opt out of capture, set posture `off` in account settings (the console or the `set_org_settings` MCP tool) — the account posture is the control, not a deployment env var. Artifact *metadata and lineage* ride the same exporter; artifact *content* is a separate axis — the **recommended, encouraged** path, since content custody is what turns your captured prompts, outputs and evidence into analysis, but it stays consent-gated: the signup default is `managed` — every new org is created with content custody on, having accepted the published terms at signup — and opting out is one account setting (in the console or the `set_org_settings` MCP tool, set posture `off` or `metadata`, or use a `zero_retention` route). A failing artifact can be promoted to a regression case and replayed as a bake-off — see → prove → save is the loop, and the store is where seeing happens.
10
10
 
11
11
  What is retained beyond that is your choice, per route, and there are two lanes.
12
12
 
@@ -29,6 +29,7 @@ The five-step get-started is the front door; these are the shipped surfaces it d
29
29
  - **Published receipts** — publish a signed, login-free receipt of a bake-off or route at `/r/<id>` — counts, scores and hashes only, verifiable at `/v1/verify/<id>`; names aliased, content never leaves. See [Published receipts](receipts.md).
30
30
  - **Analytics query API** — a self-describing, read-only query surface over your own telemetry (`GET /v1/analytics/meta`, `POST /v1/analytics/query`): an allowlisted SELECT grammar that returns metadata only, never content. See [Optimization loop](optimization-loop.md).
31
31
  - **Provenance & trust** — provenance-first prompt assembly keeps third-party content out of the most-trusted slot; annotate a variable `x-trust: untrusted` to spotlight it. See [Provenance & trust](screening.md).
32
- - **Bake-offs — pairwise, disagreement & regret** — replay your own traffic, judge candidates pairwise with disagreement shown, and propose the lowest-**regret** arm under a quality gate (near-optimal, not merely cheapest). See [Bake-offs & replay](bakeoffs.md) and [Run verdicts](run-verdicts.md).
32
+ - **Bake-offs — pairwise, disagreement & regret** — replay your own traffic, judge candidates pairwise with disagreement shown, and propose the lowest-**regret** arm under a quality gate (near-optimal, not merely cheapest). See [Bake-offs & replay](bakeoffs.md) and the [Grade protocol](grade-protocol.md).
33
+ - **Live routing (call-config)** — change which model serves a route *live*, no redeploy: pin a model, or run a weighted A/B (the chosen arm records on `meta.arm`). The SDK reads the route's config just-before-call (fail-open; no override ⇒ byte-identical) and overlays it onto a bundle you serve through `rig.runBundle`. Set it in the console's call-config card, or have your coding agent set it over MCP (`set_call_config`, the `route-config` scope — explicit and reversible). See [Change your route's model live](quickstart.md#change-your-routes-model-live-no-redeploy) and [The MCP oracle](mcp-oracle.md#setting-a-routes-live-config).
33
34
 
34
35
  New claims are held to the same bar as the five steps: content custody stays early access (metadata and hashes), and pricing stays the launch-terms sentence.
@@ -1,31 +1,42 @@
1
1
  # Artifact instrumentation (runs, steps, artifacts)
2
2
 
3
+ **The question this page answers: how do I record what my pipeline actually did, run by run, step by step, without changing what it does?**
4
+
3
5
  ModelRig can record the **execution graph** of any pipeline your code runs — the
4
6
  run, its steps, and the artifacts each step produced (the prompt, the raw model
5
7
  text, the parsed output, the evidence it grounded on) — with lineage,
6
- evaluations, and an integrity hash for each. The console then renders that run as
8
+ grades, and an integrity hash for each. The console then renders that run as
7
9
  a semantic chain you click through: *what your pipeline actually did*.
8
10
 
9
- It is **on by default when a control plane is configured** (an api-mode
10
- `rig_sk_` key, or the direct-mode Supabase pair) — metadata + hashes only. A
11
- **pure local-only rig** (no sink to ship to) stays off. Either way it is
12
- additive; set `MODELRIG_ARTIFACTS=0` to opt out, and with the namespace off it
13
- is inert: `run.start` is a no-op, `artifact.save` returns null, no rows are
14
- written, and your pipeline behaves byte-for-byte as before. `modelrig status`
15
- prints the current posture (on/off + why).
16
-
17
- > **Metadata + hashes only, this release.** The serialized value you save is
18
- > hashed (sha256) and the bytes are **discarded** — nothing but the metadata row
19
- > is persisted. Content custody (the blob path) ships in a later release; the
20
- > hash proves integrity today. A `zeroRetention` run refuses every artifact
21
- > fail-closed at the SDK gate.
11
+ Recording your workstream is the **recommended, normal path** — it is what the
12
+ run → step → artifact graph and every downstream analysis are built on — so it is
13
+ **on by default when a control plane is configured** (an api-mode `rig_sk_` key,
14
+ or the direct-mode Supabase pair) — metadata + hashes only. A **pure local-only
15
+ rig** (no sink to ship to) stays off. Either way it is additive, and the **opt-out
16
+ is one account setting**: set posture `off` in account settings (the console or the
17
+ `set_org_settings` MCP tool) — the account posture is the customer control, not a
18
+ deployment env var. (A pure local-only rig with no control plane is inert either
19
+ way — `run.start` a no-op, `artifact.save` null, no rows written, your pipeline
20
+ byte-for-byte as before.) `modelrig status` prints the current posture (on/off + why). Storing the artifact *content bytes* on top of this metadata is the
21
+ second axis — the recommended, consent-gated [content custody](content-custody.md)
22
+ path — never blurred with the telemetry axis here.
23
+
24
+ > **What `artifact.save` keeps.** This page is the metadata axis: every saved
25
+ > artifact records a metadata row with a sha256 integrity hash, whatever your
26
+ > content setting. Whether the serialized *bytes* are kept as well is your
27
+ > organization's [content custody](content-custody.md) posture: new
28
+ > organizations are created `managed` (bytes kept in your managed store); posture
29
+ > `metadata` keeps hashes only and `off` captures nothing. A `zeroRetention` run
30
+ > refuses every artifact fail-closed at the SDK gate.
22
31
 
23
32
  ## The namespace toggle
24
33
 
25
34
  ```bash
26
- # On by default once a control plane is configured — nothing to set.
27
- export MODELRIG_ARTIFACTS=0 # opt OUT (or force ON with =1 on a
28
- # local-only rig)
35
+ # On by default once a control plane is configured — nothing to set. The CUSTOMER
36
+ # opt-out is the account posture (set posture off in account settings / the
37
+ # set_org_settings MCP tool), not this env var.
38
+ export MODELRIG_ARTIFACTS=1 # local-only namespace toggle: force ON
39
+ # with =1 on a rig with no control plane
29
40
  export MODELRIG_ARTIFACT_STEPS=step_a,step_b # optional: instrument a subset
30
41
  # (unset/empty = every instrumented step)
31
42
  ```
@@ -48,13 +59,13 @@ import { createRig, loadConfigFromEnv } from "modelrig";
48
59
 
49
60
  const rig = createRig(loadConfigFromEnv());
50
61
 
51
- // 1. Open a run context around the pipeline. episodeKey bridges feedback;
62
+ // 1. Open a run context around the pipeline. episodeKey bridges grades;
52
63
  // it is a grouping key, not a unique id — a natural choice is your own
53
- // per-run attempt id.
64
+ // per-run attempt id. environment fills the Env column on /runs.
54
65
  const run = rig.artifacts.run.start({
55
66
  pipeline: "my-pipeline",
56
67
  episodeKey: runAttemptId,
57
- environment: process.env.NODE_ENV,
68
+ environment: process.env.NODE_ENV, // e.g. "production", "staging", "test"
58
69
  });
59
70
  try {
60
71
  for (const step of steps) {
@@ -74,10 +85,10 @@ try {
74
85
  if (p && parsed) rig.artifacts.artifact.link(parsed, p, "derived_from");
75
86
  if (raw && parsed) rig.artifacts.artifact.link(parsed, raw, "derived_from");
76
87
 
77
- // 4. Evaluations (optional, deterministic): flag conformance, degraded
88
+ // 4. Grades (optional, deterministic): flag conformance, degraded
78
89
  // provenance, anything you can decide without an LLM judge.
79
- if (parsed) rig.artifacts.artifact.evaluate(parsed, {
80
- evaluator: "schema-check", kind: "deterministic", passed: conforms(result.json),
90
+ if (parsed) rig.grade({ kind: "artifact", id: parsed.id }, {
91
+ score: conforms(result.json) ? 1 : 0, kind: "deterministic", grader: "schema-check",
81
92
  });
82
93
  }
83
94
  run.end("succeeded");
@@ -130,19 +141,29 @@ process** (`required dimension "project" missing from run tags — costs … wil
130
141
  under-attributed on /costs`) — on the routed lane and the raw lane alike. It is a
131
142
  hygiene warning, never an error: the run proceeds.
132
143
 
133
- **Conventional tag keys** (documented, not enforced): `subject` (what a run is
134
- *about* — a ticker, a client alias), `feature`, `project` (flat grouping),
135
- `step` (names the step this call belongs to), and `run_id` (bridges a call to a
136
- run's `episodeKey`). **Inside a `run.start` / `run.scope` context the SDK stamps
137
- both `run_id` and `step` for you** — `run_id` = the run's
138
- `episodeKey`, or its id when you gave none; `step` = the current step key (your
139
- explicit `stepKey`, else `tags.step`, else the auto `route#n` / `provider/model#n`
140
- key) — on every attempt row of both lanes, unless you passed your own value (yours
141
- always wins). Outside a run context nothing is stamped, so the row is
142
- byte-identical. This is why runs are the standard path: a `run.start` alone fills
143
- both `/runs` (the graph) and `/projects` (the tag tree), with no hand-written id.
144
- ModelRig owns `lane`, `zdr_enforced`, `rung`, and — on the hosted gateway —
145
- `key_ownership`; you cannot override those.
144
+ ### Reserved tag keys (the one list)
145
+
146
+ Any other key is yours. These are reserved:
147
+
148
+ | Key | Who sets it | What it means |
149
+ |---|---|---|
150
+ | `subject` | you | Your end-customer's **opaque** id. It powers the per-customer tenant statements on `/tenants`; hash it if it looks like PII ([tenants.md](./tenants.md)). A tag named `client` or `tenant` is only a cost dimension. |
151
+ | `feature` | you | The product feature the call serves. |
152
+ | `user` | you | The end user, as an opaque id. |
153
+ | `session` | you | The conversation or session the call belongs to. |
154
+ | `cost_center` | you | The team or budget to attribute spend to. |
155
+ | `run_id` | **the SDK**, inside a run | The run's `episodeKey`, or its id when you gave none. **Don't set it yourself.** A value you pass overrides the stamp, so the call no longer joins its run on `/runs` and the console's Run ID group-by disagrees with `/runs`. Put your own attempt id in a key of your own, such as `pipeline_attempt_id`. |
156
+ | `step` | **the SDK**, inside a run | The current step key: your explicit `stepKey`, else `tags.step`, else an auto `route#n` / `provider/model#n` key. Name steps with `stepKey`. |
157
+ | `lane`, `zdr_enforced`, `rung`, `key_ownership` | ModelRig | Set by the SDK or the hosted gateway. You can't override them. |
158
+ | `replay`, `replay_capture`, `variant`, `cutover` | ModelRig tooling | Set by bake-offs and replays. |
159
+
160
+ The five keys you set are first-class group-bys in the cost and coverage views, so use them verbatim; `modelrig validate` warns about near-misses such as `costcenter` or `team`. `project` and `environment` are common conventions, not reserved.
161
+
162
+ **Inside a `run.start` / `run.scope` context the SDK stamps `run_id` and `step`
163
+ on every attempt row of both lanes.** Outside a run context nothing is stamped,
164
+ so the row is byte-identical. This is why runs are the standard path: a
165
+ `run.start` alone fills both `/runs` (the graph) and `/projects` (the tag tree),
166
+ with no hand-written id.
146
167
 
147
168
  **How they compose.** Inside a `run.start` context, both `rig.run` and
148
169
  `rig.runRaw` record a **ground-truth step** and carry the run's `run_id` tag, so
@@ -168,18 +189,177 @@ run.end("succeeded");
168
189
  > that wrapped raw calls in `run.start` got an **empty tree**. (Raw steps are
169
190
  > metadata only — no artifact *content* is written on the raw lane.)
170
191
 
192
+ ## Live call-config and A/B arms
193
+
194
+ Instrumentation records *what your pipeline did*; **call-config** changes *which model
195
+ does it*, live, without a redeploy — and the two meet on the telemetry row. When a route
196
+ runs a live A/B (`mode='experiment'`), the arm that served each call is recorded on
197
+ `inferences.meta.arm`, so the run → step → artifact graph and the console's A/B readout can
198
+ group outcomes by arm. This section is how that field gets populated; the call-config API
199
+ itself (the resolver, the fail-open contract, the console card, and the MCP write path) is
200
+ in the quickstart's [Change your route's model live](quickstart.md#change-your-routes-model-live-no-redeploy).
201
+
202
+ **Overlay live call-config onto a bundle.** A route's live config is read
203
+ **just-before-call** through a resolver and merged over the bundle you serve through
204
+ `rig.runBundle()` (never `rig.runRaw` — the raw lane owns no bundle to overlay). The read
205
+ is fail-open and, with no override, byte-identical to the deploy-time bundle:
206
+
207
+ ```ts
208
+ import {
209
+ createApiCallConfigFetcher, createCallConfigResolver, resolveCallConfigForRun,
210
+ } from "modelrig";
211
+
212
+ const resolver = createCallConfigResolver({
213
+ fetcher: createApiCallConfigFetcher({ ingestUrl, apiKey: process.env.MODELRIG_API_KEY! }),
214
+ ttlMs: 30_000, // cached; ETag-revalidated — no per-call network cost when fresh
215
+ });
216
+
217
+ // Per call: resolve the config, draw an arm locally (pin/no-override ⇒ arm is null).
218
+ const resolved = await resolver.getCallConfig(route);
219
+ const { bundle: runnable, arm } = resolveCallConfigForRun(bundle, resolved, { input });
220
+
221
+ // Thread the arm onto the run so it lands on inferences.meta.arm.
222
+ await rig.artifacts.run.scope({ pipeline: "report", episodeKey: runId }, async () => {
223
+ const out = await rig.runBundle(runnable, { input, tags: { subject, feature }, arm, stepKey });
224
+ rig.artifacts.artifact.save(out.output, { name: "section.output", type: "step_output" });
225
+ });
226
+ ```
227
+
228
+ **How `meta.arm` gets populated.** `resolveCallConfigForRun(bundle, resolved, { input, unitKey })`
229
+ returns `{ bundle, arm }`: for an experiment it draws one weighted arm and returns its stable
230
+ name; for a pin, a null/`live`/malformed resolution, or no override it returns the bundle
231
+ unchanged and `arm: null`. You pass that `arm` on `RunOptions.arm`, and the attempt-meta
232
+ producer stamps it onto **every recorded row's `meta.arm`** for that call (a non-meta-safe
233
+ value is dropped). **Absent `arm` ⇒ no `meta.arm`** — byte-identical to a non-experiment
234
+ run. Drawing is **sticky**: pass `unitKey` (or let `stickyKey` read a field from `input`) and
235
+ the same unit always draws the same arm, so the A/B is measured honestly across calls.
236
+
237
+ The console reads per-arm outcomes at `GET /v1/console/ab-readout?route=<route>` (samples,
238
+ conformance, and cost grouped by arm; non-experiment rows, which carry no arm, are ignored).
239
+ `meta.arm` sits in the recorded-metadata inventory alongside the rest of the attempt
240
+ envelope — see [content custody](content-custody.md#attempt-envelope-inferencesmeta-attemptmetav1).
241
+
171
242
  ## Never throws into your pipeline
172
243
 
173
244
  Every entry point is fail-open: a telemetry-buffer write that fails is logged and
174
245
  dropped, never thrown into your code. Instrumentation that runs *after* an
175
246
  expensive model call must never be the thing that kills the run.
176
247
 
248
+ ## Which org am I exporting to?
249
+
250
+ A valid key minted in the wrong org fails silently: every ingest call succeeds,
251
+ and the runs land in someone else's console. So the first time a process flushes
252
+ telemetry, the SDK asks `GET /v1/whoami` once and logs where it is exporting:
253
+
254
+ ```text
255
+ [modelrig] exporting to org «Acme Analytics» (acme-analytics) as key «production exporter» rig_sk_…ab12
256
+ ```
257
+
258
+ The call runs in the background. It never delays or drops a batch, and if it
259
+ fails it says nothing (a rejected key already gets its own loud `AUTH FAILED`
260
+ line). To have the SDK check the org for you, pass the slug you expect in code:
261
+
262
+ | Config option | Type | Default | What it does |
263
+ |---|---|---|---|
264
+ | `expectOrg` | `string` (org slug) | absent | When the key resolves to a different org, logs `[modelrig] ORG MISMATCH: key resolves to «a», expectOrg is «b» — telemetry is landing in «a»` loudly on every flush once the org is known. It never throws and never drops telemetry: the rows still land in the key's org. |
265
+
266
+ ```ts
267
+ const rig = createRig({ ...loadConfigFromEnv(), expectOrg: "acme-analytics" });
268
+ ```
269
+
270
+ It is a code option, not an environment variable. To gate a deploy or CI step
271
+ on the same check, run `modelrig doctor --expect-org acme-analytics`. It exits 1 and
272
+ prints both slugs when the key belongs to a different org. More on keys,
273
+ orgs and the console picker: [Orgs and keys](https://modelrig.dev/orgs-and-keys.html).
274
+
177
275
  ## See it
178
276
 
277
+ Check first that the console is showing the org your key exports to:
278
+
279
+ <!-- BLOCK:CONFIRM_ORG -->
280
+ **Confirm the org before you trust an empty console.** Run `modelrig doctor` from the same environment your app runs in. It asks GET /v1/whoami which organization the key belongs to, and the name it prints must match the org badge in the console header. If it names a different organization, your data is landing there: switch the console to that organization, or mint a key in the one you meant (https://modelrig.dev/orgs-and-keys.html).
281
+
282
+ ```bash
283
+ npx modelrig doctor
284
+ # [modelrig] key rig_sk_…AbCd → org «Your Org» ✓ (ingest https://api.modelrig.ai, …)
285
+
286
+ # No CLI in that environment? Ask the API directly:
287
+ curl -H "Authorization: Bearer $MODELRIG_API_KEY" https://api.modelrig.ai/v1/whoami
288
+ ```
289
+ <!-- /BLOCK:CONFIRM_ORG -->
290
+
179
291
  The console's **Runs** tab lists every instrumented run; `/runs/[id]` is the
180
- semantic chain (artifacts grouped by step, with hashes, costs, and evaluation
292
+ semantic chain (artifacts grouped by step, with hashes, costs, and grade
181
293
  badges) and `/artifacts/[id]` shows one artifact's lineage, versions, and
182
- evaluations. Enabling the flag also lights up the `/setup` artifacts line.
294
+ grades. Enabling the flag also lights up the `/setup` artifacts line.
295
+
296
+ ## Grades (`rig.grade`)
297
+
298
+ Capture records **what** ran; a **grade** records whether it was **good**.
299
+ A grade is a 0–1 **score** (with an optional comment) your code — or a human, or
300
+ an AI — attaches to a subject: one model call (an **inference**), a whole run
301
+ (an **episode**), or an artifact. It is the signal per-route
302
+ [optimization coverage](grade-protocol.md) is built from — the evidence level
303
+ that decides how much confidence a swap proposal carries.
304
+
305
+ ```ts
306
+ // One call — the inference id rides on every RunResult:
307
+ const result = await rig.run("example.support_summarize", { input, tags: { subject } });
308
+ rig.grade({ kind: "inference", id: result.meta.inferenceId }, { score: 1, kind: "human" });
309
+
310
+ // A whole run: the subject id is the run's run_id (its episodeKey, or its id).
311
+ rig.grade({ kind: "run", id: runAttemptId }, { score: 0.2, comment: "CAGR off by 10x", kind: "human" });
312
+ ```
313
+
314
+ - **The subject** is `{ kind: "inference", id }` (`result.meta.inferenceId`),
315
+ `{ kind: "run", id }` (the run's `run_id` — its `episodeKey`, or its id), or
316
+ `{ kind: "artifact", id }`. **`opts`** is `{ score, scale?, comment?, kind,
317
+ source? }` — the `score` in `[0, 1]` is the primitive (up = 1, down = 0), and
318
+ the pass/fail verdict is derived from it against the route threshold. A subject
319
+ naming no known inference, run, or artifact attaches to nothing — attribution
320
+ is never guessed.
321
+ - **Emit it from a signal you already have.** Most pipelines already decide
322
+ pass/fail somewhere — a schema validator, a downstream accept/reject, a human
323
+ QA gate. Emit `rig.grade` from that (score 1 on a clean pass, 0 on a rejection)
324
+ rather than adding a new review step.
325
+ - **Never on your hot path.** The write is synchronous and local (SQLite), never a
326
+ network call and never a failure your code can see; the cloud mirror rides the
327
+ telemetry exporter's normal cadence.
328
+ - **Why it matters.** A route with **≥10 attached grades** reaches **Rung 1** on
329
+ `/coverage`; a route with no schema to conform to (an `output: text` prose
330
+ route, or a schema-less JSON route) gains proposal confidence only this way. Grades are also what let `/insights`
331
+ surface a task that is degrading.
332
+ - **From a non-SDK surface** (an end user clicking thumbs-down in your web app),
333
+ post the same fields to `POST /v1/grade` with a `rig_sk_` key carrying the
334
+ `grade` scope — the org is resolved from the key, never the body. See the
335
+ [grade protocol](grade-protocol.md) for the full row shape and the ladder.
336
+
337
+ ## Promote a golden, then bake it off (`promoteToEval` → `--from-eval-cases`)
338
+
339
+ A run's artifacts are the raw material for a **durable eval case**. Promote a real
340
+ output into the org's eval suite and it becomes replayable evidence — the path a
341
+ **raw/run-based pipeline** (a `rig.runRaw` seam with no declared route) uses to
342
+ prove a cheaper model, alongside the route-scoped `bakeoff --route … --replay-last
343
+ N`.
344
+
345
+ ```ts
346
+ // Promote an artifact you saved this run into the org's eval suite.
347
+ const caseId = rig.artifacts.artifact.promoteToEval(handle, { task: "report.section" });
348
+ // `corrected` set ⇒ a golden-corrected pair; absent ⇒ a failure case.
349
+ // Unresolvable task ⇒ a warned no-op (returns null, never throws).
350
+ ```
351
+
352
+ ```bash
353
+ # Replay the promoted eval cases through variants — the raw/run-based lane.
354
+ modelrig bakeoff --from-eval-cases report.section --variants default,cheap
355
+ ```
356
+
357
+ - `promoteToEval(ref, { task?, corrected?, origin? })` binds the case to a route by
358
+ `task` (from `opts`, else the task the artifact was saved with this session), and
359
+ is also reachable from the console's one-click **"make golden"**.
360
+ - `--route` needs a **declared route**; `--from-eval-cases <task>` is the path for a
361
+ `runRaw` seam that owns no route. Either way a bake-off **measures** — it never
362
+ switches what serves a route; a swap is a human-approved PR.
183
363
 
184
364
  ## A reusable per-step seam
185
365
 
@@ -205,17 +385,10 @@ function recordStep(
205
385
  const parsed = rig.artifacts.artifact.save(work.json, { name: `${stepKey}.output`, type: "step_output", task: stepKey, stepKey });
206
386
  if (p && parsed) rig.artifacts.artifact.link(parsed, p, "derived_from");
207
387
  if (raw && parsed) rig.artifacts.artifact.link(parsed, raw, "derived_from");
208
- if (parsed) rig.artifacts.artifact.evaluate(parsed, { evaluator: "schema-check", kind: "deterministic", passed: work.conforms });
388
+ if (parsed) rig.grade({ kind: "artifact", id: parsed.id }, { score: work.conforms ? 1 : 0, kind: "deterministic", grader: "schema-check" });
209
389
  }
210
390
  ```
211
391
 
212
392
  Call `recordStep(step, { … })` once per stage inside the `run.start` / `run.end`
213
393
  context above, and every entry point stays fail-open — a save that fails is
214
394
  logged and dropped, never thrown into your pipeline.
215
-
216
- > **In-repo example (reconstructed via onboarding-test run-2).** ModelRig's own
217
- > customer-zero pipeline (the InferWealth v3 report generator) was stripped back
218
- > to a clean slate to be re-onboarded through this exact doc. Its live seam is
219
- > reconstructed as part of onboarding-test run-2; until then, the snippet above
220
- > is the reference. Do not resurrect a deleted seam from git history — writing a
221
- > fresh one is the point.