modelrig 0.4.0 → 0.5.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 (55) hide show
  1. package/CHANGELOG.md +129 -0
  2. package/README.md +13 -0
  3. package/dist/adapters/gemini.d.ts +39 -0
  4. package/dist/adapters/gemini.d.ts.map +1 -1
  5. package/dist/adapters/gemini.js +53 -2
  6. package/dist/artifacts/artifact.d.ts +3 -2
  7. package/dist/artifacts/artifact.d.ts.map +1 -1
  8. package/dist/artifacts/artifact.js +11 -4
  9. package/dist/artifacts/run-context.d.ts +21 -2
  10. package/dist/artifacts/run-context.d.ts.map +1 -1
  11. package/dist/artifacts/run-context.js +46 -1
  12. package/dist/bakeoff/replay.d.ts.map +1 -1
  13. package/dist/bakeoff/replay.js +17 -1
  14. package/dist/cli.d.ts.map +1 -1
  15. package/dist/cli.js +21 -1
  16. package/dist/index.d.ts +15 -9
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +15 -5
  19. package/dist/registry/select.d.ts +59 -0
  20. package/dist/registry/select.d.ts.map +1 -1
  21. package/dist/registry/select.js +66 -0
  22. package/dist/routes/loader-warnings.d.ts +28 -0
  23. package/dist/routes/loader-warnings.d.ts.map +1 -0
  24. package/dist/routes/loader-warnings.js +56 -0
  25. package/dist/routes/loader.d.ts +1 -11
  26. package/dist/routes/loader.d.ts.map +1 -1
  27. package/dist/routes/loader.js +44 -26
  28. package/dist/run/raw-defaults.d.ts +52 -0
  29. package/dist/run/raw-defaults.d.ts.map +1 -0
  30. package/dist/run/raw-defaults.js +123 -0
  31. package/dist/run/raw.d.ts +12 -0
  32. package/dist/run/raw.d.ts.map +1 -1
  33. package/dist/run/raw.js +90 -8
  34. package/dist/run/resolve.d.ts +17 -1
  35. package/dist/run/resolve.d.ts.map +1 -1
  36. package/dist/run/resolve.js +20 -4
  37. package/dist/run/routing-docs.js +1 -1
  38. package/dist/run/run.d.ts +7 -1
  39. package/dist/run/run.d.ts.map +1 -1
  40. package/dist/run/run.js +14 -33
  41. package/dist/run/variant.d.ts +43 -0
  42. package/dist/run/variant.d.ts.map +1 -0
  43. package/dist/run/variant.js +86 -0
  44. package/dist/savings/emit.d.ts +33 -0
  45. package/dist/savings/emit.d.ts.map +1 -1
  46. package/dist/savings/emit.js +75 -3
  47. package/dist/types/route.d.ts +31 -0
  48. package/dist/types/route.d.ts.map +1 -1
  49. package/dist/watch/evaluate.d.ts +27 -1
  50. package/dist/watch/evaluate.d.ts.map +1 -1
  51. package/dist/watch/evaluate.js +13 -0
  52. package/dist/watch/proposals.d.ts +28 -0
  53. package/dist/watch/proposals.d.ts.map +1 -1
  54. package/dist/watch/proposals.js +51 -0
  55. package/package.json +1 -1
package/dist/run/run.d.ts CHANGED
@@ -12,7 +12,7 @@ import type { RigManifest } from "../routes/manifest";
12
12
  import type { RunOptions, RunResult } from "../types/route";
13
13
  import type { TelemetryStore } from "../types/telemetry";
14
14
  import type { CaptureStore } from "../telemetry/capture";
15
- import type { CapabilityLookup } from "./resolve";
15
+ import type { CapabilityLookup, RunMode } from "./resolve";
16
16
  import type { SearchProvider } from "./grounding";
17
17
  import type { StreamDeltaSink } from "./dispatch";
18
18
  import type { TaskSettingsResolver } from "../settings/task-settings";
@@ -50,6 +50,12 @@ export interface RunDeps {
50
50
  * content before a capture row is written. null/omitted = local-only (the
51
51
  * YAML flag governs, no per-task scrub policy). */
52
52
  readonly settings?: TaskSettingsResolver | null;
53
+ /** Resolution mode (routing-console spec §4.4, R3) — `live` (default) on every
54
+ * production surface, `trial` ONLY on the bake-off replay path (`replayVariant`
55
+ * sets it). `trial` keeps declared `trial` candidates and permits selecting a
56
+ * trial variant; `live` drops trial candidates before minting and rejects a
57
+ * trial variant. Absent ⇒ live ⇒ byte-identical to the pre-R3 loop. */
58
+ readonly mode?: RunMode;
53
59
  }
54
60
  export declare function run(deps: RunDeps, routeName: string, opts: RunOptions): Promise<RunResult>;
