Jadpo is experimental and not ready for production. These docs follow the compiler's main branch as of 30 Oct 2026. What's built · Install 0.1.0
jadpo
Reference: Data

Data

Migrations

Entities describe the schema you want. A migration records each step from one version of the database to the next. jadpo migrate new add_priority compares the entities with the previous migration's snapshot and writes the next folder:

text
database/migrations/20261020T1400_add_priority/  snapshot.json        generated: the whole schema at this point  postgres.sql         generated: the difference from the previous snapshot  changes.jadpo        only when a decision is needed

When a change needs a decision, jadpo migrate new writes changes.jadpo with a line to fill in for each one, such as Todo.priority: {}. jadpo check names what is missing until it is written, and running jadpo migrate new again finishes the migration, writing the decisions into its SQL. Here a required priority is added, so existing rows need a value, and the legacy flag is retired:

database/migrations/20261020T1401_add_priority/changes.jadpo:

example
// changes.jadpo/// Adds a priority to every todo and retires the legacy flag.migration {    Todo.priority: {        backfill: Priority.normal  // existing rows need a value    }    Todo.legacy_flag: FieldChange.retired}
  • Forgetting is caught at once. jadpo check fails while the entities and the latest snapshot disagree, naming what changed and the command to run, so the editor, the build and CI all stop. Loose files, with no app.jadpo, have no migrations, so the check is a project's.
  • Decisions only where needed: a value for existing rows of a required field, or retiring a field.
  • Removing a field takes two migrations. FieldChange.retired comes first: code stops using the field, and the column stays, now nullable, for servers still running the old build. Later, FieldChange.dropped removes it, and needs an explicit acceptance, a person's recorded approval of a breaking change (Acceptances).
  • Renames keep their data. The identity registry, a file Jadpo keeps under database/, records what each renamed thing was born as (Records and manifest). Tables and columns keep the names they were born with, so renaming a field keeps its column. A field renamed by hand, without the registry, reads as one removed and one added, and the removed one's decision says so.
  • Backfills can be worked out from each row's own fields, for a new field that reshapes data the entity already holds (Computed backfills).
  • jadpo migrate apply runs pending migrations as a deploy step, each in a transaction, one per store, and records each in the store's jadpo_migrations table. It refuses a database that applied a migration the project doesn't have, or one that changed since. jadpo migrate status lists the migrations and which a database has applied.
  • A native deployable checks its database. A native deployable built by jadpo build refuses to start against a database that hasn't applied every migration it was built with, or that applied one it doesn't have or that changed since, naming the store and the migrations (A native deployable).
  • The test database. jadpo test gives each action test its own copy of a template built from the migrations, on the server JADPO_TEST_DATABASE_URL names or a disposable one, with copies prepared in the background. --keep-failed keeps a failed test's copy.

A migration's postgres.sql holds a section per store, each opened by a -- store: <name> line. It names every table, column, constraint and index as the snapshot does: a table after its entity's born name in lower_snake_case (Todo is todo), a column after its field, and keys and indexes <table>_<name>_key, _fkey and _idx, cut short with a digest past PostgreSQL's 63 bytes. Each store that holds an entity also gets the runtime's system tables, prefixed jadpo_.

Not built yet: jadpo rename, which writes the identity registry (Tooling); FieldChange.dropped and its acceptance; removing an entity, moving one between stores, and changing a column's type other than widening it. jadpo migrate new says so for each.

Markdown for agents: /docs/migrations.md.Built from Jadpo main at a707efb.