Skip to content

Document bitrouter.yaml and pivot to config-forward - #61

Merged
SPIKESPIGEL404 merged 2 commits into
mainfrom
claude/config-as-code-docs
Aug 6, 2026
Merged

Document bitrouter.yaml and pivot to config-forward#61
SPIKESPIGEL404 merged 2 commits into
mainfrom
claude/config-as-code-docs

Conversation

@SPIKESPIGEL404

Copy link
Copy Markdown
Contributor

Adds usage/configuration.mdx and moves the positioning from "no config to maintain" to config-forward.

Why

The declarative surface was undocumented. Coverage audit against content/docs/:

bitrouter.yaml block Before
presets: (@name) Concept covered, YAML key appears on zero pages
variants: (:name) Same
routing: (sort / only / ignore / require_tags) Same
policy_table: Nothing. Zero pages.
policy: / policy-lock.yaml Only quickstart + the CLI dump

Routing was documented API-first and console-first, so a reader who wanted routing in git had nowhere to land.

The page

usage/configuration.mdx — discovery order, the published JSON Schema, ${VAR} substitution, every top-level block, the policy table, the adaptive loop, and config validate in CI. Titled Configuration so it sits correctly beside CLI / MCP / Skills and doesn't overclaim (the file also holds server:, database:, mcp:), with Routing as code as the opening thesis and a named section anchor.

The differentiated claim is that the policy isn't inert. policy.writeback defaults to locked, so the router observes and proposes rather than writes; bitrouter policy evolve prints a dry-run and --apply publishes. You read the diff and commit it — the same review path as any other change. Adequacy escalation un-does downgrades that fail in production; exploration (off by default, and flagged as genuinely aggressive) finds safe downgrades instead of making you guess them. That's the part LiteLLM's config.yaml has no answer to, and it's exactly what had no docs.

The page owns the file; virtual-model.mdx and model-variants.mdx keep owning the behavior, so semantics aren't duplicated.

Positioning pivot

  • migrate-from-tensorzero claimed "no schema to maintain" as an advantage. That was the old line and it was misleading regardless — there is a schema, it's published, and it validates in CI. Recast as a difference of degree: BitRouter is also configured as code, it just doesn't make you declare the shape of your application before you can route a request. The ClickHouse/no-database contrast is untouched, since that one is real.
  • migrate-from-litellm mapped model_list onto "provider keys + routing presets" with no config file in sight. Both it and the fallback paragraph now point at the actual file.

A correction this surfaced

virtual-model.mdx said @name is a BitRouter Cloud namespace feature and that self-hosting gets models: instead. That's wrong. Verified against the binary:

$ bitrouter route "@fast" -c bitrouter.yaml
{"error":{"kind":"not_found","message":"no active provider declares model 'openai/gpt-4o-mini'",
          "context":["resolving model '@fast'"]}}

@fast resolved from the local presets: block and substituted the preset's model — it only failed because the test provider doesn't serve that model. presets: works self-hosted; Cloud adds the managed lifecycle (namespaces, enable/disable, the routing-presets endpoints), not the grammar. Separately, models: is a different feature — a named alias over an ordered endpoint chain, not invoked with @ — and the page conflated the two. Both fixed.

Verification

  • Every YAML example on the page validates, assembled into one combined config and run against the real binary: valid: true, 2 presets, 1 variant.
  • The documented validate contract is empirical, not inferred — exit 0 with counts; exit 1 with a line/column parse error; an unset ${VAR} reported under warnings and not fatal, which is what lets CI check shape without production keys.
  • The schema at the URL the page gives contains every key documented, including all of policy_table and the explore_* block.
  • pnpm lint:docs OK, 48 docs / 6 sections.
  • All 13 outbound links return 200; all 5 anchors (#routing-as-code, #the-policy-table, #the-adaptive-loop, #validate-in-ci, and cli#policy) resolve.

One thing for a follow-up, in the other repo

The schema ships at dist/schema/bitrouter.config.schema.json and declares:

"$id": "https://bitrouter.dev/schema/v1.0.0-alpha.27/config.schema.json"

bitrouter.dev is NXDOMAIN — the domain doesn't resolve, and bitrouter.ai serves 404 there. This page therefore documents the raw GitHub URL, which is live and follows the pattern DEFAULT_REGISTRY_URL already uses for the registry. Worth either serving dist/schema/ from bitrouter.ai or changing the $id in helpers/dist-helper/src/schema.rs — a two-line fix that needs no infra.

🤖 Generated with Claude Code

The whole declarative surface was undocumented. `presets:` never appeared
as a YAML key on any page, `routing:` and `variants:` likewise, and
`policy_table:` — tiered per-request routing with a tool guardrail and
online adequacy learning — had zero coverage anywhere. Routing concepts
were documented API-first, so a reader who wanted routing in git had
nowhere to land.

Add `usage/configuration.mdx`: discovery order, the published JSON
Schema, `${VAR}` substitution, every top-level block, the policy table,
the adaptive loop, and `config validate` in CI.

The thesis is routing as code, and the differentiated half is that the
policy is not inert. `policy.writeback` defaults to `locked`, so the
router observes and proposes; `bitrouter policy evolve` prints a
dry-run and `--apply` publishes. You read the diff and commit it — the
same review path as any other change. That is the part LiteLLM's
config.yaml has no answer to, and it happens to be exactly what was
undocumented.

Pivot the positioning to match:

- migrate-from-tensorzero claimed "no schema to maintain" as an
  advantage. That is no longer the line we want and was misleading
  anyway — there is a schema, it is published, and it validates in CI.
  Recast the difference as one of degree: BitRouter is also configured
  as code, it just doesn't make you declare the shape of your
  application before you can route a request.
- migrate-from-litellm mapped `model_list` onto "provider keys +
  routing presets" with no config file in sight. Point both it and the
  fallback paragraph at the actual file.

Also corrects virtual-model.mdx, which said `@name` is a Cloud
namespace feature and self-host gets `models:` instead. Verified
otherwise against the binary: `presets:` in bitrouter.yaml resolves
`@name` locally (`bitrouter route "@fast"` substitutes the preset's
model), and `models:` is a separate feature — a named alias over an
ordered endpoint chain, not invoked with `@`. The two were conflated.

Verified: every YAML example on the page validates as one combined
config against the real binary (valid, 2 presets, 1 variant); the
documented validate contract is empirical — exit 0 with counts, exit 1
with a line/column parse error, and unset `${VAR}` reported under
warnings WITHOUT failing, so CI can check shape without production
keys; the schema at the documented URL contains every key used; all 13
outbound links return 200 and all 5 anchors resolve.
Both PRs added a page to `usage/`, so the section list and CONTRIBUTING's
inventory conflicted. Both resolutions are additive — neither side was
replacing the other.

Nav order is cli · tui · configuration · mcp · skills: the two surfaces
you run, then the file they read, then the two agent-facing ones.

Verified after merging: lint 49 docs / 6 sections, now including the
GitHub-alert rule main gained in #60 — the configuration page uses
<Callout> throughout and passes it; 83/83 tests; all five Usage pages
plus the two edited pages return 200.
@SPIKESPIGEL404
SPIKESPIGEL404 merged commit c911d3e into main Aug 6, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant