Skip to content

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 LOCAL

2. 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 confirm

Promotion 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.