Config as code

Routing ladders, guardrails and capture policy are set through a dashboard. That means the optimization behavior of a production service is configured out-of-band from the code it governs: it never appears in review, it is absent from git, nobody can say when it changed or who changed it, and staging quietly shares production’s settings unless someone remembers otherwise.

This makes that configuration an ordinary file.

Define

// optimizer.config.ts
import { defineConfig } from '@rfa-labs/shimmy';

export default defineConfig({
  routing: {
    ladder: { openai: { economy: 'gpt-5-mini', frontier: 'gpt-5.5' } },
    band: 1,
  },
  guard: { redact_stored_pii: true },
  capture: { capture_content: false, retention_days: 14 },
});

Plan, then apply

import { plan, apply, formatPlan } from '@rfa-labs/shimmy';

console.log(formatPlan(await plan(shimmy.control, config)));  // read-only
await apply(shimmy.control, config);

Two steps rather than one, because a routing change is not cosmetic: moving a rung changes which model serves live traffic, and learning_mode: 'fast' spends a tenant’s budgeted optimization runs. Seeing that before it happens is worth one extra call.

plan() only reads, so it is safe to run in CI on every pull request. A non-empty diff on a branch that did not touch the config is itself a finding — someone changed settings through the dashboard.

Why validation runs first

Tier names are persisted keys, not display text.

The server accepts an unrecognized one without complaint and simply starts an empty evidence chain under it. So a typo does not fail — it silently discards every downshift sample accumulated under the correct spelling and retrains the router from zero. The failure is invisible until someone notices savings regressed weeks later.

Valid tiers, in order: nano, economy, standard, premium, frontier.

Also caught locally:

  • cross-provider mode with fewer than two reachable providers.
  • A negative band or reverify_days.
  • A ladder entry that names no model.

All problems are reported at once, so one round trip fixes a whole config rather than N.

Apply order

When a plan touches several sections, they are written capture → guard → routing.

Routing is the change that alters which model serves live traffic; the other two are policy about what gets stored. If something fails partway, having tightened privacy settings before changing behavior is the better half to have completed.

A validation problem in any section blocks the whole apply, so you never land in a state matching no config file.

What you can set

SectionFields
routingladder, ladder_mode, tiebreak, band, allow_sub_standard_probing, learning_mode, reverify_days
guardredact_stored_pii, redact_upstream_pii, block_on_injection, injection_threshold
capturecapture_content, retention_days

Omitted sections are untouched. Setting ladder: null clears the override and falls back to the server’s defaults — distinct from omitting the key, and reported as a change.

Next