Desired-state execution grows a library front door and learns to create the table it is converging toward — declaring a brand-new table on a fresh database now works end to end under the engine's proof discipline.
Highlights
Greenfield CREATE TABLE through the declarative front door
- A desired file whose table does not exist yet now converges — the engine proves the name is free (
CheckTableAbsent, read againstpg_classso a missing privilege can never masquerade as absence), proves the role holdsCREATEon the schema, then runs theCREATE TABLEand its index builds as brief bounded steps under the usuallock_timeout/statement_timeoutbudgets. A rerun converges to an empty plan. (#60, #62, #63) - Refusals are typed and land before anything runs — an occupied name (relation or standalone type) is the new
create-collisionreason; a privilege gap isinsufficient-privileges;PARTITION OF,INHERITS,LIKE,OF, andIF NOT EXISTSshapes refuse withunsupported-statement. (#63) - Plan order states execution order — desired-file statements are ordered at parse (the
CREATE TABLEfirst, indexes keeping input order after it) everywhere the file replays: the greenfield plan, the create path's steps, and the scratch-schema introspection that derives a diff once the table exists. (#63)
Library-level desired-state execution
migrate.RunDesiredconverges one live table onto its parsed desired schema: derive the convergence plan, admit it as a whole (table existence, destructive guard, routed dispositions, optionalExpectedFingerprintpin), then run each planned statement back through the samemigrate.Runpipeline with fresh introspection and classification, stopping at the first refusal or failure. The result carries the plan, per-statement verdicts, and an aggregate outcome with committed-prefix detail. (#49, #53)- Two new refusal reasons:
destructive-change(the plan discards live structure — desired-state execution never runs it) andplan-fingerprint-mismatch(the plan derived at execution time is not the pinned reviewed plan). Library-only for now — themigrate --desiredCLI verb follows separately. (#53)
Classification and resolution fixes
- Alter attempts pin
search_pathto the target schema (thenpublic) whenever the statement is schema-qualified — the same resolution the create path and introspection use, so unqualified secondary names (a column's type, an expression's function) can no longer silently bind a same-named object inpublic. (#63) ALTER COLUMN ... DROP NOT NULLnow classifies as destructive — droppingNOT NULLdiscards the same guarantee as dropping the equivalent constraint;DROP DEFAULTdeliberately stays non-destructive. (#53)
Upgrade notes
- A greenfield desired plan now executes where it previously refused — automation branching on the old
unsupported-statementrefusal for a not-yet-existing table sees the create run instead. - Callers relying on ambient
search_pathresolution must qualify secondary names — alter attempts on a schema-qualified statement now resolve unqualified secondary names (a column's type, an expression's function) in the target schema, not the session'ssearch_path. ALTER COLUMN ... DROP NOT NULLnow reportsdestructive: truein plan reports, and desired-state execution refuses it like any other drop.DROP DEFAULTdeliberately stays non-destructive.- Refusal-reason vocabulary additions:
create-collision,destructive-change,plan-fingerprint-mismatch. A consumer enumerating the closed set should re-sync.
Docs
- docs/capabilities.md — the canonical tiered support matrix, now with an online-safety column (#56, #57)
- docs/execution-model.md — autocommit-each-step and the committed prefix (#50)
- Known refusal and disclosure gaps on the create path are documented in docs/limitations.md.
Contributors
Full changelog: v0.1.0...v0.2.0