| title | Migration |
|---|---|
| description | Migration protocol schemas |
{/*
Migration protocol — the two kinds of migration, kept apart on purpose.
A schema migration (ChangeSet + its atomic operations) reshapes the
physical database to match metadata: add a field, change a type, create an
object, run SQL. It is derivable from the metadata and applied by
os migrate plan / os migrate apply.
A data migration rewrites the rows themselves, and whether it is done is
a property of ONE DEPLOYMENT's database rather than of the installed code
version — so it cannot be expressed as a ChangeSet, and its completion cannot
be inferred from a release. DataMigrationFlag is the per-deployment record
that one ran here and its self-check passed; consumers that would act
irreversibly on migrated data gate on the flag instead of the version.
**Source:** `packages/spec/src/system/migration.zod.ts`import { AddFieldOperation, ChangeSet, CreateObjectOperation, DataMigrationFlag, DeleteObjectOperation, ExecuteSqlOperation, MigrationDependency, MigrationOperation, ModifyFieldOperation, RemoveFieldOperation, RenameObjectOperation } from '@objectstack/spec/system';
import type { AddFieldOperation, ChangeSet, CreateObjectOperation, DataMigrationFlag, DeleteObjectOperation, ExecuteSqlOperation, MigrationDependency, MigrationOperation, ModifyFieldOperation, RemoveFieldOperation, RenameObjectOperation } from '@objectstack/spec/system';
// Validate data
const result = AddFieldOperation.parse(data);Add a new field to an existing object
| Property | Type | Required | Description |
|---|---|---|---|
| type | 'add_field' |
✅ | |
| objectName | string |
✅ | Target object name |
| fieldName | string |
✅ | Name of the field to add |
| field | { name?: string; label?: string; type: Enum<'text' | 'textarea' | 'email' | 'url' | 'phone' | 'password' | 'secret' | 'markdown' | 'html' | 'richtext' | 'number' | 'currency' | 'percent' | 'date' | 'datetime' | 'time' | 'boolean' | 'toggle' | 'select' | 'multiselect' | 'radio' | 'checkboxes' | 'lookup' | 'master_detail' | 'tree' | 'user' | 'image' | 'file' | 'avatar' | 'video' | 'audio' | 'formula' | 'summary' | 'autonumber' | 'composite' | 'repeater' | 'record' | 'location' | 'address' | 'code' | 'json' | 'color' | 'rating' | 'slider' | 'signature' | 'qrcode' | 'progress' | 'tags' | 'vector'>; description?: string; … } |
✅ | Full field definition to add |
A versioned set of atomic schema migration operations
| Property | Type | Required | Description |
|---|---|---|---|
| id | string |
✅ | Unique identifier for this change set |
| name | string |
✅ | Human readable name for the migration |
| description | string |
optional | Detailed description of what this migration does |
| author | string |
optional | Author who created this migration |
| createdAt | string |
optional | ISO 8601 timestamp when the migration was created |
| dependencies | { migrationId: string; package?: string }[] |
optional | Migrations that must run before this one |
| operations | { type: 'add_field'; objectName: string; fieldName: string; field: object } | { type: 'modify_field'; objectName: string; fieldName: string; changes: Record<string, any> } | { type: 'remove_field'; objectName: string; fieldName: string } | { type: 'create_object'; object: object } | { type: 'rename_object'; oldName: string; newName: string } | { type: 'delete_object'; objectName: string } | { type: 'execute_sql'; sql: string; description?: string }[] |
✅ | Ordered list of atomic migration operations |
| rollback | { type: 'add_field'; objectName: string; fieldName: string; field: object } | { type: 'modify_field'; objectName: string; fieldName: string; changes: Record<string, any> } | { type: 'remove_field'; objectName: string; fieldName: string } | { type: 'create_object'; object: object } | { type: 'rename_object'; oldName: string; newName: string } | { type: 'delete_object'; objectName: string } | { type: 'execute_sql'; sql: string; description?: string }[] |
optional | Operations to reverse this migration |
Create a new object
| Property | Type | Required | Description |
|---|---|---|---|
| type | 'create_object' |
✅ | |
| object | { name: string; label?: string; pluralLabel?: string; description?: string; … } |
✅ | Full object definition to create |
Deployment-level record that a data migration ran here and its self-check passed — the evidence gate consumers read instead of the platform version
| Property | Type | Required | Description |
|---|---|---|---|
| id | string |
✅ | Migration id (e.g. adr-0104-file-references) — one row per data migration |
| last_run_at | string |
✅ | When this migration last completed a gated (apply-mode) run on this deployment |
| verified_at | string | null |
optional | When the self-check last PASSED. Null/absent until it does — and cleared again by a later failing run, so a regression closes the gate |
| applied_at | string | null |
optional | When the backfill last ran in apply mode (writes enabled) |
| blocking | integer |
✅ | Blocking discrepancies reported by the last self-check. The gate requires 0 |
| advisory | integer |
optional | Advisory findings from the last run (external URLs, stale owners, …) — cost storage or need a modelling decision, never block the gate |
| details | string |
optional | JSON-encoded counts from the last run, for diagnostics |
Delete an existing object
| Property | Type | Required | Description |
|---|---|---|---|
| type | 'delete_object' |
✅ | |
| objectName | string |
✅ | Name of the object to delete |
Execute a raw SQL statement
| Property | Type | Required | Description |
|---|---|---|---|
| type | 'execute_sql' |
✅ | |
| sql | string |
✅ | Raw SQL statement to execute |
| description | string |
optional | Human-readable description of the SQL |
Dependency reference to another migration that must run first
| Property | Type | Required | Description |
|---|---|---|---|
| migrationId | string |
✅ | ID of the migration this depends on |
| package | string |
optional | Package that owns the dependency migration |
This schema accepts one of the following structures:
Add a new field to an existing object
Type: add_field
| Property | Type | Required | Description |
|---|---|---|---|
| type | 'add_field' |
✅ | |
| objectName | string |
✅ | Target object name |
| fieldName | string |
✅ | Name of the field to add |
| field | { name?: string; label?: string; type: Enum<'text' | 'textarea' | 'email' | 'url' | 'phone' | 'password' | 'secret' | 'markdown' | 'html' | 'richtext' | 'number' | 'currency' | 'percent' | 'date' | 'datetime' | 'time' | 'boolean' | 'toggle' | 'select' | 'multiselect' | 'radio' | 'checkboxes' | 'lookup' | 'master_detail' | 'tree' | 'user' | 'image' | 'file' | 'avatar' | 'video' | 'audio' | 'formula' | 'summary' | 'autonumber' | 'composite' | 'repeater' | 'record' | 'location' | 'address' | 'code' | 'json' | 'color' | 'rating' | 'slider' | 'signature' | 'qrcode' | 'progress' | 'tags' | 'vector'>; description?: string; … } |
✅ | Full field definition to add |
Modify properties of an existing field
Type: modify_field
| Property | Type | Required | Description |
|---|---|---|---|
| type | 'modify_field' |
✅ | |
| objectName | string |
✅ | Target object name |
| fieldName | string |
✅ | Name of the field to modify |
| changes | Record<string, any> |
✅ | Partial field definition updates |
Remove a field from an existing object
Type: remove_field
| Property | Type | Required | Description |
|---|---|---|---|
| type | 'remove_field' |
✅ | |
| objectName | string |
✅ | Target object name |
| fieldName | string |
✅ | Name of the field to remove |
Create a new object
Type: create_object
| Property | Type | Required | Description |
|---|---|---|---|
| type | 'create_object' |
✅ | |
| object | { name: string; label?: string; pluralLabel?: string; description?: string; … } |
✅ | Full object definition to create |
Rename an existing object
Type: rename_object
| Property | Type | Required | Description |
|---|---|---|---|
| type | 'rename_object' |
✅ | |
| oldName | string |
✅ | Current object name |
| newName | string |
✅ | New object name |
Delete an existing object
Type: delete_object
| Property | Type | Required | Description |
|---|---|---|---|
| type | 'delete_object' |
✅ | |
| objectName | string |
✅ | Name of the object to delete |
Execute a raw SQL statement
Type: execute_sql
| Property | Type | Required | Description |
|---|---|---|---|
| type | 'execute_sql' |
✅ | |
| sql | string |
✅ | Raw SQL statement to execute |
| description | string |
optional | Human-readable description of the SQL |
Modify properties of an existing field
| Property | Type | Required | Description |
|---|---|---|---|
| type | 'modify_field' |
✅ | |
| objectName | string |
✅ | Target object name |
| fieldName | string |
✅ | Name of the field to modify |
| changes | Record<string, any> |
✅ | Partial field definition updates |
Remove a field from an existing object
| Property | Type | Required | Description |
|---|---|---|---|
| type | 'remove_field' |
✅ | |
| objectName | string |
✅ | Target object name |
| fieldName | string |
✅ | Name of the field to remove |
Rename an existing object
| Property | Type | Required | Description |
|---|---|---|---|
| type | 'rename_object' |
✅ | |
| oldName | string |
✅ | Current object name |
| newName | string |
✅ | New object name |