Compute a dry-run diff between metadata and the live database.
const url = 'https://example.com/v1/plan';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"branch":null,"destructive":false,"vault":true}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://example.com/v1/plan \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "branch": null, "destructive": false, "vault": true }'Authorizations
Section titled “Authorizations”Request Body
Section titled “Request Body”Optional JSON body parameters for plan and apply endpoints.
Optional JSON body parameters for plan and apply endpoints.
object
Cart branch to plan against, in place of the server’s own
working-tree metadata directory (#929 D5). POST /plan only –
POST /apply rejects a present branch with 400 APPLY_BRANCH_NOT_ALLOWED.
Allow destructive operations (drop column, narrow type).
Vault data before destructive operations (default: true).
Responses
Section titled “ Responses ”Top-level result returned by plan(), apply(), plan_branch(), and
merge_branch_and_apply().
Top-level result returned by plan(), apply(), plan_branch(), and
merge_branch_and_apply().
object
Ordered list of individual changes with risk and SQL.
A new table is created with the given columns.
object
Whether this change executes, is skipped, or would make the whole
run refuse under this result’s options. See [Disposition].
This change runs as part of the result.
Includes every change when the result was computed with
destructive: true, and the managed Lookup->Junction FK drop even
under destructive: false – the strategy migration that scheduled
it already decided the drop is safe, independent of the destructive
gate.
This change does not run under this result’s options (a skippable
destructive kind – an orphan column, junction or schema drop –
needs destructive: true). It is still reported with its DDL and
risk so it can be reviewed; the result’s diagnostics carry a warning
for it. Only orphan drops are ever skipped.
This change’s presence is exactly what makes apply/publish
refuse the whole run: its risk is destructive, it is not
skipped, and it is not a managed drop, under destructive: false.
An unsafe column type narrowing (for example integer big ->
regular) and a type change the server cannot classify are both
refuse without destructive: true.
Only a plan (which never gates) can report one of these –
apply/publish abort before producing a result once any change
in the diff has this disposition.
The highest risk among this change’s DDL statements.
No lock escalation, instant operation.
Needs data transformation before completing (e.g. adding NOT NULL to populated column).
Acquires exclusive lock, may block on large tables.
Data loss possible (drop column, narrow type).
The DDL statements this change runs, in the order they execute. Views are rebuilt on every run and are listed under no change.
Column metadata in a plan change report.
object
Generated column mode ("virtual" or "stored"), if applicable.
SQL expression for a generated column, if applicable.
Column name (snake_case).
Whether the column has a NOT NULL constraint.
Whether this column is the table’s primary key.
Bare PostgreSQL type, never including modifiers.
Examples: "TEXT", "UUID", "BIGINT", "TIMESTAMPTZ", "VARCHAR(255)".
Modifier information is carried by the dedicated boolean fields (primary_key,
not_null, unique) — it never appears inline here.
Whether the column has a UNIQUE constraint.
A column is added to an existing table.
object
Whether this change executes, is skipped, or would make the whole
run refuse under this result’s options. See [Disposition].
This change runs as part of the result.
Includes every change when the result was computed with
destructive: true, and the managed Lookup->Junction FK drop even
under destructive: false – the strategy migration that scheduled
it already decided the drop is safe, independent of the destructive
gate.
This change does not run under this result’s options (a skippable
destructive kind – an orphan column, junction or schema drop –
needs destructive: true). It is still reported with its DDL and
risk so it can be reviewed; the result’s diagnostics carry a warning
for it. Only orphan drops are ever skipped.
This change’s presence is exactly what makes apply/publish
refuse the whole run: its risk is destructive, it is not
skipped, and it is not a managed drop, under destructive: false.
An unsafe column type narrowing (for example integer big ->
regular) and a type change the server cannot classify are both
refuse without destructive: true.
Only a plan (which never gates) can report one of these –
apply/publish abort before producing a result once any change
in the diff has this disposition.
The highest risk among this change’s DDL statements.
No lock escalation, instant operation.
Needs data transformation before completing (e.g. adding NOT NULL to populated column).
Acquires exclusive lock, may block on large tables.
Data loss possible (drop column, narrow type).
The DDL statements this change runs, in the order they execute. Views are rebuilt on every run and are listed under no change.
Column metadata in a plan change report.
object
Generated column mode ("virtual" or "stored"), if applicable.
SQL expression for a generated column, if applicable.
Column name (snake_case).
Whether the column has a NOT NULL constraint.
Whether this column is the table’s primary key.
Bare PostgreSQL type, never including modifiers.
Examples: "TEXT", "UUID", "BIGINT", "TIMESTAMPTZ", "VARCHAR(255)".
Modifier information is carried by the dedicated boolean fields (primary_key,
not_null, unique) — it never appears inline here.
Whether the column has a UNIQUE constraint.
A column is dropped and re-added with a new definition (type change).
object
Whether this change executes, is skipped, or would make the whole
run refuse under this result’s options. See [Disposition].
This change runs as part of the result.
Includes every change when the result was computed with
destructive: true, and the managed Lookup->Junction FK drop even
under destructive: false – the strategy migration that scheduled
it already decided the drop is safe, independent of the destructive
gate.
This change does not run under this result’s options (a skippable
destructive kind – an orphan column, junction or schema drop –
needs destructive: true). It is still reported with its DDL and
risk so it can be reviewed; the result’s diagnostics carry a warning
for it. Only orphan drops are ever skipped.
This change’s presence is exactly what makes apply/publish
refuse the whole run: its risk is destructive, it is not
skipped, and it is not a managed drop, under destructive: false.
An unsafe column type narrowing (for example integer big ->
regular) and a type change the server cannot classify are both
refuse without destructive: true.
Only a plan (which never gates) can report one of these –
apply/publish abort before producing a result once any change
in the diff has this disposition.
The highest risk among this change’s DDL statements.
No lock escalation, instant operation.
Needs data transformation before completing (e.g. adding NOT NULL to populated column).
Acquires exclusive lock, may block on large tables.
Data loss possible (drop column, narrow type).
The DDL statements this change runs, in the order they execute. Views are rebuilt on every run and are listed under no change.
Column metadata in a plan change report.
object
Generated column mode ("virtual" or "stored"), if applicable.
SQL expression for a generated column, if applicable.
Column name (snake_case).
Whether the column has a NOT NULL constraint.
Whether this column is the table’s primary key.
Bare PostgreSQL type, never including modifiers.
Examples: "TEXT", "UUID", "BIGINT", "TIMESTAMPTZ", "VARCHAR(255)".
Modifier information is carried by the dedicated boolean fields (primary_key,
not_null, unique) — it never appears inline here.
Whether the column has a UNIQUE constraint.
A generated column’s expression is updated.
object
Whether this change executes, is skipped, or would make the whole
run refuse under this result’s options. See [Disposition].
This change runs as part of the result.
Includes every change when the result was computed with
destructive: true, and the managed Lookup->Junction FK drop even
under destructive: false – the strategy migration that scheduled
it already decided the drop is safe, independent of the destructive
gate.
This change does not run under this result’s options (a skippable
destructive kind – an orphan column, junction or schema drop –
needs destructive: true). It is still reported with its DDL and
risk so it can be reviewed; the result’s diagnostics carry a warning
for it. Only orphan drops are ever skipped.
This change’s presence is exactly what makes apply/publish
refuse the whole run: its risk is destructive, it is not
skipped, and it is not a managed drop, under destructive: false.
An unsafe column type narrowing (for example integer big ->
regular) and a type change the server cannot classify are both
refuse without destructive: true.
Only a plan (which never gates) can report one of these –
apply/publish abort before producing a result once any change
in the diff has this disposition.
The highest risk among this change’s DDL statements.
No lock escalation, instant operation.
Needs data transformation before completing (e.g. adding NOT NULL to populated column).
Acquires exclusive lock, may block on large tables.
Data loss possible (drop column, narrow type).
The DDL statements this change runs, in the order they execute. Views are rebuilt on every run and are listed under no change.
Column metadata in a plan change report.
object
Generated column mode ("virtual" or "stored"), if applicable.
SQL expression for a generated column, if applicable.
Column name (snake_case).
Whether the column has a NOT NULL constraint.
Whether this column is the table’s primary key.
Bare PostgreSQL type, never including modifiers.
Examples: "TEXT", "UUID", "BIGINT", "TIMESTAMPTZ", "VARCHAR(255)".
Modifier information is carried by the dedicated boolean fields (primary_key,
not_null, unique) — it never appears inline here.
Whether the column has a UNIQUE constraint.
A new index is created on a table.
object
Whether this change executes, is skipped, or would make the whole
run refuse under this result’s options. See [Disposition].
This change runs as part of the result.
Includes every change when the result was computed with
destructive: true, and the managed Lookup->Junction FK drop even
under destructive: false – the strategy migration that scheduled
it already decided the drop is safe, independent of the destructive
gate.
This change does not run under this result’s options (a skippable
destructive kind – an orphan column, junction or schema drop –
needs destructive: true). It is still reported with its DDL and
risk so it can be reviewed; the result’s diagnostics carry a warning
for it. Only orphan drops are ever skipped.
This change’s presence is exactly what makes apply/publish
refuse the whole run: its risk is destructive, it is not
skipped, and it is not a managed drop, under destructive: false.
An unsafe column type narrowing (for example integer big ->
regular) and a type change the server cannot classify are both
refuse without destructive: true.
Only a plan (which never gates) can report one of these –
apply/publish abort before producing a result once any change
in the diff has this disposition.
The highest risk among this change’s DDL statements.
No lock escalation, instant operation.
Needs data transformation before completing (e.g. adding NOT NULL to populated column).
Acquires exclusive lock, may block on large tables.
Data loss possible (drop column, narrow type).
The DDL statements this change runs, in the order they execute. Views are rebuilt on every run and are listed under no change.
An existing index is dropped and recreated with a new definition.
object
Whether this change executes, is skipped, or would make the whole
run refuse under this result’s options. See [Disposition].
This change runs as part of the result.
Includes every change when the result was computed with
destructive: true, and the managed Lookup->Junction FK drop even
under destructive: false – the strategy migration that scheduled
it already decided the drop is safe, independent of the destructive
gate.
This change does not run under this result’s options (a skippable
destructive kind – an orphan column, junction or schema drop –
needs destructive: true). It is still reported with its DDL and
risk so it can be reviewed; the result’s diagnostics carry a warning
for it. Only orphan drops are ever skipped.
This change’s presence is exactly what makes apply/publish
refuse the whole run: its risk is destructive, it is not
skipped, and it is not a managed drop, under destructive: false.
An unsafe column type narrowing (for example integer big ->
regular) and a type change the server cannot classify are both
refuse without destructive: true.
Only a plan (which never gates) can report one of these –
apply/publish abort before producing a result once any change
in the diff has this disposition.
The highest risk among this change’s DDL statements.
No lock escalation, instant operation.
Needs data transformation before completing (e.g. adding NOT NULL to populated column).
Acquires exclusive lock, may block on large tables.
Data loss possible (drop column, narrow type).
The DDL statements this change runs, in the order they execute. Views are rebuilt on every run and are listed under no change.
An engine-created index that is no longer desired is dropped.
Emitted by orphan-index cleanup: an idx_* index on a desired table that
the compiler no longer produces (e.g. a junction’s uniquePair flipped to
false, or an indexed field was removed). Index drops lose no rows, so
they are not gated behind --destructive.
object
Whether this change executes, is skipped, or would make the whole
run refuse under this result’s options. See [Disposition].
This change runs as part of the result.
Includes every change when the result was computed with
destructive: true, and the managed Lookup->Junction FK drop even
under destructive: false – the strategy migration that scheduled
it already decided the drop is safe, independent of the destructive
gate.
This change does not run under this result’s options (a skippable
destructive kind – an orphan column, junction or schema drop –
needs destructive: true). It is still reported with its DDL and
risk so it can be reviewed; the result’s diagnostics carry a warning
for it. Only orphan drops are ever skipped.
This change’s presence is exactly what makes apply/publish
refuse the whole run: its risk is destructive, it is not
skipped, and it is not a managed drop, under destructive: false.
An unsafe column type narrowing (for example integer big ->
regular) and a type change the server cannot classify are both
refuse without destructive: true.
Only a plan (which never gates) can report one of these –
apply/publish abort before producing a result once any change
in the diff has this disposition.
The highest risk among this change’s DDL statements.
No lock escalation, instant operation.
Needs data transformation before completing (e.g. adding NOT NULL to populated column).
Acquires exclusive lock, may block on large tables.
Data loss possible (drop column, narrow type).
The DDL statements this change runs, in the order they execute. Views are rebuilt on every run and are listed under no change.
A trigger and its function are created on a table.
object
Whether this change executes, is skipped, or would make the whole
run refuse under this result’s options. See [Disposition].
This change runs as part of the result.
Includes every change when the result was computed with
destructive: true, and the managed Lookup->Junction FK drop even
under destructive: false – the strategy migration that scheduled
it already decided the drop is safe, independent of the destructive
gate.
This change does not run under this result’s options (a skippable
destructive kind – an orphan column, junction or schema drop –
needs destructive: true). It is still reported with its DDL and
risk so it can be reviewed; the result’s diagnostics carry a warning
for it. Only orphan drops are ever skipped.
This change’s presence is exactly what makes apply/publish
refuse the whole run: its risk is destructive, it is not
skipped, and it is not a managed drop, under destructive: false.
An unsafe column type narrowing (for example integer big ->
regular) and a type change the server cannot classify are both
refuse without destructive: true.
Only a plan (which never gates) can report one of these –
apply/publish abort before producing a result once any change
in the diff has this disposition.
The highest risk among this change’s DDL statements.
No lock escalation, instant operation.
Needs data transformation before completing (e.g. adding NOT NULL to populated column).
Acquires exclusive lock, may block on large tables.
Data loss possible (drop column, narrow type).
The DDL statements this change runs, in the order they execute. Views are rebuilt on every run and are listed under no change.
An engine-created trigger that is no longer desired is dropped.
Emitted by orphan-trigger cleanup: a trg_* trigger on a desired table
that the compiler no longer produces (e.g. a lookup field’s mutable
flipped false -> true, or a rollup field was removed). The backing
function is not dropped “along with” this trigger — it is dropped
separately, in a second pass, once no live trigger anywhere in the
schema and no trigger the plan still desires (see
[Change::DropFunction]) references it. A function can back triggers
on more than one table (rollup relocation, which keeps the same
hf_rollup_{parent}__{col} name regardless of which child table
produces it, is the live example; the seed-immutable name-truncation
collision at metadata::compiler::object ~443 was historically
another route to this, before #684’s fail-loud bound made that
collision unrepresentable), so dropping it in the same pass as the trigger
that orphaned it risks a PostgreSQL RESTRICT violation against a
still-live or still-desired sibling trigger. Trigger drops lose no
rows, so they are not gated behind --destructive (mirrors
[Change::DropIndex]).
object
Whether this change executes, is skipped, or would make the whole
run refuse under this result’s options. See [Disposition].
This change runs as part of the result.
Includes every change when the result was computed with
destructive: true, and the managed Lookup->Junction FK drop even
under destructive: false – the strategy migration that scheduled
it already decided the drop is safe, independent of the destructive
gate.
This change does not run under this result’s options (a skippable
destructive kind – an orphan column, junction or schema drop –
needs destructive: true). It is still reported with its DDL and
risk so it can be reviewed; the result’s diagnostics carry a warning
for it. Only orphan drops are ever skipped.
This change’s presence is exactly what makes apply/publish
refuse the whole run: its risk is destructive, it is not
skipped, and it is not a managed drop, under destructive: false.
An unsafe column type narrowing (for example integer big ->
regular) and a type change the server cannot classify are both
refuse without destructive: true.
Only a plan (which never gates) can report one of these –
apply/publish abort before producing a result once any change
in the diff has this disposition.
The highest risk among this change’s DDL statements.
No lock escalation, instant operation.
Needs data transformation before completing (e.g. adding NOT NULL to populated column).
Acquires exclusive lock, may block on large tables.
Data loss possible (drop column, narrow type).
The DDL statements this change runs, in the order they execute. Views are rebuilt on every run and are listed under no change.
An orphaned trigger’s backing function is dropped, in a second pass
separate from [Change::DropTrigger].
Emitted by drop_orphaned_trigger_functions (apply/diff.rs), once
per still-unreferenced function, after every table’s DropTriggers
(and every table’s CreateTriggers) have already been recorded for
this plan. “Unreferenced” means: no live trigger anywhere in the
schema still executes it, and the plan’s own desired schema does not
want a same-named function on some other table either — the second
half of that check matters because a function this plan is about to
(re)create on a different table is not yet live, and would otherwise
look safe to drop. Function drops lose no rows, so they are not gated
behind --destructive (mirrors [Change::DropIndex]).
object
Whether this change executes, is skipped, or would make the whole
run refuse under this result’s options. See [Disposition].
This change runs as part of the result.
Includes every change when the result was computed with
destructive: true, and the managed Lookup->Junction FK drop even
under destructive: false – the strategy migration that scheduled
it already decided the drop is safe, independent of the destructive
gate.
This change does not run under this result’s options (a skippable
destructive kind – an orphan column, junction or schema drop –
needs destructive: true). It is still reported with its DDL and
risk so it can be reviewed; the result’s diagnostics carry a warning
for it. Only orphan drops are ever skipped.
This change’s presence is exactly what makes apply/publish
refuse the whole run: its risk is destructive, it is not
skipped, and it is not a managed drop, under destructive: false.
An unsafe column type narrowing (for example integer big ->
regular) and a type change the server cannot classify are both
refuse without destructive: true.
Only a plan (which never gates) can report one of these –
apply/publish abort before producing a result once any change
in the diff has this disposition.
The highest risk among this change’s DDL statements.
No lock escalation, instant operation.
Needs data transformation before completing (e.g. adding NOT NULL to populated column).
Acquires exclusive lock, may block on large tables.
Data loss possible (drop column, narrow type).
The DDL statements this change runs, in the order they execute. Views are rebuilt on every run and are listed under no change.
A column is dropped from an existing table.
object
Whether this change executes, is skipped, or would make the whole
run refuse under this result’s options. See [Disposition].
This change runs as part of the result.
Includes every change when the result was computed with
destructive: true, and the managed Lookup->Junction FK drop even
under destructive: false – the strategy migration that scheduled
it already decided the drop is safe, independent of the destructive
gate.
This change does not run under this result’s options (a skippable
destructive kind – an orphan column, junction or schema drop –
needs destructive: true). It is still reported with its DDL and
risk so it can be reviewed; the result’s diagnostics carry a warning
for it. Only orphan drops are ever skipped.
This change’s presence is exactly what makes apply/publish
refuse the whole run: its risk is destructive, it is not
skipped, and it is not a managed drop, under destructive: false.
An unsafe column type narrowing (for example integer big ->
regular) and a type change the server cannot classify are both
refuse without destructive: true.
Only a plan (which never gates) can report one of these –
apply/publish abort before producing a result once any change
in the diff has this disposition.
The highest risk among this change’s DDL statements.
No lock escalation, instant operation.
Needs data transformation before completing (e.g. adding NOT NULL to populated column).
Acquires exclusive lock, may block on large tables.
Data loss possible (drop column, narrow type).
The DDL statements this change runs, in the order they execute. Views are rebuilt on every run and are listed under no change.
A column’s SQL type is changed.
object
Whether this change executes, is skipped, or would make the whole
run refuse under this result’s options. See [Disposition].
This change runs as part of the result.
Includes every change when the result was computed with
destructive: true, and the managed Lookup->Junction FK drop even
under destructive: false – the strategy migration that scheduled
it already decided the drop is safe, independent of the destructive
gate.
This change does not run under this result’s options (a skippable
destructive kind – an orphan column, junction or schema drop –
needs destructive: true). It is still reported with its DDL and
risk so it can be reviewed; the result’s diagnostics carry a warning
for it. Only orphan drops are ever skipped.
This change’s presence is exactly what makes apply/publish
refuse the whole run: its risk is destructive, it is not
skipped, and it is not a managed drop, under destructive: false.
An unsafe column type narrowing (for example integer big ->
regular) and a type change the server cannot classify are both
refuse without destructive: true.
Only a plan (which never gates) can report one of these –
apply/publish abort before producing a result once any change
in the diff has this disposition.
The highest risk among this change’s DDL statements.
No lock escalation, instant operation.
Needs data transformation before completing (e.g. adding NOT NULL to populated column).
Acquires exclusive lock, may block on large tables.
Data loss possible (drop column, narrow type).
The DDL statements this change runs, in the order they execute. Views are rebuilt on every run and are listed under no change.
A column gains a NOT NULL constraint.
object
Whether this change executes, is skipped, or would make the whole
run refuse under this result’s options. See [Disposition].
This change runs as part of the result.
Includes every change when the result was computed with
destructive: true, and the managed Lookup->Junction FK drop even
under destructive: false – the strategy migration that scheduled
it already decided the drop is safe, independent of the destructive
gate.
This change does not run under this result’s options (a skippable
destructive kind – an orphan column, junction or schema drop –
needs destructive: true). It is still reported with its DDL and
risk so it can be reviewed; the result’s diagnostics carry a warning
for it. Only orphan drops are ever skipped.
This change’s presence is exactly what makes apply/publish
refuse the whole run: its risk is destructive, it is not
skipped, and it is not a managed drop, under destructive: false.
An unsafe column type narrowing (for example integer big ->
regular) and a type change the server cannot classify are both
refuse without destructive: true.
Only a plan (which never gates) can report one of these –
apply/publish abort before producing a result once any change
in the diff has this disposition.
The highest risk among this change’s DDL statements.
No lock escalation, instant operation.
Needs data transformation before completing (e.g. adding NOT NULL to populated column).
Acquires exclusive lock, may block on large tables.
Data loss possible (drop column, narrow type).
The DDL statements this change runs, in the order they execute. Views are rebuilt on every run and are listed under no change.
A column’s NOT NULL constraint is removed.
object
Whether this change executes, is skipped, or would make the whole
run refuse under this result’s options. See [Disposition].
This change runs as part of the result.
Includes every change when the result was computed with
destructive: true, and the managed Lookup->Junction FK drop even
under destructive: false – the strategy migration that scheduled
it already decided the drop is safe, independent of the destructive
gate.
This change does not run under this result’s options (a skippable
destructive kind – an orphan column, junction or schema drop –
needs destructive: true). It is still reported with its DDL and
risk so it can be reviewed; the result’s diagnostics carry a warning
for it. Only orphan drops are ever skipped.
This change’s presence is exactly what makes apply/publish
refuse the whole run: its risk is destructive, it is not
skipped, and it is not a managed drop, under destructive: false.
An unsafe column type narrowing (for example integer big ->
regular) and a type change the server cannot classify are both
refuse without destructive: true.
Only a plan (which never gates) can report one of these –
apply/publish abort before producing a result once any change
in the diff has this disposition.
The highest risk among this change’s DDL statements.
No lock escalation, instant operation.
Needs data transformation before completing (e.g. adding NOT NULL to populated column).
Acquires exclusive lock, may block on large tables.
Data loss possible (drop column, narrow type).
The DDL statements this change runs, in the order they execute. Views are rebuilt on every run and are listed under no change.
A foreign key or unique constraint is changed on a column.
object
Whether this change executes, is skipped, or would make the whole
run refuse under this result’s options. See [Disposition].
This change runs as part of the result.
Includes every change when the result was computed with
destructive: true, and the managed Lookup->Junction FK drop even
under destructive: false – the strategy migration that scheduled
it already decided the drop is safe, independent of the destructive
gate.
This change does not run under this result’s options (a skippable
destructive kind – an orphan column, junction or schema drop –
needs destructive: true). It is still reported with its DDL and
risk so it can be reviewed; the result’s diagnostics carry a warning
for it. Only orphan drops are ever skipped.
This change’s presence is exactly what makes apply/publish
refuse the whole run: its risk is destructive, it is not
skipped, and it is not a managed drop, under destructive: false.
An unsafe column type narrowing (for example integer big ->
regular) and a type change the server cannot classify are both
refuse without destructive: true.
Only a plan (which never gates) can report one of these –
apply/publish abort before producing a result once any change
in the diff has this disposition.
The highest risk among this change’s DDL statements.
No lock escalation, instant operation.
Needs data transformation before completing (e.g. adding NOT NULL to populated column).
Acquires exclusive lock, may block on large tables.
Data loss possible (drop column, narrow type).
The DDL statements this change runs, in the order they execute. Views are rebuilt on every run and are listed under no change.
A semantic CHECK constraint was added to a column.
object
Whether this change executes, is skipped, or would make the whole
run refuse under this result’s options. See [Disposition].
This change runs as part of the result.
Includes every change when the result was computed with
destructive: true, and the managed Lookup->Junction FK drop even
under destructive: false – the strategy migration that scheduled
it already decided the drop is safe, independent of the destructive
gate.
This change does not run under this result’s options (a skippable
destructive kind – an orphan column, junction or schema drop –
needs destructive: true). It is still reported with its DDL and
risk so it can be reviewed; the result’s diagnostics carry a warning
for it. Only orphan drops are ever skipped.
This change’s presence is exactly what makes apply/publish
refuse the whole run: its risk is destructive, it is not
skipped, and it is not a managed drop, under destructive: false.
An unsafe column type narrowing (for example integer big ->
regular) and a type change the server cannot classify are both
refuse without destructive: true.
Only a plan (which never gates) can report one of these –
apply/publish abort before producing a result once any change
in the diff has this disposition.
The highest risk among this change’s DDL statements.
No lock escalation, instant operation.
Needs data transformation before completing (e.g. adding NOT NULL to populated column).
Acquires exclusive lock, may block on large tables.
Data loss possible (drop column, narrow type).
The DDL statements this change runs, in the order they execute. Views are rebuilt on every run and are listed under no change.
Column the constraint guards.
Constraint name.
schema.table physical key.
A semantic CHECK constraint was dropped from a column.
object
Whether this change executes, is skipped, or would make the whole
run refuse under this result’s options. See [Disposition].
This change runs as part of the result.
Includes every change when the result was computed with
destructive: true, and the managed Lookup->Junction FK drop even
under destructive: false – the strategy migration that scheduled
it already decided the drop is safe, independent of the destructive
gate.
This change does not run under this result’s options (a skippable
destructive kind – an orphan column, junction or schema drop –
needs destructive: true). It is still reported with its DDL and
risk so it can be reviewed; the result’s diagnostics carry a warning
for it. Only orphan drops are ever skipped.
This change’s presence is exactly what makes apply/publish
refuse the whole run: its risk is destructive, it is not
skipped, and it is not a managed drop, under destructive: false.
An unsafe column type narrowing (for example integer big ->
regular) and a type change the server cannot classify are both
refuse without destructive: true.
Only a plan (which never gates) can report one of these –
apply/publish abort before producing a result once any change
in the diff has this disposition.
The highest risk among this change’s DDL statements.
No lock escalation, instant operation.
Needs data transformation before completing (e.g. adding NOT NULL to populated column).
Acquires exclusive lock, may block on large tables.
Data loss possible (drop column, narrow type).
The DDL statements this change runs, in the order they execute. Views are rebuilt on every run and are listed under no change.
Column the constraint was on.
Constraint name.
schema.table physical key.
A unique: true flip on an already-applied column adds a real
UNIQUE constraint (#775).
Both directions of the unique flip are implemented: this variant is
false -> true; [Change::DropUniqueConstraint] is the inverse. See
the owner ruling recorded there (#775, 2026-08-05).
object
Whether this change executes, is skipped, or would make the whole
run refuse under this result’s options. See [Disposition].
This change runs as part of the result.
Includes every change when the result was computed with
destructive: true, and the managed Lookup->Junction FK drop even
under destructive: false – the strategy migration that scheduled
it already decided the drop is safe, independent of the destructive
gate.
This change does not run under this result’s options (a skippable
destructive kind – an orphan column, junction or schema drop –
needs destructive: true). It is still reported with its DDL and
risk so it can be reviewed; the result’s diagnostics carry a warning
for it. Only orphan drops are ever skipped.
This change’s presence is exactly what makes apply/publish
refuse the whole run: its risk is destructive, it is not
skipped, and it is not a managed drop, under destructive: false.
An unsafe column type narrowing (for example integer big ->
regular) and a type change the server cannot classify are both
refuse without destructive: true.
Only a plan (which never gates) can report one of these –
apply/publish abort before producing a result once any change
in the diff has this disposition.
The highest risk among this change’s DDL statements.
No lock escalation, instant operation.
Needs data transformation before completing (e.g. adding NOT NULL to populated column).
Acquires exclusive lock, may block on large tables.
Data loss possible (drop column, narrow type).
The DDL statements this change runs, in the order they execute. Views are rebuilt on every run and are listed under no change.
Column the constraint covers.
schema.table physical key.
A unique: true -> false flip on an already-applied column drops the
live UNIQUE constraint (#775).
Owner ruling (2026-08-05): this direction is a constraint
relaxation, not a destructive operation — false -> true either
succeeds or fails loud on duplicate values (see
[Change::AddUniqueConstraint]); the inverse can only ever loosen
what the database accepts, so it loses no rows. Relaxing a
restriction loses no rows; nothing to vault (#221 axis) — classified
MigrationRisk::Safe in apply/risk.rs, unlike DropColumn/
DropTable, which destroy data and are vault-gated.
object
Whether this change executes, is skipped, or would make the whole
run refuse under this result’s options. See [Disposition].
This change runs as part of the result.
Includes every change when the result was computed with
destructive: true, and the managed Lookup->Junction FK drop even
under destructive: false – the strategy migration that scheduled
it already decided the drop is safe, independent of the destructive
gate.
This change does not run under this result’s options (a skippable
destructive kind – an orphan column, junction or schema drop –
needs destructive: true). It is still reported with its DDL and
risk so it can be reviewed; the result’s diagnostics carry a warning
for it. Only orphan drops are ever skipped.
This change’s presence is exactly what makes apply/publish
refuse the whole run: its risk is destructive, it is not
skipped, and it is not a managed drop, under destructive: false.
An unsafe column type narrowing (for example integer big ->
regular) and a type change the server cannot classify are both
refuse without destructive: true.
Only a plan (which never gates) can report one of these –
apply/publish abort before producing a result once any change
in the diff has this disposition.
The highest risk among this change’s DDL statements.
No lock escalation, instant operation.
Needs data transformation before completing (e.g. adding NOT NULL to populated column).
Acquires exclusive lock, may block on large tables.
Data loss possible (drop column, narrow type).
The DDL statements this change runs, in the order they execute. Views are rebuilt on every run and are listed under no change.
Live constraint name that was dropped, echoed back verbatim from
introspection (CurrentColumn::unique).
schema.table physical key.
Row-level security is enabled on a table.
object
Whether this change executes, is skipped, or would make the whole
run refuse under this result’s options. See [Disposition].
This change runs as part of the result.
Includes every change when the result was computed with
destructive: true, and the managed Lookup->Junction FK drop even
under destructive: false – the strategy migration that scheduled
it already decided the drop is safe, independent of the destructive
gate.
This change does not run under this result’s options (a skippable
destructive kind – an orphan column, junction or schema drop –
needs destructive: true). It is still reported with its DDL and
risk so it can be reviewed; the result’s diagnostics carry a warning
for it. Only orphan drops are ever skipped.
This change’s presence is exactly what makes apply/publish
refuse the whole run: its risk is destructive, it is not
skipped, and it is not a managed drop, under destructive: false.
An unsafe column type narrowing (for example integer big ->
regular) and a type change the server cannot classify are both
refuse without destructive: true.
Only a plan (which never gates) can report one of these –
apply/publish abort before producing a result once any change
in the diff has this disposition.
The highest risk among this change’s DDL statements.
No lock escalation, instant operation.
Needs data transformation before completing (e.g. adding NOT NULL to populated column).
Acquires exclusive lock, may block on large tables.
Data loss possible (drop column, narrow type).
The DDL statements this change runs, in the order they execute. Views are rebuilt on every run and are listed under no change.
An RLS policy is created (or re-created) on a table.
object
Whether this change executes, is skipped, or would make the whole
run refuse under this result’s options. See [Disposition].
This change runs as part of the result.
Includes every change when the result was computed with
destructive: true, and the managed Lookup->Junction FK drop even
under destructive: false – the strategy migration that scheduled
it already decided the drop is safe, independent of the destructive
gate.
This change does not run under this result’s options (a skippable
destructive kind – an orphan column, junction or schema drop –
needs destructive: true). It is still reported with its DDL and
risk so it can be reviewed; the result’s diagnostics carry a warning
for it. Only orphan drops are ever skipped.
This change’s presence is exactly what makes apply/publish
refuse the whole run: its risk is destructive, it is not
skipped, and it is not a managed drop, under destructive: false.
An unsafe column type narrowing (for example integer big ->
regular) and a type change the server cannot classify are both
refuse without destructive: true.
Only a plan (which never gates) can report one of these –
apply/publish abort before producing a result once any change
in the diff has this disposition.
The highest risk among this change’s DDL statements.
No lock escalation, instant operation.
Needs data transformation before completing (e.g. adding NOT NULL to populated column).
Acquires exclusive lock, may block on large tables.
Data loss possible (drop column, narrow type).
The DDL statements this change runs, in the order they execute. Views are rebuilt on every run and are listed under no change.
A per-module schema is created (it did not exist yet).
object
Whether this change executes, is skipped, or would make the whole
run refuse under this result’s options. See [Disposition].
This change runs as part of the result.
Includes every change when the result was computed with
destructive: true, and the managed Lookup->Junction FK drop even
under destructive: false – the strategy migration that scheduled
it already decided the drop is safe, independent of the destructive
gate.
This change does not run under this result’s options (a skippable
destructive kind – an orphan column, junction or schema drop –
needs destructive: true). It is still reported with its DDL and
risk so it can be reviewed; the result’s diagnostics carry a warning
for it. Only orphan drops are ever skipped.
This change’s presence is exactly what makes apply/publish
refuse the whole run: its risk is destructive, it is not
skipped, and it is not a managed drop, under destructive: false.
An unsafe column type narrowing (for example integer big ->
regular) and a type change the server cannot classify are both
refuse without destructive: true.
Only a plan (which never gates) can report one of these –
apply/publish abort before producing a result once any change
in the diff has this disposition.
The highest risk among this change’s DDL statements.
No lock escalation, instant operation.
Needs data transformation before completing (e.g. adding NOT NULL to populated column).
Acquires exclusive lock, may block on large tables.
Data loss possible (drop column, narrow type).
The DDL statements this change runs, in the order they execute. Views are rebuilt on every run and are listed under no change.
An orphaned module schema is dropped (--destructive).
object
Whether this change executes, is skipped, or would make the whole
run refuse under this result’s options. See [Disposition].
This change runs as part of the result.
Includes every change when the result was computed with
destructive: true, and the managed Lookup->Junction FK drop even
under destructive: false – the strategy migration that scheduled
it already decided the drop is safe, independent of the destructive
gate.
This change does not run under this result’s options (a skippable
destructive kind – an orphan column, junction or schema drop –
needs destructive: true). It is still reported with its DDL and
risk so it can be reviewed; the result’s diagnostics carry a warning
for it. Only orphan drops are ever skipped.
This change’s presence is exactly what makes apply/publish
refuse the whole run: its risk is destructive, it is not
skipped, and it is not a managed drop, under destructive: false.
An unsafe column type narrowing (for example integer big ->
regular) and a type change the server cannot classify are both
refuse without destructive: true.
Only a plan (which never gates) can report one of these –
apply/publish abort before producing a result once any change
in the diff has this disposition.
The highest risk among this change’s DDL statements.
No lock escalation, instant operation.
Needs data transformation before completing (e.g. adding NOT NULL to populated column).
Acquires exclusive lock, may block on large tables.
Data loss possible (drop column, narrow type).
The DDL statements this change runs, in the order they execute. Views are rebuilt on every run and are listed under no change.
A junction table is renamed because its objectKey changed while its
owning relationship key is stable. All rows are preserved (OID-preserving
ALTER TABLE … RENAME TO).
object
Whether this change executes, is skipped, or would make the whole
run refuse under this result’s options. See [Disposition].
This change runs as part of the result.
Includes every change when the result was computed with
destructive: true, and the managed Lookup->Junction FK drop even
under destructive: false – the strategy migration that scheduled
it already decided the drop is safe, independent of the destructive
gate.
This change does not run under this result’s options (a skippable
destructive kind – an orphan column, junction or schema drop –
needs destructive: true). It is still reported with its DDL and
risk so it can be reviewed; the result’s diagnostics carry a warning
for it. Only orphan drops are ever skipped.
This change’s presence is exactly what makes apply/publish
refuse the whole run: its risk is destructive, it is not
skipped, and it is not a managed drop, under destructive: false.
An unsafe column type narrowing (for example integer big ->
regular) and a type change the server cannot classify are both
refuse without destructive: true.
Only a plan (which never gates) can report one of these –
apply/publish abort before producing a result once any change
in the diff has this disposition.
The highest risk among this change’s DDL statements.
No lock escalation, instant operation.
Needs data transformation before completing (e.g. adding NOT NULL to populated column).
Acquires exclusive lock, may block on large tables.
Data loss possible (drop column, narrow type).
The DDL statements this change runs, in the order they execute. Views are rebuilt on every run and are listed under no change.
An orphaned junction table is dropped because its owning relationship was
removed entirely. Destructive — requires --destructive and is vaulted
before execution.
object
Whether this change executes, is skipped, or would make the whole
run refuse under this result’s options. See [Disposition].
This change runs as part of the result.
Includes every change when the result was computed with
destructive: true, and the managed Lookup->Junction FK drop even
under destructive: false – the strategy migration that scheduled
it already decided the drop is safe, independent of the destructive
gate.
This change does not run under this result’s options (a skippable
destructive kind – an orphan column, junction or schema drop –
needs destructive: true). It is still reported with its DDL and
risk so it can be reviewed; the result’s diagnostics carry a warning
for it. Only orphan drops are ever skipped.
This change’s presence is exactly what makes apply/publish
refuse the whole run: its risk is destructive, it is not
skipped, and it is not a managed drop, under destructive: false.
An unsafe column type narrowing (for example integer big ->
regular) and a type change the server cannot classify are both
refuse without destructive: true.
Only a plan (which never gates) can report one of these –
apply/publish abort before producing a result once any change
in the diff has this disposition.
The highest risk among this change’s DDL statements.
No lock escalation, instant operation.
Needs data transformation before completing (e.g. adding NOT NULL to populated column).
Acquires exclusive lock, may block on large tables.
Data loss possible (drop column, narrow type).
The DDL statements this change runs, in the order they execute. Views are rebuilt on every run and are listed under no change.
Non-fatal warnings emitted during compilation or diffing.
A diagnostic message emitted during metadata compilation.
object
Human-readable description of the issue.
Source location, e.g. “core_Contact.account”
The [DiffOptions] this result was computed with, echoed back so
the result is self-describing.
object
When true, destructive operations (drop column, narrow type) produce DDL. When false (default), they produce diagnostics only.
When true (default), vault data before destructive operations.
Which tree (and, when knowable, which commit) this result was computed against.
object
The server’s own working-tree metadata directory (state.metadata_path).
object
refs/heads/main’s HEAD commit, or None when metadata_path
is not inside a git checkout (a bare hf apply against a
filesystem path with no .git at all is a normal, non-erroring
case – see crate::git::main_commit_for_metadata_path).
A cart branch, planned or published through an ephemeral worktree.
object
The cart branch name, exactly as supplied (may contain /).
The branch’s refs/heads/<branch> HEAD commit at the time this
result was computed.
Aggregate change counts.
object
Number of columns added to existing tables.
Number of columns whose definition changed (replace, generated expression).
Number of columns dropped.
Number of columns whose SQL type changed.
Number of constraint changes (foreign key, unique).
Number of new indexes created.
Number of existing indexes replaced.
Number of new tables created.
Number of junction tables dropped (--destructive only).
Number of junction tables renamed (OID-preserving; rows preserved).
Number of tables/columns that required no changes.
Example
{ "changes": [ { "disposition": "execute", "risk": "safe", "action": "createTable" } ], "diagnostics": [ { "severity": "info" } ], "source": { "kind": "main" }, "status": "planned"}default
Section titled “default”Structured error envelope (SPEC-DATA-0006): {"errors": [{"domain", "scope", "target", "code", "message"}, ...]}. domain (added #950 D9) names the subsystem that produced the entry (data, metadata, apply, git, auth, iam, hfx, or api for a transport-level failure with no domain error behind it) and is always present.
The top-level response body. Wraps a non-empty list of [ErrorEntry].
object
The error entries making up the envelope. Always non-empty.
One entry in the error envelope.
Carries a [Domain] naming the subsystem that produced it, an anchoring
[ErrorScope], an optional target that disambiguates within the
scope, a stable SCREAMING_SNAKE_CASE code, and a human-readable
English message. See SPEC-DATA-0006 for the full envelope contract.
object
Machine-readable, stable, SCREAMING_SNAKE_CASE identifier.
Which subsystem produced this entry. Always serialized.
data::error::DataError – record CRUD, validation, queries.
server::routes::metadata::error::MetadataError – the metadata
write API (module/object/field/screen PUT and POST).
server::apply::ApplyError – the metadata apply/plan pipeline.
server::git::GitError – the git-backed metadata repository.
auth::error::AuthError – JWT and credential authentication.
server::routes::iam::error::IamError – principal and credential
management (IAM handlers still emit Domain::Data for genuine
request-body field validation, which stays DataError).
server::routes::hfx::error::HfxError – HFX parse/emit.
Module packages (.hfmod, #1165): package format, signature and
compatibility refusals, and the install lifecycle that consumes them.
A transport-level ApiError built directly (forbidden,
not_found, internal, service_unavailable), with no
<Domain>Error behind it.
HFX source location for an HFX_PARSE entry.
Emitted as the extra location field on the entry JSON when present.
The line/column are 1-based. The frame is the offending source line
extracted verbatim from the input.
object
1-based column within the line, measured in UTF-16 code units of the prefix so browser-side editors can consume it directly.
The offending source line text, when available.
1-based line number within the source text.
Server-authored English message. Not part of the API contract; may change across releases without notice.
The key of the validation rule that failed, when the code is
RULE_FAILED. None for all other codes. Skipped during
serialization when absent so it does not appear on unrelated
entries (same pattern as location).
Field key for field scope, record id for record scope, null
otherwise. Always serialized, even when null.
Example
{ "errors": [ { "domain": "data", "scope": "field" } ]}