# gizdb:migrate

Bounded migration commands use the public DAL actor and its transaction owner.

```gizlang
import * as Migrate from "gizdb:migrate";
```

## Types

### `Backfill`

```gizlang
type Backfill = { source: string; keys: Vec<Typed.Slot>; inputs: Vec<Typed.Slot>; target: string; outputs: Vec<Typed.Slot>; mode: string; transform: (row: Vec<Value>) => Result<Vec<Value>, DbError> effects {fail}; }
```

Inputs and outputs start with the complete primary key, in storage order.
The transform is pure. It can run again after an uncommitted batch fails.
insert moves rows to a new table; update fills non-key columns in place.

- `source`: `string`
- `keys`: `Vec<M79.Slot>`
- `inputs`: `Vec<M79.Slot>`
- `target`: `string`
- `outputs`: `Vec<M79.Slot>`
- `mode`: `string`
- `transform`: `(row: Vec<M80.Value>) => _gizlang_sdk_core.Result<Vec<M80.Value>, M81.DbError> effects { fail }`

### `Step`

```gizlang
type Step = | { kind: "schema"; statements: Vec<Statement>; } | { kind: "data"; backfill: Backfill; } | { kind: "contract"; statements: Vec<Statement>; }
```

The schema planner's caller supplies lowered statements. This executor does
not derive a second schema. Only an explicit final contract can remove a column.

- `schema`
- `data`
- `contract`

### `Migration`

```gizlang
type Migration = { id: string; digest: bytes; steps: Vec<Step>; }
```

The caller supplies a stable 32-byte digest of the complete plan and transform.
Function values have no stable serialization. Reusing a digest after editing
a transform violates this declaration.

- `id`: `string`
- `digest`: `bytes`
- `steps`: `Vec<M83.Step>`

### `Limits`

```gizlang
type Limits = { rows: i64; bytes: i64; batches: i64; }
```

bytes bounds retained source pages and the encoded input, output and progress
values of each batch. The DAL owns SQL/WAL allocation admission separately.
batches=0 runs until completion; a positive value pauses after that many data commits.

- `rows`: `i64`
- `bytes`: `i64`
- `batches`: `i64`

### `Progress`

```gizlang
type Progress = { id: string; step: i64; lastKey: string; rows: i64; state: string; digest: bytes; }
```

Where a migration stands: its step, the last key it moved, the rows so far, its state and its plan's digest.

- `id`: `string`
- `step`: `i64`
- `lastKey`: `string`
- `rows`: `i64`
- `state`: `string`
- `digest`: `bytes`

### `Report`

```gizlang
type Report = { progress: Progress; batches: i64; paused: bool; }
```

What a run did: the progress it reached, its batches, and whether it paused at Limits.batches.

- `progress`: `M83.Progress`
- `batches`: `i64`
- `paused`: `bool`

## Functions

### `status`

```gizlang
function status(connection: Connection, id: string): Option<Progress> throws DbError
```

Missing metadata or an unknown id returns none. Other failures remain errors.

Effects: fail, io.

### `apply`

```gizlang
function apply(connection: Connection, migration: Migration, limits: Limits): Report throws DbError
```

Each committed batch and its cursor are one actor write transaction. A fresh
frontier is taken BEFORE progress and source reads. apply fences that frontier
in the actor before it writes. Therefore every accepted transform used the
row's current state at commit. A concurrent commit returns conflict; resume
rereads progress and the failed batch. No stale value or progress is published.

Guarantee: per-row commit-time conversion, not a continuously maintained
projection. Edits/inserts/deletes behind lastKey are not replayed. Same-table
updates preserve fields outside outputs. Applications must maintain converted
fields on subsequent writes before an explicit contract is safe. General
online catch-up needs durable change delivery acknowledged in the SAME write
as migration progress. Connection.changes currently publishes its checkpoint
separately; it cannot provide that crash guarantee through this boundary.
Transform evaluation can repeat; committed data batches cannot repeat.

Effects: fail, read, write, console, io.

