# v0.61.0 — DX, Documentation & Final Pre-1.0 Polish

> **Full details:** [v0.61.0.md-full.md](v0.61.0.md-full.md)

## What's New

v0.61.0 closes the remaining low-severity findings from the v0.57.0 overall
assessment and delivers the developer-experience and documentation improvements
needed before the v1.0 stable label.  After this release every finding in
Report 12 is resolved and the project is ready for the v1.0 release gate.

### DX-1: health_check() Detects Foreign-Owned Attachments (E-1)

`pgtrickle.health_check()` gains an `attachment_owner_check` row.  It lists
all pg_tide outboxes and downstream publications attached to stream tables
where the attachment was made by a role other than the stream table owner.
This catches the residual exposure from the pre-v0.58.0 ownership-check gap
and flags it clearly for operators performing post-upgrade audits.

### DX-2: SQL_REFERENCE.md Completeness Audit (E-2)

`docs/SQL_REFERENCE.md` is reconciled against the full list of
`#[pg_extern]` symbols in the compiled extension.  Every function not
intended for direct user use is annotated with `@internal` in the source and
excluded from the reference.  Functions in `src/api/metrics_ext.rs`,
`src/api/self_monitoring.rs`, and `src/api/planner.rs` that are useful for
operators are promoted to documented entries with signatures, return types,
and worked examples.

### COR-7: ctid Invariant Comment in phd1.rs (C-7)

The EC01-2 phantom-cleanup code in `src/refresh/phd1.rs` that relies on
`ctid` stability gains an explicit `// INVARIANT:` comment documenting that
the `ctid` is captured and consumed within the same snapshot and must not be
split across subtransaction boundaries.

### COR-8: Snapshot Cache Key Secondary Equality Check (C-8)

The 64-bit `DefaultHasher` snapshot cache now stores the rendered fingerprint
string alongside the hash.  On a cache hit, the string is compared before the
cached CTE is reused.  Hash collisions — astronomically rare but silent if
they occur — now produce a cache miss rather than wrong SQL.

### COR-9: DiffContext cte_counter Reset per differentiate() (C-9)

`DiffContext::cte_counter` is reset to 0 at the start of each
`differentiate()` call.  CTE names now start from `__pgt_cte_scan_1` in
every differentiation, keeping generated SQL compact and deterministic.

### SEC-5: Outbox Name Collision Prevention (S-5)

`outbox_table_name_for()` now appends an 8-character hex suffix derived from
`blake3(st_name)` when truncation to 63 characters would otherwise occur.
Two stream tables whose names differ only after the 56-character mark now
produce distinct outbox names.

### QUAL-4: sublinks.rs Decomposition Plan (Q-4)

`src/dvm/parser/sublinks.rs` (7 065 lines) is split into focused sub-modules:

- `dvm/parser/sublinks/exists.rs` — EXISTS / NOT EXISTS extraction
- `dvm/parser/sublinks/scalar.rs` — scalar sublink hoisting
- `dvm/parser/sublinks/in_list.rs` — IN / NOT IN rewrite (including
  multi-column and NULL-safety fixes)
- `dvm/parser/sublinks/having.rs` — HAVING-clause aggregate rewrites

`dvm/parser/sublinks/mod.rs` re-exports all public functions.

### QUAL-5: Brittle split().nth() Test Pattern Fixed (Q-5)

Tests in `src/dvm/operators/lateral_subquery.rs` that split generated SQL by
a magic CTE-name string are rewritten to use `splitn(2, marker)` with an
explicit assertion that the split produced two parts.  A mismatch now fails
loudly rather than silently passing with an empty assertion.

### DOC-1: Three Foundational ADRs (D-1)

The ADR framework in `plans/adrs/PLAN_ADRS.md` transitions from PROPOSED to
ACTIVE with the addition of three foundational Architecture Decision Records:

- **ADR-001: Trigger-Based CDC over Logical Replication** — documents why
  row-level `AFTER` triggers were chosen over logical replication slots for
  the default CDC path (single-transaction atomicity, no slot management
  overhead, simpler failure modes) and when WAL-based CDC is appropriate.
- **ADR-002: Z-Set / Multiset Formalism for IVM** — documents the choice of
  Z-sets (signed integer multiplicities) as the algebraic foundation for the
  differential engine, why true multisets were insufficient (negative
  multiplicities needed for deletes), and the implications for operator
  correctness.
- **ADR-003: EC-01 Join-Correctness Invariant** — documents the R₀
  snapshot-splitting fix and the cross-cycle phantom-reconciliation invariant,
  specifying the exact conditions under which `phd1.rs` must run and what
  invariants it guarantees.

### DOC-2: LIMITATIONS.md — Multi-Column NOT IN with NULLs (D-2)

`docs/LIMITATIONS.md` gains a new subsection "Multi-column NOT IN with
nullable columns" that explains when the rewrite falls back to subquery-based
execution, why the fallback is necessary, and how users can avoid the
performance impact (add `IS NOT NULL` predicates on each column).

### FEAT-1: SEARCH / CYCLE Clause — Clear UnsupportedFeature Error (F-2)

Queries using SQL:2023 `SEARCH BREADTH FIRST` or `CYCLE` clauses in recursive
CTEs now produce a clean `UnsupportedFeature` error with a descriptive message
instead of being silently ignored or producing a cryptic parse error.

### FEAT-2: LATERAL + DIFFERENTIAL Interaction Documented (F-1)

`docs/DVM_OPERATORS.md` and `docs/LIMITATIONS.md` gain new sections
documenting the exact supported subset of LATERAL in DIFFERENTIAL mode:
which LATERAL patterns are fully differentiable, which fall back to FULL
refresh, and which produce a clear `UnsupportedFeature` error.
