# PostgreSQL model migrations

Recorded migrations are Cicadel's standard schema workflow. GORM handles runtime
queries; it never replaces history with `AutoMigrate`. The first-party
`contrib/migrations` module supplies generation, historical state, version
dependencies, execution, verification and recovery. Standard management commands
use this implementation.

## Ask your agent

```text
Read https://cicadel.fauzaanu.com/en/0.1.0/topics/migrations/index.md.
Add [model or schema change] through the application's registered Models and
recorded PostgreSQL history. Generate and review SQL, test against a disposable
database, and report the plan before touching an existing production database.
Do not use AutoMigrate or rewrite applied migration files.
```

## Standard workflow

`startproject` includes accounts, formsets, migration commands and typed query
generation. `startapp blog` creates an app with `Models()`, `MigrationDirectory`
and an embedded migration filesystem. Register `blog.New()` in
`project/apps.go`, retaining the existing first-party registrations.

Add and register a model in `apps/blog/models.go`:

<!-- example: migrations/apps/blog/models.go -->
```go
package blog

import "github.com/fauzaanu/cicadel"

type Note struct {
    ID int64 `gorm:"primaryKey"`
    Title string `gorm:"not null"`
}

func (Note) TableName() string { return "blog_notes" }

func Models() []cicadel.Model {
    return []cicadel.Model{cicadel.ModelOf[Note]("note", "blog_notes")}
}
```

From the project root:

```sh
go run manage.go makemigrations blog --name initial
go run manage.go makemigrations blog --check
go run manage.go generatequeries
DATABASE_URL='postgres://.../app' go run manage.go migrate plan
DATABASE_URL='postgres://.../app' go run manage.go migrate
DATABASE_URL='postgres://.../app' go run manage.go migrate verify
DATABASE_URL='postgres://.../app' go run manage.go showmigrations
```

Generation and typed query generation run offline, even if `DATABASE_URL` points
to an unavailable server. App factories used by `InstalledApps(nil)` must remain
free of database work. Runtime commands require PostgreSQL. Generation compares
registered models with recorded historical state, never a developer's database.
Review and commit generated SQL and query files. `--check` does not write files.
Build the application again after generating SQL so its embedded history is current.

Model names, types and table ownership use the same public `ModelOf` registry for
migrations and typed queries. Declared tables must agree with GORM's table names.
Configure pinned cross-app historical references, naming strategies and custom
type mappings in `project/migrations.go` through `management.ModelMigrationOptions`.
Explicit rename, removal, conversion and backfill declarations use
`makemigrations APP --changes changes.json`; see the [operation contract](https://cicadel.fauzaanu.com/en/0.1.0/ref/migrations/index.md).

## Core module composition

The migration engine is a core default distributed through an installable Go
module. Standard projects already include its management commands. Register
models and migration sources through the app contract, and generate package-owned
migrations in the package source checkout. Application builds load that recorded
history from the embedded filesystem.

Use the same framework version for the CLI and application. Recorded migrations
and history from any release keep working with later releases; see the
[data guarantee](https://cicadel.fauzaanu.com/en/0.1.0/releases/releases/index.md#stability-guarantee).

## Recovery

Management commands load all registered migration sources into one dependency
catalog and use the runner's shared PostgreSQL lock. Applications must register
every app whose applied history they manage. Use `migrate plan --target APP=VERSION`
to review a target, then `migrate apply --target APP=VERSION --allow-data-loss` for
a deliberately reviewed rollback. `migrate baseline --reason TEXT` verifies an
eligible pre-existing initial schema. Nontransactional index recovery uses
`migrate recover --migration APP:VERSION --recovery retry|complete`.

Migration history is not a data backup. Unsupported changes fail explicitly; see
[the supported PostgreSQL operations](https://cicadel.fauzaanu.com/en/0.1.0/ref/migrations/index.md) and
[acceptance evidence](https://cicadel.fauzaanu.com/en/0.1.0/sources/contrib/migrations/ACCEPTANCE.md).
