# State + Preferences Migrations

When the schema changes on breaking minor/major bumps, JSON state files
written by the previous version must be migrated forward. This directory
holds the transform scripts that do that job.

## How It Works

Each migration is a `.mjs` file named `{type}-{from}-to-{to}.mjs` where:

- `type` is `state` or `prefs`
- `from` is the version the file is expected to already be at
- `to` is the version it will be at after running

Example: `state-2.0.0-to-3.0.0.mjs` migrates `agent-state.json` from
`schemaVersion: "2.0.0"` to `"3.0.0"`.

Every migration exports a single default function:

```js
// state-2.0.0-to-3.0.0.mjs
export default function migrate(state) {
  // state is a parsed object conforming to the 2.0.0 schema
  // Return a new object conforming to the 3.0.0 schema.
  return {
    ...state,
    schemaVersion: "3.0.0",
    // new field added in 3.0.0
    newField: "default-value",
    // ...
  };
}
```

## Running Migrations

```bash
# Auto-detect current version, apply the chain up to latest
node pipeline/scripts/migrate-state.mjs \
  /Users/me/.claude/logs/multi-agent/my-project/PROJ-123/agent-state.json

# Preview what would change (no write)
node pipeline/scripts/migrate-state.mjs --dry-run <path>

# Migrate a preferences file
node pipeline/scripts/migrate-state.mjs --type=prefs \
  ~/.claude/multi-agent-preferences.json
```

Migrations are **always applied in order**. If a state is at 2.0.0 and the
current schema is 4.0.0, the runner applies 2→3 then 3→4 sequentially.
Partial chains are not allowed  -  all versions in between must have
migrations committed.

## Current State

- `state` schema version: `2.0.0` (no migrations yet)
- `prefs` schema version: `2.0.0` (no migrations yet)

First breaking schema change will land its migration here. The pattern and
runner are pre-wired so the change is mechanical: drop the file in, bump the
target schema's `const`, done.

## Guidelines for Authors

- Keep each migration pure  -  no fs / network / random. Same input must produce
  same output so replaying is idempotent.
- Always write the `schemaVersion` in the returned object.
- Never delete user data silently. If a field is removed, either copy it
  into an `_archived` field or document its disappearance in the migration
  header comment.
- Ship a test fixture in `pipeline/schemas/migrations/fixtures/` showing
  before/after for a realistic state.