55
61
  //# sourceMappingURL=run.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/run/run.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAMrD,OAAO,KAAK,EAAE,eAAe,EAAgB,MAAM,mBAAmB,CAAC;AAGvE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD,OAAO,KAAK,EAAsC,UAAU,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAQlD,OAAO,KAAK,EAAE,cAAc,EAAoC,MAAM,aAAa,CAAC;AAUpF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAKlD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAEtE,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IACxD,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAC;IACnC,QAAQ,CAAC,YAAY,EAAE,gBAAgB,CAAC;IACxC,+EAA+E;IAC/E,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC;;gDAE4C;IAC5C,QAAQ,CAAC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IACzC,0EAA0E;IAC1E,QAAQ,CAAC,QAAQ,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IACvC;2EACuE;IACvE,QAAQ,CAAC,QAAQ,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IACxC;;0BAEsB;IACtB,QAAQ,CAAC,MAAM,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IACxC,qDAAqD;IACrD,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C;;;mFAG+E;IAC/E,QAAQ,CAAC,aAAa,CAAC,EAAE,eAAe,CAAC;IACzC;;;;;uDAKmD;IACnD,QAAQ,CAAC,QAAQ,CAAC,EAAE,oBAAoB,GAAG,IAAI,CAAC;CACjD;AAqDD,wBAAsB,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CAmjBhG"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/run/run.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAMrD,OAAO,KAAK,EAAE,eAAe,EAAgB,MAAM,mBAAmB,CAAC;AAGvE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD,OAAO,KAAK,EAAW,UAAU,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AACrE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,KAAK,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAS3D,OAAO,KAAK,EAAE,cAAc,EAAoC,MAAM,aAAa,CAAC;AAUpF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAKlD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAEtE,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IACxD,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAC;IACnC,QAAQ,CAAC,YAAY,EAAE,gBAAgB,CAAC;IACxC,+EAA+E;IAC/E,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC;;gDAE4C;IAC5C,QAAQ,CAAC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IACzC,0EAA0E;IAC1E,QAAQ,CAAC,QAAQ,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IACvC;2EACuE;IACvE,QAAQ,CAAC,QAAQ,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IACxC;;0BAEsB;IACtB,QAAQ,CAAC,MAAM,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IACxC,qDAAqD;IACrD,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C;;;mFAG+E;IAC/E,QAAQ,CAAC,aAAa,CAAC,EAAE,eAAe,CAAC;IACzC;;;;;uDAKmD;IACnD,QAAQ,CAAC,QAAQ,CAAC,EAAE,oBAAoB,GAAG,IAAI,CAAC;IAChD;;;;2EAIuE;IACvE,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;CACzB;AAsBD,wBAAsB,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CAgkBhG"}
package/dist/run/run.js CHANGED
@@ -17,6 +17,7 @@ const pricing_1 = require("../registry/pricing");
17
17
  const failures_1 = require("../types/failures");
18
18
  const manifest_1 = require("../routes/manifest");
19
19
  const resolve_1 = require("./resolve");
20
+ const variant_1 = require("./variant");
20
21
  const envelope_1 = require("./envelope");
21
22
  const retry_1 = require("./retry");
22
23
  const repair_1 = require("./repair");
@@ -27,35 +28,6 @@ const record_attempt_1 = require("./record-attempt");
27
28
  const ladder_key_1 = require("./ladder-key");
28
29
  const run_context_1 = require("../artifacts/run-context");
29
30
  const capture_write_1 = require("./capture-write");
