This release delivers the three high-priority architectural improvements identified in the Phase 4 engineering assessment: typed executor configuration, full multi-tenant catalog parameterisation, and automated crash recovery via the compensating-step registry.
The ExecutionContext struct replaces the optional builder pattern for the two safety-critical executor fields (desired_state and connection_string), making it a compile error to omit them. All command handlers (apply, rollback, promote) have been updated accordingly.
Every catalog SQL constant now accepts a CatalogSchema parameter and substitutes the schema name via for_schema(). The validate_catalog_schema_not_overridden() stop-gap from v0.19 is removed. aqueduct init --schema my_catalog creates all catalog tables in my_catalog; the multi-tenant isolation integration test verifies that two projects on the same database with different catalog schemas have fully independent version histories.
The --resume flow now queries aqueduct.ddl_log for rows in status = 'running' before advancing to the checkpoint. For each such row the compensating SQL is executed and the row is updated to status = 'compensated'. A new integration test covers the crash-after-DDL, before-RecordSnapshot scenario.
The OutputEmitter is now stored as a process-wide singleton via OnceLock, initialised in main before command dispatch and accessible via output::emitter(). The init command is the first to use the emitter for its success messages.
The httpmock dependency was replaced with wiremock in v0.17, eliminating the last cargo audit --deny warnings finding (RUSTSEC-2025-0052).
The catalog schema version advances from 8 to 9, adding a status column to aqueduct.ddl_log used by compensating-step recovery.
Added
- Typed
ExecutionContextstruct (ARCH-2):ExecutionContextstruct with required
desired_stateandconnection_stringfields replaces the optional builder pattern.
Omitting either field is now a compile error.for_apply(),for_rollback(), and
for_promote()constructors updated. - Full catalog schema parameterisation (ARCH-1):
CatalogSchemathreaded through
every catalog SQL function.ensure_catalog_current(),connect_and_migrate(),
read_live_state(),get_latest_dag_version(),detect_extension_installed(),
read_ddl_log(),import_from_live(),compute_promotion_plan(),
validate_source_clean(), anddestroy_project()all accept&CatalogSchema.
validate_catalog_schema_not_overridden()stop-gap removed. - Multi-tenant catalog isolation test:
test_multi_tenant_catalog_isolationverifies
that two catalogs (tenant_a,tenant_b) on the same database have independent
dag_versionstables with no cross-contamination. - Automated compensating-step recovery (CORR-2):
find_resume_step()queries
aqueduct.ddl_logforstatus = 'running'rows and executes theircompensating_sql
viabatch_executebefore advancing to the resume checkpoint. Rows are updated to
status = 'compensated'on success. COMPLETE_COMPENSATING_STEP_SQLandMARK_COMPENSATING_APPLIED_SQLconstants in
catalog.rsfor the two-phase compensating-step lifecycle.- CORR-2 crash recovery integration test:
test_corr2_compensating_step_recovery
inserts a syntheticstatus = 'running'ddl_log row and verifies that--resumemoves
it out of therunningstate. - Catalog schema version 9:
CATALOG_SCHEMA_VERSIONadvances from 8 to 9. The v8→v9
migration adds astatus TEXT NOT NULL DEFAULT 'complete'column toaqueduct.ddl_log
and backfills existing rows. OutputEmitterprocess-wide singleton (M-1):output::init_emitter(mode)stores
the emitter in astatic OnceLock<OutputEmitter>;output::emitter()returns the
global reference.main.rscallsinit_emitterbefore dispatch. Theinitcommand
uses the emitter for its success messages, replacing directprintln!calls.
Changed
for_schema(sql, schema)now substitutes= 'aqueduct'string-literal comparisons
andCREATE/DROP SCHEMA IF NOT EXISTS aqueductstatements in addition to the
aqueduct.dot-prefix references.DestroyOptionsgains acatalog_schema: CatalogSchemafield.- All
cargo check --testscall sites forread_live_state,get_latest_dag_version,
detect_extension_installed,read_ddl_log,import_from_live,
compute_promotion_plan,validate_source_clean, andDestroyOptions::newupdated
throughout CLI commands and integration tests. - Workspace version bumped
0.19.0→0.20.0.
Fixed
executor.rs:import_from_livewas callingread_live_state(client, None)(2-arg form)
instead of the 3-argread_live_state(client, None, catalog_schema). Fixed.- Extra trailing semicolons (
await?;;) incommands/promote.rsandcommands/status.rs
introduced by the multi-replace pass. Removed.