Appearance
Experiment locally, then promote the selection model
The sanctioned loop for authoring factors/rules/tracks safely (#1962): hydrate local from prod, experiment locally, then promote the finished model to the remote DB — with a dry-run diff at every write. Envelope format: selection-model-export.
Local D1 deliberately carries zero recommendation rules (
seed:remote-only fragments). Importing a prod export is the sanctioned way to hydrate local — not running the remote seed against local.
1. Hydrate local from prod
bash
npm run model:export -- --remote --out model-prod.json # export PROD (read-only)
npm run model:import -- model-prod.json # dry-run diff against LOCAL
npm run model:import -- model-prod.json --apply # write LOCAL2. Experiment locally
Edit factors, rules, rulesets, and tracks through the local admin (/admin/factors, /admin/right-sizing, /admin/tracks) with the dev server (dev-up). The guided flow at http://localhost:6650 exercises the model live.
3. Promote local → remote
bash
npm run model:promote # exports LOCAL, dry-runs against REMOTE, prints the diff
npm run model:promote -- --apply # same, then applies after an explicit confirmPromotion refuses loudly (admin tier) when:
- any rule references a system/attribute the remote doesn't have — the error names the rule;
- an enum threshold doesn't resolve through the remote's alias tables (#1851);
- a track's slug collides with a different track already on the remote.
Warnings (promotion proceeds): education media assets absent on the remote import as "no media" (R2 objects don't travel); a slug change to an already-published remote track is ignored (published slugs are locked).
Before the first promotion of a session, take a backup:npm run backup:remote (runbook).
Admin (no CLI) alternative
/admin/model on either instance: download the export from one, upload it to the other — the dry-run diff is mandatory there too, and apply is audit-logged.