30
- /** Look up a named variant; null = default (base config). Unknown names are
31
- * config errors — the caller asked for something the bundle doesn't define. */
32
- function resolveVariant(route, name) {
33
- if (name === undefined || name === "default")
34
- return null;
35
- const variant = route.variants.find((v) => v.name === name);
36
- if (variant === undefined) {
37
- throw new loader_1.RouteConfigError(`route "${route.route}" has no variant "${name}" — defined: [${route.variants
38
- .map((v) => v.name)
39
- .join(", ")}]`);
40
- }
41
- return variant;
42
- }
43
- /** The variant-effective view of a route: narrowed candidates, overridden
44
- * json rung, overridden template. Loader validation guarantees variant
45
- * candidates ⊆ declared candidates, so the candidate-set invariant holds. */
46
- function effectiveRoute(route, variant) {
47
- if (variant === null)
48
- return route;
49
- return Object.freeze({
50
- ...route,
51
- candidates: variant.candidates ?? route.candidates,
52
- systemTemplate: variant.promptOverride ?? route.systemTemplate,
53
- policy: Object.freeze({
54
- ...route.policy,
55
- json: variant.json ?? route.policy.json,
56
- }),
57
- });
58
- }
59
31
  /** Phase 1 prompt convention: the bundle's system template carries the whole
60
32
  * task (constitution, data, step instructions); the user turn is a terse
61
33
  * trigger. Providers treat long system prompts + short user turns well, and
@@ -73,10 +45,11 @@ function costPerMTok(provider, model) {
73
45
  }
74
46
  const DEFAULT_SLEEP = (ms) => ms <= 0 ? Promise.resolve() : new Promise((resolve) => setTimeout(resolve, ms));
75
47
  async function run(deps, routeName, opts) {
48
+ const mode = deps.mode ?? "live";
76
49
  const baseRoute = deps.routes.get(routeName);
77
- const variant = resolveVariant(baseRoute, opts.variant);
50
+ const variant = (0, variant_1.resolveVariant)(baseRoute, opts.variant, mode);
78
51
  const servedVariant = variant?.name ?? null;
79
- const route = (0, resolve_1.applyRunRequirements)(effectiveRoute(baseRoute, variant), opts.zeroRetention === true);
52
+ const route = (0, resolve_1.applyRunRequirements)((0, variant_1.effectiveRoute)(baseRoute, variant), opts.zeroRetention === true);
80
53
  const effectiveTags = route.require.includes("zero_retention") ? { ...opts.tags, zdr_enforced: "true" } : opts.tags;
81
54
  // Data-hygiene warning (spec WS5): once per process per dimension.
82
55
  (0, manifest_1.warnMissingRequiredDimensions)(deps.manifest ?? null, opts.tags);
@@ -96,7 +69,7 @@ async function run(deps, routeName, opts) {
96
69
  // exit that records an inference also records a ground-truth step referencing
97
70
  // it (attempt-per-row, ruling c). Inert without a run.start on this path.
98
71
  const runCtx = (0, run_context_1.getActiveRunContext)();
99
- const autoStepKey = runCtx !== null ? runCtx.nextStepKey(route.route) : null;
72
+ const autoStepKey = runCtx !== null ? (0, run_context_1.resolveStepKey)(opts.stepKey, opts.tags.step, () => runCtx.nextStepKey(route.route)) : null;
100
73
  // Per-call ladder key (spec §2.1 R0): one call_id, a monotonic attempt_seq per
101
74
  // row, and the current candidate_index (advanced by nextCandidate below).
102
75
  const ladder = (0, ladder_key_1.createLadderKeyMinter)();
@@ -146,6 +119,14 @@ async function run(deps, routeName, opts) {
146
119
  systemPrompt = originalSystemPrompt + (0, repair_1.repairFeedbackBlock)(errors);
147
120
  }
148
121
  else {
122
+ // Declare-first backstop (spec §4.4): the loader rejects a `trial` candidate
123
+ // as `repair_model`, but a programmatically-built bundle (runBundle) could
124
+ // bypass the loader — so refuse to mint a trial candidate for LIVE repair
125
+ // here too (the primary ladder's trial-drop has no repair-path equivalent).
126
+ if (mode === "live" &&
127
+ route.candidates.some((c) => c.provider === step.model.provider && c.model === step.model.model && c.trial === true)) {
128
+ return null;
129
+ }
149
130
  // Brand stays in resolve.ts; loader validated repairModel ⊆ declared.
150
131
  const minted = (0, resolve_1.resolveDeclaredCandidate)(route, step.model);
151
132
  if (minted === null)
@@ -255,7 +236,7 @@ async function run(deps, routeName, opts) {
255
236
  };
256
237
  }
257
238
  const searchAvailable = deps.search !== undefined && deps.search !== null;
258
- const iter = (0, resolve_1.resolveCandidates)(route, deps.capabilities, costPerMTok, searchAvailable, (0, resolve_1.candidateFilterFromOptions)(opts));
239
+ const iter = (0, resolve_1.resolveCandidates)(route, deps.capabilities, costPerMTok, searchAvailable, (0, resolve_1.candidateFilterFromOptions)(opts), undefined, mode);
259
240
  // Advance the iterator AND publish the served candidate's index to the ladder,
260
241
  // so every row of this candidate's turn (incl. repair rows) carries it (§2.1).
261
242
  const nextCandidate = () => {
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Variant resolution — extracted from run.ts (spec §4.4, R3) to host the
3
+ * declare-first "a trial variant selected live is a config error" rule and to
4
+ * keep run.ts under the LOC cap. Pure: bundle + requested name (+ run mode) →
5
+ * the resolved variant and the variant-effective route view.
6
+ */
7
+ import type { RouteBundle, RouteVariant } from "../types/route";
8
+ import type { RunMode } from "./resolve";
9
+ /**
10
+ * A variant is trial-only when it has candidates and EVERY one maps to a
11
+ * declared `trial` candidate. Computed dynamically from `route.candidates`
12
+ * rather than trusted from the stored `variant.trialOnly` stamp, so a bundle
13
+ * built in code — a programmatic `runBundle` whose variants never passed
14
+ * through the loader that sets the flag — is judged exactly like a loaded one
15
+ * (and still earns the teaching error on a live trial selection). The loader's
16
+ * stamp, when present, is honored as a fast path.
17
+ */
18
+ export declare function isTrialOnlyVariant(route: RouteBundle, variant: RouteVariant): boolean;
19
+ /**
20
+ * Look up a named variant; null = default (base config). Unknown names are
21
+ * config errors — the caller asked for something the bundle doesn't define.
22
+ *
23
+ * Declare-first (spec §4.4): a `trialOnly` variant — one whose candidates are
24
+ * ALL declared `trial` candidates — is valid only in bake-off replay
25
+ * (`mode: "trial"`). Selecting it on a LIVE path is a `RouteConfigError` with a
26
+ * teaching message that names the bake-off, never a silent fallback (residual
27
+ * flag F2′): the run loop must not dispatch an undeclared OR trial model live.
28
+ */
29
+ export declare function resolveVariant(route: RouteBundle, name: string | undefined, mode?: RunMode): RouteVariant | null;
30
+ /**
31
+ * The variant-effective view of a route: narrowed candidates, overridden json
32
+ * rung, overridden template. Loader validation guarantees variant candidates ⊆
33
+ * declared candidates, so the candidate-set invariant holds.
34
+ *
35
+ * Declare-first (spec §4.4): a variant's `candidates` entries are authored as
36
+ * bare `{provider, model}` and do NOT carry the route-level `trial` flag. We
37
+ * MUST re-attach it from the matching declared candidate — otherwise a variant
38
+ * that narrows to a trial candidate (e.g. a mixed trial+live variant, which is
39
+ * not `trialOnly` and IS selectable live) would strip the flag and
40
+ * `resolveCandidates`' live trial-drop would let the trial model serve live.
41
+ */
42
+ export declare function effectiveRoute(route: RouteBundle, variant: RouteVariant | null): RouteBundle;
43
+ //# sourceMappingURL=variant.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"variant.d.ts","sourceRoot":"","sources":["../../src/run/variant.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAYrF;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,IAAI,GAAE,OAAgB,GACrB,YAAY,GAAG,IAAI,CAkBrB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,YAAY,GAAG,IAAI,GAAG,WAAW,CAoB5F"}
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ /**
3
+ * Variant resolution — extracted from run.ts (spec §4.4, R3) to host the
4
+ * declare-first "a trial variant selected live is a config error" rule and to
5
+ * keep run.ts under the LOC cap. Pure: bundle + requested name (+ run mode) →
6
+ * the resolved variant and the variant-effective route view.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.isTrialOnlyVariant = isTrialOnlyVariant;
10
+ exports.resolveVariant = resolveVariant;
11
+ exports.effectiveRoute = effectiveRoute;
12
+ const loader_1 = require("../routes/loader");
13
+ /**
14
+ * A variant is trial-only when it has candidates and EVERY one maps to a
15
+ * declared `trial` candidate. Computed dynamically from `route.candidates`
16
+ * rather than trusted from the stored `variant.trialOnly` stamp, so a bundle
17
+ * built in code — a programmatic `runBundle` whose variants never passed
18
+ * through the loader that sets the flag — is judged exactly like a loaded one
19
+ * (and still earns the teaching error on a live trial selection). The loader's
20
+ * stamp, when present, is honored as a fast path.
21
+ */
22
+ function isTrialOnlyVariant(route, variant) {
23
+ if (variant.trialOnly === true)
24
+ return true;
25
+ const cands = variant.candidates;
26
+ return (cands !== undefined &&
27
+ cands.length > 0 &&
28
+ cands.every((vc) => route.candidates.some((rc) => rc.provider === vc.provider && rc.model === vc.model && rc.trial === true)));
29
+ }
30
+ /**
31
+ * Look up a named variant; null = default (base config). Unknown names are
32
+ * config errors — the caller asked for something the bundle doesn't define.
33
+ *
34
+ * Declare-first (spec §4.4): a `trialOnly` variant — one whose candidates are
35
+ * ALL declared `trial` candidates — is valid only in bake-off replay
36
+ * (`mode: "trial"`). Selecting it on a LIVE path is a `RouteConfigError` with a
37
+ * teaching message that names the bake-off, never a silent fallback (residual
38
+ * flag F2′): the run loop must not dispatch an undeclared OR trial model live.
39
+ */
40
+ function resolveVariant(route, name, mode = "live") {
41
+ if (name === undefined || name === "default")
42
+ return null;
43
+ const variant = route.variants.find((v) => v.name === name);
44
+ if (variant === undefined) {
45
+ throw new loader_1.RouteConfigError(`route "${route.route}" has no variant "${name}" — defined: [${route.variants
46
+ .map((v) => v.name)
47
+ .join(", ")}]`);
48
+ }
49
+ if (mode === "live" && isTrialOnlyVariant(route, variant)) {
50
+ throw new loader_1.RouteConfigError(`route "${route.route}" variant "${name}" is a trial variant (all its candidates are declared ` +
51
+ `trial: true) — trial variants are declared but never served live. Measure it with a bake-off ` +
52
+ `(rig.bakeoff / "Run bake-off" in the console), then promote the winner to a live candidate.`);
53
+ }
54
+ return variant;
55
+ }
56
+ /**
57
+ * The variant-effective view of a route: narrowed candidates, overridden json
58
+ * rung, overridden template. Loader validation guarantees variant candidates ⊆
59
+ * declared candidates, so the candidate-set invariant holds.
60
+ *
61
+ * Declare-first (spec §4.4): a variant's `candidates` entries are authored as
62
+ * bare `{provider, model}` and do NOT carry the route-level `trial` flag. We
63
+ * MUST re-attach it from the matching declared candidate — otherwise a variant
64
+ * that narrows to a trial candidate (e.g. a mixed trial+live variant, which is
65
+ * not `trialOnly` and IS selectable live) would strip the flag and
66
+ * `resolveCandidates`' live trial-drop would let the trial model serve live.
67
+ */
68
+ function effectiveRoute(route, variant) {
69
+ if (variant === null)
70
+ return route;
71
+ const candidates = variant.candidates === undefined
72
+ ? route.candidates
73
+ : variant.candidates.map((vc) => {
74
+ const declared = route.candidates.find((c) => c.provider === vc.provider && c.model === vc.model);
75
+ return declared?.trial === true ? { ...vc, trial: true } : vc;
76
+ });
77
+ return Object.freeze({
78
+ ...route,
79
+ candidates,
80
+ systemTemplate: variant.promptOverride ?? route.systemTemplate,
81
+ policy: Object.freeze({
82
+ ...route.policy,
83
+ json: variant.json ?? route.policy.json,
84
+ }),
85
+ });
86
+ }
@@ -6,6 +6,13 @@
6
6
  * greps for it.
