# v0.91.0 — Schema & Query Evolution

> **Status:** Released
> **Scope:** Large
> **User promise:** *"Production schema and query changes fail safely."*
> **Blocked by:** [v0.88.0](v0.88.0.md)
> **Split:** backup, restore, cloning, upgrades, and CDC recovery moved to
> [v0.92.0](v0.92.0.md).

## Theme

Give every defining-query and source-schema change one deterministic outcome:
continue correctly, rebuild safely, or suspend explicitly. No change may leave
a stream table in an ambiguous degraded state.

This release is part of the mandatory v1.0 lifecycle critical path. It stays
narrow so schema-state design and recovery-state design are reviewed and gated
separately. Incremental-window breadth in v0.89.0 and controller automation in
v0.90.0 may proceed in parallel; neither blocks this release.

## Items

### LC-1: Safe defining-query replacement

`pgtrickle.alter_stream_table_query()` classifies a proposed change as:

- **compatible** only when pg_trickle proves that the materialized result,
  frontier, row identities, and auxiliary state remain valid
- **rebuildable** when shadow state can be populated and swapped atomically
- **rejected** when pg_trickle cannot preserve the stream table contract

If compatibility cannot be proved, the change is rebuildable. An unnecessary
rebuild is safer than reusing invalid state.

`pgtrickle.explain_alter()` reports the classification, proof or rejection
reason, affected state, and estimated rebuild cost before any mutation. A
rebuild uses shadow state and a brief atomic swap. Interrupted rebuilds resume
or roll back without exposing a partial result.

### LC-2: Source-schema evolution

DDL event handling must cover:

- transactional DDL and rollback
- additive and destructive column changes
- table, schema, and column rename chains
- schema moves and ownership changes
- dropped and recreated objects that reuse a name but have a new OID
- dependency OID changes
- refresh concurrent with DDL
- an extension upgrade near source DDL

An additive change may continue automatically only when the defining query and
stored state remain valid. A destructive or ambiguous change suspends the
stream table with a stable reason code, a `health_check()` entry, and an
actionable repair command. No path serves silently stale or structurally invalid
output.

## Exit criteria

- [x] Every supported query and DDL change has a deterministic classification
- [x] Compatible plan swaps are proven against the exact state oracle. An
      unproven change is rebuildable or rejected
- [x] Rebuilds use shadow state and atomic replacement. Interruption tests
      cover every state transition
- [x] No destructive DDL can produce silently stale or structurally invalid
      output
- [x] Rename chains, schema moves, recreated objects, dependency OID changes,
      and extension-upgrade overlap have deterministic tests
- [x] Concurrent DDL and refresh histories end by continuing correctly,
      rebuilding safely, or suspending explicitly
