Repository navigation
Releases: jokull/onwardpg
Release list
v0.1.0-preview.15
Fixed
- An expression whose catalog nests an AND or OR chain no longer reads as
changed against a catalog that holds the same chain flat.BETWEENkeeps
its own AND node, soCHECK (x BETWEEN 1 AND 5 AND y > 0)deparses as
(((x >= 1) AND (x <= 5)) AND (y > 0)). A dump of that catalog restores
the text flat, anddrift checkandcontract checkagainst the restored
copy reported the constraint aschanged_in_actual. Inspection now records
table CHECK constraints, partial index predicates, index expressions,
column defaults, and generated columns in the flat form, which is what
PostgreSQL deparses for the flat expression, so the two catalogs compare and
fingerprint equal. Domains, policies, views, and triggers were already read
flat. A schema with such an expression has a new fingerprint, so a pending
bundle planned over it before this release no longer matches its own
receipts: runonwardpg planandonwardpg verifyfor it again. answers.jsonanddecisions.jsonlist answers by question id (kind and
key). Before, they kept the order in which the answers were given, so a
bundle planned with an incremental hint loop had a different entry digest
than the same bundle planned once with the final hint set, and a second
plan rewrote it. One plan of a given input now gives one entry digest. A
bundle written by an earlier release keeps its digests until it is planned
again.
v0.1.0-preview.14
Fixed
- An ignored object now masks the unsupported state of itself and of the
objects in it, in every command that takes ignore selectors. Before,
contract check --ignore schema:legacystill answeredunsupportedwith
acl:schema:legacywhen the schema was granted topg_read_all_data:
the selector removed the schema and its objects, but not their ACLs.
schema:NAMEnow masks the schema's ACL, ownership, andIN SCHEMA
default ACLs, and the ACL, ownership, storage, and comment state of each
table, column, index, sequence, type, and routine in it.table:,
column:,index:,constraint:,sequence:,routine:, type, and
extension:selectors mask the state of their own object, andtable:
also that of its columns and indexes. Unsupported state of a kept object,
and cluster or database state such as a default ACL for every schema or a
parameter grant, still blocks. A masked marker has no ignore receipt of its
own, so a catalog that a checkpoint accepted keeps its fingerprint.
contract checklists the masked markers after the masked objects in
ignored.expected_maskedandignored.actual_masked. - A new NOT NULL column without a default no longer promotes its dependents
to contract. Expand adds the column as nullable and contract sets it NOT
NULL. That contract work made the column a contract provider, so each
UNIQUE constraint, foreign key, CHECK, index, and view over it moved to
contract withdependency_phase_promotion. A promoted UNIQUE or foreign key
has no gate, so the plan becameunsupportedwith
contract_enforcement_missing_gate. A newquote.company NOT NULLin two
UNIQUE constraints and in a composite foreign key frompayment_duedid
this. preview.12 exempted a column modification of this kind; a new column
now follows the same rule. A new primary key over the column is contract
work of its own and still followsSET NOT NULL. - A sequence, type, or routine ACL that holds exactly the default privileges,
asGRANTthenREVOKEleaves it, is no longer unsupported state. It means
what the NULL ACL means. Schema and table ACLs already compared this way.
The items are compared as sets, and an object withpg_init_privs, a
former extension member, keeps its explicit ACL.
v0.1.0-preview.13
Added
onwardpg contract check --ignore SELECTORtakes the selectors of
drift check --ignoreand applies them to both sides of the checkpoint
comparison. A cutover database can hold objects that are not drift for the
bundle: an archive schema that the operator created in the window, a column
of another pending change, or foreign keys that a restore could not create.
Before, the check answeredcatalog_driftand could not become ready. The
checkpoint is a fingerprint, so with--ignorethe command replays the
history through the bundle's expand in the target's scratch database, as
verifydoes when it receipts the checkpoint, proves that the replay
reproduces the receipted fingerprint (expand_checkpoint_not_reproduced
otherwise), and compares both sides with the selectors applied. A selector
that matches nothing isinvalid_ignore. The report addsignored: each
selector with the receipts it matched on each side, every masked object on
each side, and the compared fingerprints; a ready report keeps it. When the
catalogs still differ,differenceslists the objects asdrift check
does. Without--ignorethe command is unchanged.
Changed
schema:NAMEnow ignores the schema and every object in it, in every
command that takes ignore selectors. Before, it removed only the schema
node, so the tables of an ignored schema were still compared. A foreign key
from a kept table into the ignored schema is an error, as one into an
ignored table already was, so an ignored object cannot hide drift in a kept
one. Unsupported-state markers in the schema are not acknowledged by it.- When two given selectors match the same object, such as
schema:NAMEand
a selector of an object in that schema, ortable:*and one table, both
are used. Before, only the first of them was marked used, and the other was
reported as an unused selector. drift checkandcontract checkreport an ignore-selector error from the
live inspection or the scratch replay, such as a foreign key into an
ignored table, asinvalid_ignore.
v0.1.0-preview.12
Fixed
- A column whose only contract work is
ALTER COLUMN ... DROP DEFAULTor
SET NOT NULLno longer promotes its dependents to contract. Before, every
change with a contract statement promoted each desired dependent with the
dependency_phase_promotionhazard. OneDROP DEFAULTonquote.status
moved its CHECK change, an index, and 11 views into contract, so the
window between expand and contract ran without them. A promoted CHECK
change could also lose its validation gate and make the plan unsupported.
No CHECK, index, or view reads a column's default or nullability, so these
dependents now keep their own phase. A new primary key or
REPLICA IDENTITY USING INDEXstill followsSET NOT NULL: both are
contract work of their own. A type, collation, identity, serial, or
generated-expression change still promotes its dependents, and so does a
domain default change, becauseADD COLUMNover the domain fills existing
rows with the domain default of that moment. A column that is itself
promoted still promotes its dependents.
v0.1.0-preview.11
Added
-
onwardpg contract check --bundle IDcan check a bundle that is not the
history head, for a cutover window that runs several bundles back to back
(expand 1, contract 1, expand 2, ...) behind a write barrier. The check is
ready only while the database matches that bundle's receipted post-expand
catalog, which exists only after every earlier contract and before any
later expand. Out-of-order states are refused with
predecessor_contract_pending,expand_not_applied,
contract_already_applied, orsuccessor_expand_applied. The report adds
history_head,chain_position, andchain_length. -
--evidenceaccepts a JSON array of writer-evidence objects, one per
bundle, so one file attests the write barrier for the whole pending chain
prefix. The check uses the object bound to the selected bundle and refuses
a file with none or with two. A ready report records the accepted
attestation underevidence: digest, release, observation window, and
cohorts. Build the file from the bundle manifests and check each contract
in chain order:jq -n \ --arg environment production --arg release cutover-2026-10-18 \ --arg observed_at 2026-10-18T06:00:00Z --arg expires_at 2026-10-18T10:00:00Z \ --slurpfile cohorts write-barrier-cohorts.json \ '[inputs | {target, environment: $environment, plan_id, bundle_entry_digest: .history.entry_digest, desired_fingerprint: .desired_source.fingerprint, generation, release: $release, observed_at: $observed_at, expires_at: $expires_at, cohorts: $cohorts[0]}]' \ migrations/onward/app/T1/manifest.json \ migrations/onward/app/T2/manifest.json > cutover-evidence.json onwardpg contract check --target app --bundle T1 \ --environment production --database-env PROD_READONLY_DATABASE_URL \ --evidence cutover-evidence.json
write-barrier-cohorts.jsonlists one cohort per required category (web,
workers,scheduled_jobs,queues,connection_pools,previews,
ad_hoc_writers), each with statusread_only,drained, orisolated,
source_kindmanual, and asourcethat names the barrier. The
observation window must not exceed 24 hours.
Fixed
- A table or column rename on PostgreSQL 18 no longer names the replay's NOT
NULL constraint. PostgreSQL appends a collision suffix (not_null1,
not_null3, ...) to a generated name past 63 bytes, and the suffix depends
on creation history, so production and onwardpg's replay can name the same
constraint differently. Before, the plan emittedALTER TABLE ... RENAME CONSTRAINT <replay name> TO ..., which failed on production. The plan now
finds the constraint by table and column inpg_constraintat apply time
and renames it only when its name differs from the target. - Planning the same inputs twice writes a byte-identical bundle. Before, the
rename_tablerejection reason named whichever mismatching child Go map
iteration reached first, soplan.jsonand the plan, result, and history
entry digests changed between runs while the phases stayed the same. The
reason now names the first mismatch in stable key order. The same fix
covers the order of replaced and validated domain-constraint statements,
theenum_rewrite_column_changeandderived_view_closure_object
reasons, and which contract gate names a draft edit requirement.
v0.1.0-preview.10
Fixed
- A table rename keeps the foreign keys that point at it. A catalog export
prints each such foreign key with the newREFERENCEStarget and the
referenced key index under its derived new name (users_pkey->
accounts_pkey). Before, every inbound foreign key became a drop,ADD ... NOT VALID, andVALIDATE, and a self-referencing foreign key made the
table lose its rename candidacy (child_semantics_changed). Any other
foreign-key change is still planned. - A column rename keeps the foreign keys on either end. The owning table's
renames rewrite the local column list and theON DELETE SET NULL/DEFAULT
list; the referenced table's renames rewrite only theREFERENCEScolumn
list. Before, each such foreign key became a reconcile question and a drop
and re-add. - A CHECK, index, or foreign key that names several renamed columns is judged
against every rename of the plan in one pass. Before, it never matched and
was rebuilt (a CHECK throughNOT VALIDandVALIDATE). - A column rename carries an index that names the column in its
WHERE
predicate, a key expression, orINCLUDE. Before, such an index was dropped
in expand and rebuilt in contract, so a partial UNIQUE index enforced
nothing between the two phases. Predicate and expression columns are
matched by whole identifier tokens; a token in a function or type position
is never rewritten, solower(lower)->upper(upper)is still an index
change. - A table rename ignores declared column order. A column that a later
migration appended but the schema declares mid-table no longer costs the
table its rename candidacy (child_semantics_changed:column:...).
v0.1.0-preview.9
Added
-
rename_backfill_strategyoffersdirect_rename: one native
ALTER TABLE ... RENAME COLUMNin expand, with no overlap bridge. The
statement carries thesingle_deployment_cutoverhazard, because the
previous release stops working the moment it runs. When existing triggers,
defaults, generated values, partitions, or row security withhold the
generated bridge, the question offers onlydirect_renameandsplit_plan
and names the reason. Before, such a table made the whole plan
unsupportedas soon as one rename was confirmed.direct_renamerenders
the name change and nothing else, so it is withheld when more than the
name differs or a dependent view changes, and a partition child's column
is renamed by its parent's statement. -
A new UNIQUE constraint on an existing table whose columns contain the
columns of a key that PostgreSQL already enforces there (the primary key,
or a unique key with the same NULL treatment) is catalog-proven: no
reconcile_contractquestion, expand phase, hazard
unique_implied_by_existing_key:<key>. A foreign key from a new table that
references it stays in expand. Before, the key took the contract path and
that foreign key stopped the plan withcontract_enforcement_missing_gate. -
When the generated plan does not converge after contract, the checkpoint
error names the objects whose definition or dependencies differ from the
desired schema, instead of two fingerprints.
Fixed
- A column rename answer on a table that cannot take the overlap bridge no
longer stops the planner before the other rename answers are read. With
several renames on one table,plan --hints-filefailed with
unused answersfor every rename after the first, and a single--hint
stopped asunsupported; no sequence of answers could complete the plan. - The workflow report lists every planner blocker as a finding (code = the
blocker family, for examplesingle_deployment_column_bridge_required).
Before, onlycolumn_physical_orderwas shown, so an unsupported plan
could show nothing but informational analysis. cross_type_column_identityanalysis no longer lists a drop/create pair
once either column has a confirmed rename. A table with ten renames and
seventeen new columns reported 92 pairs, including pairs for columns the
hints had already decided.- Dependents of a
direct_renamecolumn (indexes, keys) are no longer
promoted to contract: the desired name exists after expand. - Resolving a confirmed rename of an indexed column rewrote the index parts
of the current snapshot in place. Every later build in the same process
then computed a different current fingerprint and rejected the receipted
answers as stale.
v0.1.0-preview.8
Fixed
drift check,plan, anddraftwork with a history that holds
non-transactional batches. A bundle fromplan --concurrent-indexeshas
CREATE INDEX CONCURRENTLYorDROP INDEX CONCURRENTLYin a
non-transactional batch.verifyran such a bundle correctly, but
drift check, and the base history replay ofplananddraft, sent the
complete history to PostgreSQL as one query. PostgreSQL runs such a query in
one implicit transaction, and the command stopped withCREATE INDEX CONCURRENTLY cannot run inside a transaction block (SQLSTATE 25001). After
such a bundle was accepted, no drift check and no new plan was possible.
There is now one history replay, the one thatverifyuses. It runs each
batch in the mode that the bundle declares, splits an edited phase at its
batch directives, runs the checks that verification runs, and uses one
connection for the complete history, so session state such assearch_path
from a baseline stays set. Bundle formats and digests are unchanged, and no
bundle has to be planned again.- A bundle with more than one statement that cannot run in a transaction
block stays verifiable after its phase file is edited. The generator wrote
one-- onwardpg:batch nontransactionalline for a batch that could hold
several such statements, for example twoCREATE INDEX CONCURRENTLYfrom
concurrent_indexes = true. An edited phase is split only at those lines
and each chunk is one query, so after any edit of the file, even a comment,
verifyfailed withnon_transactional_batch_failedand SQLSTATE25001.
The planner now makes each such statement a batch of its own, so the phase
file has one directive for each.verify,drift check, and the base
replay ofplananddraftthen run an edited phase statement by
statement. drift checkreads the live catalog before it replays history. A role that
the observer guard refuses, or a wrong URL, now stops the command in about a
second. Before, each such attempt cost a full history replay first.drift checkno longer requires access to application objects. It reads
system catalogs only, which every PostgreSQL role can read, and a role
withoutUSAGEorSELECTon anything reads the same graph as the database
owner (proved by test on PostgreSQL 15 to 18). The check that stopped the
command withdrift_observer_access_incomplete, and that listed objects
that--ignoreexcluded, such as the relations of a provider schema, is
removed fromdrift checkanddev plan.contract checkkeeps it,
because its data gates read rows.- The concurrent mode no longer emits
CREATE INDEX CONCURRENTLYorDROP INDEX CONCURRENTLYfor an index of a partitioned table. PostgreSQL rejects
both, so such a plan could not be verified. A new non-unique index is built
with anON ONLYparent, a concurrent build for each leaf partition, and an
attach. A unique index and every drop use the plain statement, with the
hazardpartitioned_index_not_concurrent.
Added
- A login role that only inherits predefined read-only roles is a valid
observer fordrift checkandcontract check:pg_read_all_data,
pg_monitor,pg_read_all_settings,pg_read_all_stats, and
pg_stat_scan_tables. This is the role that the role tool of a managed
provider can create, for examplepscale role create DATABASE BRANCH NAME --inherited-roles pg_read_all_data. Before, such a role stopped with
observer_role_elevated, and only the database owner or a role with direct
grants on every relation passed. The result reports
observer.mode: predefined_read_role. The role must still have none of
SUPERUSER,CREATEDB,CREATEROLE, andREPLICATION. A membership in any
other predefined role (for examplepg_write_all_data), in a role that can
log in, or withADMIN OPTIONis still refused. See the
observer role. - An observer login role can have
BYPASSRLS, reported as
observer.bypass_rls: true. Row-level security with no policy for a reader
hides every row from apg_read_all_datarole with no error, so a team
needsBYPASSRLSon its read-only role. Before,BYPASSRLSstopped the
command withobserver_role_elevated. - The error of
drift checkand the findings ofcontract checkfor a
refused or incomplete observer carrynext_actionswith the SQL for the two
valid forms of role. verifyexplains the failure of a non-transactional batch with SQLSTATE
25001: PostgreSQL runs SQL that holds more than one statement in one
implicit transaction, so each statement that cannot run in a transaction
block needs its own-- onwardpg:batch nontransactionalline in an edited
phase.
Changed
- The observer guard now proves that a role that is not the database owner is
read-only. It reads the effective privileges of the role and of each role
that it is a member of, and stops withobserver_access_policy_unsafewhen
one of them can write to a relation or a sequence, hasCREATEon the
database or on a schema, or owns an object. A grant toPUBLICcounts.
Before, the guard read only the schema grants and the table grants of the
observer's own roles, so a write privilege throughPUBLIC(for example
CREATEonpublicin a database made before PostgreSQL 15) or on a
sequence passed. An observer that was accepted with such a privilege is now
refused; revoke the privilege or use the database owner. - A history replay for
drift check,plan, ordraftnow stops when the
scratch server is not the PostgreSQL major of the history receipts, as
verifydoes. It also runs the manual verification queries and the
verify.sqlassertions of each bundle, which already passed when the bundle
was verified. - The timing stage for these replays is
history_replay;ddl_executenow
measures only the load of exported DDL. - A bundle keeps its index lock mode. The mode was stored in the manifest
(planner.options.concurrent_indexes) but never read: a bundle planned with
--concurrent-indexeswent back to plainCREATE INDEXandDROP INDEXin
transactional batches on the next plainonwardpg plan, with no message.
plananddraftnow use the flag of the run, then the stored choice of
the bundle, then the target configuration, then the blocking default.
--concurrent-indexes=falseselects the blocking mode. - New optional target key
concurrent_indexes = truein.onwardpg.toml.
plan,draft, anddev planthen use the concurrent mode without the
flag.initdoes not read the key.config checkreports the key. plananddraftreportindex_lock_mode(concurrent_indexes,source,
and, after a change,previous_concurrent_indexes). Two new warning codes:
index_lock_mode_changedwhen a run writes a bundle in another mode than
its previous generation, andindex_lock_mode_differs_from_configwhen a
bundle stays blocking although the target is configured as concurrent.- The removal of an index is no longer
data_loss. A standalone index that
enforces nothing is dropped without a decision hint, incontract, with
safety=reviewand the hazardsslower_queries_possibleand
blocking_lockorconcurrent_index_drop. Before, each one was
safety=dangerous,data_loss, and needed its owndrophint. - Every drop of a unique index or of a constraint keeps one decision. Its
choice hazard is now the enforcement that is removed (for a unique index:
unique_index_enforcement_removed,duplicate_rows_possible), not
data_loss. An index that is the replica identity of its table, or that the
table is clustered on, also keeps its decision. - A
drophint for an index that needs no decision is accepted
and reported as the findinghint_not_needed. next_actionshas asemantic_hints_fileaction when two or more decisions
have exactly one choice. It lists the completehintsarray for
--hints-file.- The help text of
--concurrent-indexesstates the whole effect: create and
drop, withCONCURRENTLY, in nontransactional batches.
Compatibility
- No bundle file has a new member. Bundles in accepted history verify with no
change to a byte:verifyreplays the stored SQL and does not plan it
again. - A new plan can have more batches than the same plan from preview.7: one for
each statement that cannot run in a transaction block. The statements and
their order are the same. The format ofplan.jsonand of the phase files
is unchanged, and a bundle from preview.7 with several such statements in
one batch still verifies with no change to a byte. If such a preview.7
bundle was edited and is planned again, the phase reconciliation can report
a conflict, because the generated text around the edit changed; resolve it
in the phase file. - A bundle that is planned again gets the new classification, so its
plan.json, phase SQL comments, and digests change. This is intended for a
bundle that is not yet in accepted history. Stored answers for index drops
that need no decision now are dropped from the bundle on that replan.
v0.1.0-preview.7
Changed
plan,draft,verify, andinitare about five times faster on a large
schema. On a schema of 271 tables, about 3,500 constraints and about 1,170
indexes, with a one-column change,planwent from 126–149 s to 23–26 s,
verifyfrom 114 s to 22 s, andverify --checkfrom 107 s to 20 s on the
same machine. Plans, phase SQL, and catalog fingerprints are byte-identical.
The changes:- The catalog reader no longer searches every snapshot object once for each
column, and a snapshot keeps its sorted object order between writes. These
two loops were quadratic and cost about 7 seconds for each catalog read of
that schema. - These commands run the export two times, not four: once at the start, and
once immediately before they write or report. Byte-identical output proves
that the export is deterministic and that it did not change while the
command worked, and the command does not load identical DDL into scratch
PostgreSQL a second time. Output that differs is handled as before: a
third run decides whether the export is nondeterministic, and a changed
export must have the same catalog fingerprint. A nondeterministic export
is therefore reported at the end of the command, not at the start. - A command that ends without a write (for example
unsupported,
no_changes, or a failed verification) now stops with an error if the
export changed while it worked. Before, it reported a result for the
export it read at the start. - One command no longer replays the same SQL more often than its checks
need. A verification still compares two separate executions in two
databases; they now run at the same time. A second verification in the
same command (expand checkpoint, then complete bundle) reuses an execution
of the first only when both run exactly the same SQL with the same checks.
planused seven scratch databases and five history replays; it now uses
four and three. - Base history replay runs during the first export.
- The catalog reader no longer searches every snapshot object once for each
dev,config check, anddiff --targetstill run the export twice back to
back.- onwardpg no longer checks what
schema_commandwrites to the checkout. The
earlier check read the bytes of every file outside.gitandnode_modules
before and after each export run, four times for each command, and stopped
withDDL export command modified repository inputswhen any file differed.
It is removed, with that error. A result depends on the DDL that the export
prints: the two export runs of a command must give the same bytes, and the
result is proved by replay on PostgreSQL. The removed check added one thing
to that, a stop when the command also wrote files, which does not make a
plan wrong. It cost 8 to 10 seconds for each command in a clean checkout of
50,000 files, and 96 seconds for each of the four walks in a checkout with
1.3 million paths and 24 GB of caches and build outputs. It also stopped the
command whenever another process (a build, a development server, another
agent) wrote a file during an export run. On the schema above, in
interleaved runs on one machine,planwent from 24–27 s to 16–19 s (27 s
in one run, whose two export runs took 17 s),verifyfrom 20–24 s to
15 s, andverify --checkfrom 27–31 s to 16–22 s. Other work can now run
in the same checkout while a command runs. The fingerprint was never
stored, so bundle formats and digests are unchanged. The
safety model
states what carries the guarantee and what is no longer caught.
Added
- A result document can have a
warningsmember, which never changes the
status or the exit code. The first warning code isexport_side_effects:
in a git work tree, a command that runsschema_commandcompares
git statusfrom before the first export run withgit statusafter the
last, and lists the paths whose status changed. Files that git ignores are
not listed, and without git there is no observation. See
warnings. ONWARDPG_TIMINGS=1writes one JSON line to standard error with the wall
time and run count of each stage of the command (export runs, scratch
databases, history replays, catalog reads, planner).
See performance.
v0.1.0-preview.6
Fixed
-
drift checkno longer hides live catalog state that makesinit,plan, and
diffstop as unsupported. The state (for example
ownership:extension:earthdistance=pscale_adminor
parameter_acl:session_replication_role) was in the inspected snapshot, but
the report dropped it and showed onlydrifted. The report now has an
unsupportedlist; when it is not empty the status isunsupportedand the
exit code is 3, and thedifferencesare still listed. A result without
unsupportedentries now means there is no modeled drift and nothing the
planner would refuse. The list includes unsupported state in the replayed
history as well as in the live catalog. -
contract checkreported unsupported production state ascatalog_drift
(statusblocked, exit 4), because that state is part of the observed
fingerprint. It now reports statusunsupported(exit 3), the selectors in
unsupported, and the findingunsupported_catalog_state, and runs no data
gate. Catalog drift is classified on the modeled graph alone, so a catalog
that also differs from the receipted checkpoint still gets its
catalog_drift,expand_not_applied, orcontract_already_appliedfinding. -
A foreign key whose only differences from the desired schema are its own
name and the name of the index behind the referenced primary-key or unique
constraint is now offered as arename_constraintdecision, in the same round as the key's own
rename. Before, such a foreign key was never a rename candidate, because the
index name that PostgreSQL records for the referenced key (UsingIndex) was
compared literally: after an ORM orpg_dumpround trip left old key names
(..._pk,idx_..._sqlite_autoindex_...), every foreign key referencing one
was planned as drop plus re-add, with a full re-validation on a production
table. The foreign key is a candidate only while its referenced key is itself
a rename candidate that has not been declined; every other catalog field is
still compared. A confirmed pair emits onlyALTER TABLE ... RENAME CONSTRAINT, which PostgreSQL carries out as a metadata change. A foreign
key that references a unique index without a constraint is not covered.
Added
- A target can list
live_ignoreselectors in.onwardpg.tomlfor state a
managed PostgreSQL provider owns in live clusters: an extension or a schema
owned by another role (ownership:extension:NAME=ROLE,
ownership:schema:NAME=ROLE) and apg_parameter_aclgrant
(parameter_acl:NAME). Those selectors can exist only in production, so they
could not be targetignoreentries and every live command needed one
--ignoreflag per selector.drift check,contract check, anddiff
(with the new--target NAME [--config FILE]) remove exactly the named
blocker markers from the live snapshot and list them as
observer.live_ignored(workspace_compatibilityfor adiffplan). Selectors are
exact, written as the report prints them withquote_ident-style
identifiers, validated for form and kind, and need not match in every
cluster. They never remove a typed object or add an ignore receipt.
They are removed before the comparison fingerprint is computed, so a catalog
that differs from the expected graph only by acknowledged state compares
equal (drift checkisdrift_free;contract checkmatches its
checkpoint) and the reported fingerprint depends on the list;
observer.observed_fingerprintreports the fingerprint before removal, in
drift checkandcontract checkresults when something was removed.
A configured selector that matched nothing is listed as
observer.live_ignore_unmatched(live_ignore_unmatched:SELECTORin adiff
plan'sworkspace_compatibility); that is information only and never changes
a status, exit code, or fingerprint, so a typo, or a keyword that PostgreSQL
quotes, does not pass unnoticed.--targetand--configbelong todiff
only: the legacyplan --from --tospelling does not accept them and never
applies the list.
Planning, verification, replayed history, and DDL sources ignore the list. The attribute ledger keeps these catalogs classified as blocked:
a parameter ACL or a foreign-owned schema can be a real privilege difference
between environments, so nothing is acknowledged without an exact entry, and a
changed owner or a new grant blocks again. diff --from-env ENVand--to-env ENVread a side's PostgreSQL URL from an
environment variable, likedrift check --database-env, so a live URL stays
out of process arguments. Each is mutually exclusive with--fromor--to
for the same side.
Changed
- The dependency audit of the documentation website runs in a scheduled
workflow, not in pull request CI or in the release workflow. The website is
not part of the binary.