7
7
  */
8
8
  import type { CandidateSpec, RouteBundle } from "../types/route";
9
+ /** A challenger to declare/promote — just the identifying pair. Loose on purpose
10
+ * (`provider: string`): a challenger comes from a stored proposal or the picker,
11
+ * where the provider is already validated, and the emitters only render YAML. */
12
+ export interface ChallengerRef {
13
+ readonly provider: string;
14
+ readonly model: string;
15
+ }
9
16
  export interface SwapChange {
10
17
  readonly route: string;
11
18
  readonly file: string;
@@ -34,5 +41,31 @@ export declare function buildRestoreYaml(route: string, incumbent: readonly {
34
41
  readonly provider: string;
35
42
  readonly model: string;
36
43
  }[], currentVersion: number): string;
44
+ /** The paste-ready change that DECLARES a `trial` challenger (routing-console
45
+ * spec §4.4, declare step). Additive-only: nothing existing is removed. */
46
+ export interface DeclareTrialChange {
47
+ readonly route: string;
48
+ readonly file: string;
49
+ readonly challenger: ChallengerRef;
50
+ /** The auto-named trial variant a bake-off replays (`trial-<provider>-<model>`). */
51
+ readonly trialVariant: string;
52
+ readonly newVersion: number;
53
+ /** Paste-ready YAML: version bump + full candidates block (challenger appended
54
+ * with `trial: true`) + the trial variant to add. Complete, no elisions (A4). */
55
+ readonly yaml: string;
56
+ }
57
+ /** A stable, YAML-safe trial variant name for a challenger. */
58
+ export declare function trialVariantName(challenger: ChallengerRef): string;
59
+ /**
60
+ * Emit the declare-first YAML that adds a `trial` challenger to a route (spec
61
+ * §4.4 step 1): the challenger appended to `candidates` with `trial: true`, a
62
+ * `trial-<provider>-<model>` variant that narrows to it, and `version: n+1`. The
63
+ * challenger is DECLARED but never served live (the loader/resolver enforce
64
+ * that) — so a bake-off can measure it on real traffic before promotion. Pure,
65
+ * no filesystem (emit.test.ts greps for that). Throws when the challenger is
66
+ * already a declared candidate — you don't declare what's already declared (the
67
+ * server maps this to 422).
68
+ */
69
+ export declare function buildDeclareTrialChange(bundle: RouteBundle, challenger: ChallengerRef): DeclareTrialChange;
37
70
  export declare function buildSwapChange(bundle: RouteBundle, winningVariant: string): SwapChange;
38
71
  //# sourceMappingURL=emit.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"emit.d.ts","sourceRoot":"","sources":["../../src/savings/emit.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAgB,MAAM,gBAAgB,CAAC;AAE/E,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,2EAA2E;IAC3E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;yCACqC;IACrC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B;+DAC2D;IAC3D,QAAQ,CAAC,mBAAmB,EAAE,SAAS,aAAa,EAAE,CAAC;CACxD;AAmCD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,SAAS;IAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,EAC3E,cAAc,EAAE,MAAM,GACrB,MAAM,CAgBR;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,GAAG,UAAU,CA6CvF"}
1
+ {"version":3,"file":"emit.d.ts","sourceRoot":"","sources":["../../src/savings/emit.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAgB,MAAM,gBAAgB,CAAC;AAE/E;;iFAEiF;AACjF,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,2EAA2E;IAC3E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;yCACqC;IACrC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B;+DAC2D;IAC3D,QAAQ,CAAC,mBAAmB,EAAE,SAAS,aAAa,EAAE,CAAC;CACxD;AA2CD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,SAAS;IAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,EAC3E,cAAc,EAAE,MAAM,GACrB,MAAM,CAgBR;AAED;2EAC2E;AAC3E,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,aAAa,CAAC;IACnC,oFAAoF;IACpF,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B;qFACiF;IACjF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,+DAA+D;AAC/D,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,aAAa,GAAG,MAAM,CAElE;AAED;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,aAAa,GAAG,kBAAkB,CAiC1G;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,GAAG,UAAU,CAgEvF"}
@@ -8,9 +8,19 @@
8
8
  */
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.buildRestoreYaml = buildRestoreYaml;
11
+ exports.trialVariantName = trialVariantName;
12
+ exports.buildDeclareTrialChange = buildDeclareTrialChange;
11
13
  exports.buildSwapChange = buildSwapChange;
12
14
  function candidateLines(candidates) {
13
- return candidates.flatMap((c) => [` - provider: ${c.provider}`, ` model: ${c.model}`]);
15
+ // Preserve a candidate's `trial: true` (declare-first, spec §4.4) so an
16
+ // emitted candidates block never silently promotes a DECLARED-trial candidate
17
+ // to live. The promote path drops the flag from the WINNER explicitly (it
18
+ // passes a winner entry without `trial`), never here.
19
+ return candidates.flatMap((c) => [
20
+ ` - provider: ${c.provider}`,
21
+ ` model: ${c.model}`,
22
+ ...(c.trial === true ? [" trial: true"] : []),
23
+ ]);
14
24
  }
15
25
  function overrideLines(variant) {
16
26
  const lines = [];
@@ -57,26 +67,88 @@ function buildRestoreYaml(route, incumbent, currentVersion) {
57
67
  "",
58
68
  ].join("\n");
59
69
  }
70
+ /** A stable, YAML-safe trial variant name for a challenger. */
71
+ function trialVariantName(challenger) {
72
+ return `trial-${challenger.provider}-${challenger.model}`.replace(/[^A-Za-z0-9._-]/g, "-");
73
+ }
74
+ /**
75
+ * Emit the declare-first YAML that adds a `trial` challenger to a route (spec
76
+ * §4.4 step 1): the challenger appended to `candidates` with `trial: true`, a
77
+ * `trial-<provider>-<model>` variant that narrows to it, and `version: n+1`. The
78
+ * challenger is DECLARED but never served live (the loader/resolver enforce
79
+ * that) — so a bake-off can measure it on real traffic before promotion. Pure,
80
+ * no filesystem (emit.test.ts greps for that). Throws when the challenger is
81
+ * already a declared candidate — you don't declare what's already declared (the
82
+ * server maps this to 422).
83
+ */
84
+ function buildDeclareTrialChange(bundle, challenger) {
85
+ if (bundle.candidates.some((c) => c.provider === challenger.provider && c.model === challenger.model)) {
86
+ throw new Error(`${challenger.provider}/${challenger.model} is already a declared candidate on "${bundle.route}" — ` +
87
+ "nothing to declare (pick a model that is not yet in the route to trial it)");
88
+ }
89
+ const file = `modelrig/routes/${bundle.route}.yaml`;
90
+ const newVersion = bundle.version + 1;
91
+ const variant = trialVariantName(challenger);
92
+ const newCandidates = [...bundle.candidates, { ...challenger, trial: true }];
93
+ const yaml = [
94
+ `# declare a trial of ${challenger.provider}/${challenger.model} on ${bundle.route}`,
95
+ `# a trial candidate is DECLARED but NEVER served live — a bake-off measures it, then Promote makes it live.`,
96
+ `# apply to ${file}:`,
97
+ `# 1) bump the version`,
98
+ `version: ${newVersion}`,
99
+ `# 2) replace your candidates: block with this (the challenger is appended and marked for trial)`,
100
+ "candidates:",
101
+ ...candidateLines(newCandidates),
102
+ `# 3) add this entry to your variants: block (create the block if the route has none)`,
103
+ "variants:",
104
+ ` - name: ${variant}`,
105
+ " candidates:",
106
+ ` - provider: ${challenger.provider}`,
107
+ ` model: ${challenger.model}`,
108
+ `# Requires the modelrig SDK release that understands \`trial:\` — an older SDK ignores the flag and`,
109
+ `# would serve the model live. Bake it off with: modelrig bakeoff --from-proposal <id>`,
110
+ "",
111
+ ].join("\n");
112
+ return { route: bundle.route, file, challenger, trialVariant: variant, newVersion, yaml };
113
+ }
60
114
  function buildSwapChange(bundle, winningVariant) {
61
115
  const variant = bundle.variants.find((v) => v.name === winningVariant);
62
116
  if (variant === undefined) {
63
117
  throw new Error(`route "${bundle.route}" has no variant "${winningVariant}" — cannot emit a swap for it`);
64
118
  }
65
- const winners = variant.candidates ?? bundle.candidates;
119
+ // Promote drops `trial: true` from the WINNER and moves it to position 0. The
120
+ // flag is stripped EXPLICITLY here (not merely relied upon being absent from a
121
+ // bare variant candidate) so a hand-authored `trial: true` inside the variant's
122
+ // candidate block can never leave the promoted winner as a trial candidate — a
123
+ // silent no-op promotion where the "promoted" model is then dropped live. Other
124
+ // declared candidates keep their own trial flags (candidateLines preserves them).
125
+ const winners = (variant.candidates ?? bundle.candidates).map((c) => ({
126
+ provider: c.provider,
127
+ model: c.model,
128
+ }));
66
129
  if (winners.length === 0) {
67
130
  throw new Error(`variant "${winningVariant}" has no candidates to promote`);
68
131
  }
69
132
  const incumbent = bundle.candidates[0];
133
+ // Trial-winner promotes also remove the now-redundant trial variant (spec §4.4).
134
+ const isTrialPromotion = variant.trialOnly === true;
70
135
  const promoted = promotedOrder(bundle.candidates, winners);
71
136
  const file = `modelrig/routes/${bundle.route}.yaml`;
72
137
  const newVersion = bundle.version + 1;
138
+ const applyComment = isTrialPromotion
139
+ ? `# apply to ${file} — replace version + candidates, and REMOVE the trial variant "${winningVariant}" from variants:`
140
+ : `# apply to ${file} — replace the version and candidates fields; nothing else changes`;
141
+ const trialRemovalLine = isTrialPromotion
142
+ ? [`# remove this now-promoted trial variant from variants: (its candidate is live at position 0 above):`, `# - name: ${winningVariant}`]
143
+ : [];
73
144
  const yaml = [
74
145
  `# swap: serve ${bundle.route} on ${winners[0].provider}/${winners[0].model}`,
75
- `# apply to ${file} — replace the version and candidates fields; nothing else changes`,
146
+ applyComment,
76
147
  `version: ${newVersion}`,
77
148
  "candidates:",
78
149
  ...candidateLines(promoted),
79
150
  ...overrideLines(variant),
151
+ ...trialRemovalLine,
80
152
  "",
81
153
  ].join("\n");
82
154
  const revertYaml = [
@@ -7,6 +7,23 @@ import type { ProviderId, SamplingParams, ServiceTier, ToolCall, ToolChoice, Too
7
7
  export interface CandidateSpec {
8
8
  readonly provider: ProviderId;
9
9
  readonly model: string;
10
+ /**
11
+ * Declare-first experiment flag (routing-console spec §4.4, R3). A `trial`
12
+ * candidate is DECLARED — it is in the list the brand type, the loader and the
13
+ * runtime candidate-set guard all derive from, so the candidate-set invariant
14
+ * is untouched — but `resolveCandidates` drops it on EVERY live path
15
+ * (`rig.run`, `runStream`, `runBundle`, the hosted gateway slug lane) exactly
16
+ * as it drops a `providers.ignore` entry. Only the bake-off replay path
17
+ * (`replayVariant`, run `mode: "trial"`) keeps it, so a customer can measure a
18
+ * new model on real captured traffic before promoting it to a live candidate.
19
+ * Optional & additive (S3): absent ⇒ false ⇒ an ordinary live candidate.
20
+ *
21
+ * OLDER-SDK CAVEAT (spec §7): the pre-0.4.0 loader is TOLERANT of unknown
22
+ * candidate keys — it would ignore `trial` and serve the candidate LIVE. A
23
+ * bundle using `trial: true` therefore REQUIRES the SDK release that
24
+ * understands it; the console's declare-YAML says so.
25
+ */
26
+ readonly trial?: boolean;
10
27
  }
11
28
  export type Requirement = "schema_conformant" | "grounded" | "zero_retention" | "trace_visible";
12
29
  export type Preference = "cost" | "latency";
@@ -57,6 +74,14 @@ export interface RouteVariant {
57
74
  /** JSON rung override for this variant. */
58
75
  readonly json?: "native" | "json_mode";
59
76
  readonly repair?: RepairPolicy;
77
+ /**
78
+ * COMPUTED at load (not authored): true when every candidate this variant
79
+ * narrows to is a declared `trial` candidate (routing-console spec §4.4). A
80
+ * trial variant is valid for `rig.bakeoff` but a `RouteConfigError` if a live
81
+ * caller selects it — trial candidates are declared, never live-served. The
82
+ * declare-first flow's `trial-<provider>-<model>` variant is exactly this.
83
+ */
84
+ readonly trialOnly?: boolean;
60
85
  }
61
86
  export interface RouteBundle {
62
87
  readonly route: string;
@@ -100,6 +125,12 @@ export interface RunTags {
100
125
  export interface RunOptions {
101
126
  readonly input: Record<string, unknown>;
102
127
  readonly tags: RunTags;
128
+ /** Names the ground-truth step this run records (G1 spec §3) — the same field
129
+ * the raw lane exposes, so a routed and a raw call, plus an `artifact.save`,
130
+ * can be composed into ONE step group. Resolution: `stepKey ?? tags.step ??`
131
+ * an auto-minted `route#n`. Validated `[A-Za-z0-9_.:#-]{1,128}`; an invalid
132
+ * value is warned and dropped to the auto key. Absent ⇒ unchanged behavior. */
133
+ readonly stepKey?: string;
103
134
  readonly budget?: {
104
135
  readonly envelope: string;
105
136
  /** Budget for this envelope when it is FIRST opened (open-or-get: the first
@@ -1 +1 @@
1
- {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../src/types/route.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../src/types/route.ts"],"names":[],"mappings":"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"}
@@ -56,7 +56,26 @@ export interface WatchConfig {
56
56
  * Analyst's triggers ride the widened kinds (`insight`/`promote-eval`) or reuse
57
57
  * `bakeoff` for an Analyst-initiated bake-off — all through the SAME proposals
58
58
  * machinery and the SAME `submitDrafts` seam (trajectory F1/F4, one emission path). */
59
- export type ProposalTrigger = EconomicsTrigger | ConformDegradationTrigger | AnalystClusterTrigger | AnalystPromoteEvalTrigger | AnalystBakeoffTrigger;
59
+ export type ProposalTrigger = EconomicsTrigger | ConformDegradationTrigger | AnalystClusterTrigger | AnalystPromoteEvalTrigger | AnalystBakeoffTrigger | ManualChallengerTrigger;
60
+ /**
61
+ * A human-initiated declare-first challenger (routing-console spec §2.4 / §3, R3)
62
+ * — the console picker (or the oracle) proposes trialing a specific model on a
63
+ * route. `kind` stays `"bakeoff"`; this trigger carries the challenger and the
64
+ * `trial-<provider>-<model>` variant name a bake-off replays. `reason` /
65
+ * `requestedBy` are audit context and are DELIBERATELY excluded from the
66
+ * idempotency hash (see {@link manualChallengerTriggerHash}) so two people
67
+ * declaring the same challenger dedupe to one proposal.
68
+ */
69
+ export interface ManualChallengerTrigger {
70
+ readonly type: "manual_challenger";
71
+ readonly route: string;
72
+ readonly challenger: CandidateEconomics;
73
+ /** The trial variant a bake-off replays: `trial-<provider>-<model>`. */
74
+ readonly variant: string;
75
+ readonly reason?: string;
76
+ /** Actor id (user or key) that initiated the declare — audit only. */
77
+ readonly requestedBy?: string;
78
+ }
60
79
  /** The measured statistics behind one Analyst cluster — all deterministic. */
61
80
  export interface AnalystClusterStats {
62
81
  /** Runs in this (task, cluster) over the window. */
@@ -169,6 +188,13 @@ export interface ProposalDraft {
169
188
  }
170
189
  export declare const PROPOSAL_TTL_DAYS = 30;
171
190
  export declare function triggerHash(route: string, trigger: ProposalTrigger): string;
191
+ /**
192
+ * Idempotency key for a declare-first manual challenger — per (route, challenger)
193
+ * ONLY (spec §2.4), so `reason`/`requestedBy` never split a dedupe. The
194
+ * control-plane unique index is (org_id, route, trigger_hash), so the same
195
+ * challenger declared twice on a route by the same org is a no-op.
196
+ */
197
+ export declare function manualChallengerTriggerHash(route: string, challenger: CandidateEconomics): string;
172
198
  export declare function evaluateEvents(routes: readonly RouteEconomics[], events: readonly WatchEvent[], config: WatchConfig): ProposalDraft[];
173
199
  /** The §5.5 evidence block — every field the rendered explanation and the
174
200
  * verdict need, in one self-describing object. Rates are per-Mtok (the surface
@@ -1 +1 @@
1
- {"version":3,"file":"evaluate.d.ts","sourceRoot":"","sources":["../../src/watch/evaluate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"evaluate.d.ts","sourceRoot":"","sources":["../../src/watch/evaluate.ts"],"names":[],"mappings":"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"}
@@ -18,6 +18,7 @@
18
18
  Object.defineProperty(exports, "__esModule", { value: true });
19
19
  exports.PROPOSAL_TTL_DAYS = void 0;
20
20
  exports.triggerHash = triggerHash;
21
+ exports.manualChallengerTriggerHash = manualChallengerTriggerHash;
21
22
  exports.evaluateEvents = evaluateEvents;
22
23
  exports.buildCacheContext = buildCacheContext;
23
24
  exports.explainCacheContext = explainCacheContext;
@@ -35,6 +36,18 @@ function blended(rates) {
35
36
  function triggerHash(route, trigger) {
36
37
  return (0, node_crypto_1.createHash)("sha256").update(`${route}\n${JSON.stringify(trigger)}`).digest("hex").slice(0, 32);
37
38
  }
39
+ /**
40
+ * Idempotency key for a declare-first manual challenger — per (route, challenger)
41
+ * ONLY (spec §2.4), so `reason`/`requestedBy` never split a dedupe. The
42
+ * control-plane unique index is (org_id, route, trigger_hash), so the same
43
+ * challenger declared twice on a route by the same org is a no-op.
44
+ */
45
+ function manualChallengerTriggerHash(route, challenger) {
46
+ return (0, node_crypto_1.createHash)("sha256")
47
+ .update(`manual_challenger\n${route}\n${challenger.provider}/${challenger.model}`)
48
+ .digest("hex")
49
+ .slice(0, 32);
50
+ }
38
51
  function expiryStamp() {
39
52
  return new Date(Date.now() + exports.PROPOSAL_TTL_DAYS * 24 * 3600 * 1000).toISOString();
40
53
  }
@@ -84,6 +84,34 @@ export interface ProposalsClient {
84
84
  decide(id: string, from: ProposalStatus, to: "approved" | "rejected", decidedBy: string, decidedByUserId?: string | null, orgId?: string): Promise<boolean>;
85
85
  /** One sweep: overdue suggested/evidenced → expired. */
86
86
  expireOverdue(now: Date): Promise<void>;
87
+ /**
88
+ * Declare-first (routing-console spec §3, R3): create a HUMAN/oracle-initiated
89
+ * `manual_challenger` proposal (kind `"bakeoff"`). Idempotent on
90
+ * (org_id, route, trigger_hash) where the hash keys on (route, challenger)
91
+ * only — a duplicate returns the EXISTING id with `created:false` (the caller
92
+ * maps that to 409). Provenance (`created_by`/`created_by_key`, migration
93
+ * 00055) records the actor. Returns the row id + whether it was newly created.
94
+ */
95
+ createManualChallenger(input: CreateManualChallengerInput): Promise<{
96
+ id: string;
97
+ created: boolean;
98
+ status?: ProposalStatus;
99
+ }>;
100
+ }
101
+ export interface CreateManualChallengerInput {
102
+ readonly route: string;
103
+ readonly challenger: {
104
+ readonly provider: string;
105
+ readonly model: string;
106
+ };
107
+ /** The trial variant a bake-off replays (`trial-<provider>-<model>`). */
108
+ readonly variant: string;
109
+ readonly reason?: string;
110
+ /** Actor recorded in the trigger for the console (audit); NOT part of the hash. */
111
+ readonly requestedBy?: string;
112
+ /** Provenance columns (00055): exactly one is set for a console/oracle draft. */
113
+ readonly createdByUserId?: string | null;
114
+ readonly createdByKeyId?: string | null;
87
115
  }
88
116
  export declare function createProposalsClient(config: ProposalsClientConfig): ProposalsClient;
89
117
  //# sourceMappingURL=proposals.d.ts.map