ultrarouter docs
Guides

The training-ready export

What the Flywheel export is, what `not_trainable` means, and the consent that gates it. Training on your content is a separate, governed decision — it runs only for your own workspace, never a shared or cross-workspace model.

The Flywheel's last stage turns your consented outcome ledger into a training-ready dataset: same-prompt supervised (SFT) and preference examples with pinned versions, an executable grader, and immutable content-addressed splits. The export is yours, and whether ultrarouter trains a model from your content is a separate, governed decision described below.

This page explains what the export contains, why some examples are labelled not_trainable, the consent that has to be on before a single example is produced, and how any training that happens is governed.

The export converter and its guarantees are built — the conversion, the trainability classifier, the immutable splits, the readiness report and the consent-revocation tombstones are all in code and covered by tests. What is a later surface is the packaged download from the dashboard; where this guide reaches that step it says what you will see, never a fabricated button. The consent below is live today.

How training is governed

The no-training promise was reversed on 2026-09-20. The contract now is exact:

We process authorized workspace content to train and evaluate models exclusively for your workspace. We do not use it to train shared models or another customer's models. Approved subprocessors, retention, disabling and deletion terms are disclosed.

Training is allowed only when every one of six conjuncts holds, each failing closed on missing evidence:

train_allowed = platform_enabled AND workspace_enabled AND contract_authorized
              AND source_eligible AND retention_valid AND provider_approved
  • platform_enabled / workspace_enabled — a superadmin-only platform switch (default on) and a customer-visible per-workspace switch (default on for new workspaces under a training agreement accepted at signup). A superadmin may turn training off globally but can never override a customer's OFF.
  • contract_authorized — a versioned training agreement accepted for the workspace, plus the applicable processing authority for the people in the data (recorded per jurisdiction, never assumed). Content captured under the previous no-training promise needs source-linked renewed authorization before it may train.
  • source_eligible — workspace ownership, current source consents, validated train-eligible labels, and both holdouts excluded.
  • retention_valid — checked for every contributing source.
  • provider_approved — the operation, environment, region, subprocessors, no-reuse terms and regulated scope. A BAA alone is not sufficient; regulated / PHI workspaces stay off until an approved scope and contract chain exists.

No lever and no trainer ever reads holdout traffic. Training authorization is separate from training_export_consent: enabling the export never authorizes training, and neither permission silently grants the other. Raw items never cross a workspace.

What is built, and what is not. The two switches and the train_allowed enforcement are built (row TRN0b): the superadmin-only platform switch (default on) lives in the admin console, the per-workspace switch (default on) is on your Flywheel screen, and train_allowed is one pure function enforced at every gate. Turning the workspace switch off — or withdrawing consent — invalidates the authorization epoch: serving of that workspace's adapters stops within one 5 s window, active jobs are cancelled, and a stale provider callback can never restore an artifact (not even after training is re-enabled). What is not built is a trainer: no training run of any kind executes yet. Until a governed trainer lands (row TRN1), no customer-data training run happens, and the grep-level guarantee (TST-FLW-TRAINING-GOVERNED) holds that no production code path launches an ungated training job. The export is a dataset a customer can also train on themselves; your data stays tenant-isolated.

What the export contains

The converter (@ultrarouter/core#buildTrainingExport) produces:

  • SFT examples — a same-prompt completion, model-attributed, with the exact prompt/ config version and the recipe version the reference was derived under pinned to each row.
  • Preference examples — chosen/rejected pairs from your corrections.
  • An executable grader spec — reference values plus the closed FLW-24 predicate (equals, within_tolerance, ge, le, resolved_by_source) a consumer runs against a candidate model's output. The grader is reused for the test split.
  • Immutable, content-addressed splits — every prompt's split is a deterministic bucket of (dataset salt, prompt hash): train [0,80), validation [80,90), test [90,100). Re-running over the same inputs reproduces a byte-identical bundle; a changed input yields a new hash and a new version, never a mutated one.
  • A readiness report — counts, coverage and limitations. It states what the dataset can and cannot support; it never claims that learning or improvement occurred.

What not_trainable means

not_trainable is a first-class label, never a silent drop. Every candidate becomes an exported example; one that cannot honestly be trained on is exported carrying its reason, so a consumer can count what it cannot use rather than wonder why rows went missing. A not_trainable example carries no training target — the SFT completion or preference pair is simply absent.

The reason vocabulary is closed, and it fails closed (an undeterminable state maps to the most conservative reason, never to "trainable"):

ReasonWhy it cannot teach
proxy_onlyA proxy channel row — proxies rank, they never teach.
rank_onlyA behavioral (or otherwise rank-carrying) row — a ranking signal, not a label.
audit_onlylearning_use is audit (infra/unclassified tool error, or an unqualified inferred rule).
holdout_armThe artifact's exposure arm is the sacred standing / lever holdout — the holdout never teaches.
replay_armThe FLW-75 replay hindcast arm — it ranks, never teaches.
mixed_armA mixed artifact — its requests disagree, so it is ineligible for a target.
unqualified_extractorThe teaching label came from an unqualified extractor — never a training label.
missing_consentConsent for the source was withdrawn.
no_referenceNo accepted/corrected reference output exists to learn from.

The rule underneath the table is the same law that runs through the whole Flywheel: proxies rank; they never teach. A training target can only be minted through the by-construction teaching gate — a proxy or behavioral row is structurally incapable of producing one, so the label and the target can never disagree. The export teaches only from the arms the rest of the system teaches from: treated, pre_lever and imported; the holdout, lever_holdout, replay and mixed arms are absent by construction.

The export is owner-gated and off by default. Two consents matter:

  • training_export_consent — allows the training-ready export to be produced at all.
  • corrections_consent — required for the stored final bodies the SFT targets are drawn from.

Both are owner-only, audited, and bump the config version. Set them on the Flywheel screen, or over the API (a signed-in owner session, not a raw key):

# Session-authenticated, owner-only. Enables the export and pins corrections retention.
curl -sS -X PATCH "$UR_BASE_URL/v1/workspaces/ws_…/flywheel-consents" \
  -H "Cookie: better-auth.session_token=$UR_SESSION" \
  -H "Content-Type: application/json" \
  -d '{ "training_export_consent": true, "corrections_consent": true }'

Revocation reaches the export. The bundle carries its consentLineage — the consents its sources derive from — so revoking a consent tombstones every export whose lineage crosses it. Revocation stops new derivations and marks existing exports; it never rewrites what was already produced. Neither consent defaults on, and both are revocable.

What you will see next

The converter, the trainability classifier, the split rule, the readiness report and the revocation tombstones are built and tested. The packaged retrieval — a downloadable dataset from the Flywheel screen, with its readiness report and not_trainable breakdown — is published as that surface ships. Until then, the substance is real: the consent gate is live, the conversion is deterministic and content-addressed, and the export/training separation holds today — enabling the export never authorizes training.

What this proves — and what it does not

Proves. Your consented outcome ledger can become a portable, versioned, content-addressed training dataset you own — with every untrainable example labelled and counted, teaching confined to the non-holdout arms by construction, and revocation reaching existing exports. No production code path launches an ungated training job (TST-FLW-TRAINING-GOVERNED).

Does not prove. An export is a dataset, not a result. It does not demonstrate that training on it improves anything — proving that is a separate, governed step (yours to run, or ours per-workspace once the switches ship). The readiness report states counts and limitations, never a lift.

Next steps

  • The Flywheel — where the outcomes the export draws from are captured.
  • Holdout and lift — why the holdout and replay arms are excluded from every training target.
  • Record outcomes — the capture that fills the ledger the export reads.