> **Plain-language companion:** [v0.15.0.md](v0.15.0.md)

## v0.15.0 — External Test Suites & Integration

**Status: Released (2026-04-03).** All 20 roadmap items complete.

**Goal:** Validate correctness against independent query corpora and ship the
dbt integration as a formal release.

### External Test Suite Integration

> **In plain terms:** pg_trickle's own tests were written by the pg_trickle
> team, which means they can have the same blind spots as the code. This
> adds validation against three independent public benchmarks: PostgreSQL's
> own SQL conformance suite (sqllogictest), the Join Order Benchmark (a
> realistic analytical query workload), and Nexmark (a streaming data
> benchmark). If pg_trickle produces a different answer than PostgreSQL does
> on the same query, these external suites will catch it.

Validate correctness against independent query corpora beyond TPC-H.

> ➡️ **TS1 and TS2 pulled forward to v0.11.0.** Delivering one of TS1 or TS2 is an
> exit criterion for 0.11.0. TS3 (Nexmark) remains in 0.15.0. If TS1/TS2 slip
> from 0.11.0, they land here.

| Item | Description | Effort | Ref |
|------|-------------|--------|-----|
| ~~TS1~~ | ~~sqllogictest: run PostgreSQL sqllogic suite through pg_trickle DIFFERENTIAL mode~~ ➡️ Pulled to v0.11.0 | 2–3d | [PLAN_TESTING_GAPS.md](plans/testing/PLAN_TESTING_GAPS.md) §J |
| ~~TS2~~ | ~~JOB (Join Order Benchmark): correctness baseline and refresh latency profiling~~ ➡️ Pulled to v0.11.0 | 1–2d | [PLAN_TESTING_GAPS.md](plans/testing/PLAN_TESTING_GAPS.md) §J |
| TS3 | Nexmark streaming benchmark: sustained high-frequency DML correctness | 1–2d | [PLAN_TESTING_GAPS.md](plans/testing/PLAN_TESTING_GAPS.md) §J |

> **External test suites subtotal: ~1–2 days (TS3 only; TS1/TS2 in v0.11.0)** -- ✅ TS3 complete

### Documentation Review

> **In plain terms:** A full documentation review polishes everything so the
> product is ready to be announced to the wider PostgreSQL community.

| Item | Description | Effort | Ref |
|------|-------------|--------|-----|
| I2 | Complete documentation review & polish | 4--6h | [docs/](docs/) |

> **Documentation subtotal: ✅ Done**

### Bulk Create API (G15-BC)

| Item | Description | Effort | Ref |
|------|-------------|--------|-----|
| G15-BC | ~~**`bulk_create(definitions JSONB)`** — create multiple stream tables and their CDC triggers in a single transaction. Useful for dbt/CI pipelines that manage many STs programmatically.~~ ✅ Done | ~2–3d | [plans/performance/REPORT_OVERALL_STATUS.md §15](plans/performance/REPORT_OVERALL_STATUS.md) |

> **G15-BC subtotal: ✅ Completed**

### Parser Modularization (G13-PRF) -- ✅ Done

> **In plain terms:** At ~21,000 lines, `parser.rs` was too large to maintain
> safely. Split into 5 sub-modules by concern -- zero behavior change.

| Item | Description | Effort | Ref |
|------|-------------|--------|-----|
| G13-PRF | ~~**Modularize `src/dvm/parser.rs`.**~~ ✅ Done. Split into `mod.rs`, `types.rs`, `validation.rs`, `rewrites.rs`, `sublinks.rs`. Added `// SAFETY:` comments to all ~750 `unsafe` blocks (~676 newly documented). | ~3–4wk | [plans/performance/REPORT_OVERALL_STATUS.md §13](plans/performance/REPORT_OVERALL_STATUS.md) |

> **G13-PRF subtotal: ✅ Completed**

### Watermark Hold-Back Mode (WM-7) -- ✅ Done

> **In plain terms:** The watermark gating system (shipped in v0.7.0) lets
> ETL producers signal their progress. Hold-back mode adds stuck detection:
> when a watermark is not advanced within a configurable timeout, downstream
> stream tables are paused and operators are notified.

| Item | Description | Effort | Ref |
|------|-------------|--------|-----|
| WM-7 | **Watermark hold-back mode.** `watermark_holdback_timeout` GUC detects stuck watermarks; pauses downstream gated STs; emits `pgtrickle_alert` NOTIFY with `watermark_stuck` event; auto-resumes with `watermark_resumed` event when watermark advances. | ✅ Done | [PLAN_WATERMARK_GATING.md §4.1](plans/sql/PLAN_WATERMARK_GATING.md) |

> **WM-7 subtotal: ✅ Done**

### Delta Cost Estimation (PH-E1) — ✅ Done

> **In plain terms:** Before executing the MERGE, runs a capped COUNT on the
> delta subquery to estimate output cardinality. If the count exceeds
> `pg_trickle.max_delta_estimate_rows`, emits a NOTICE and falls back to FULL
> refresh to prevent OOM or excessive temp-file spills.

| Item | Description | Effort | Ref |
|------|-------------|--------|-----|
| PH-E1 | **Delta cost estimation.** Capped `SELECT count(*) FROM (delta LIMIT N+1)` before MERGE execution. `max_delta_estimate_rows` GUC (default: 0 = disabled). Falls back to FULL + NOTICE when exceeded. | — | [PLAN_PERFORMANCE_PART_9.md §Phase E](plans/performance/PLAN_PERFORMANCE_PART_9.md) |

> **PH-E1 subtotal: ✅ Complete**

### dbt Hub Publication (I3) — ✅ Done

> **In plain terms:** `dbt-pgtrickle` is now prepared for dbt Hub publication.
> The `dbt_project.yml` is version-synced (0.15.0), README documents both
> git and Hub install methods, and a submission guide documents the hubcap
> PR process. Actual Hub listing requires creating a standalone `grove/dbt-pgtrickle`
> repository and submitting a PR to `dbt-labs/hubcap`.

| Item | Description | Effort | Ref |
|------|-------------|--------|-----|
| I3 | Prepared `dbt-pgtrickle` for [dbt Hub](https://hub.getdbt.com/) publication. Version synced to 0.15.0, README updated with Hub install snippet, submission guide written. Hub listing pending separate repo creation + hubcap PR. | 2–4h | [dbt-pgtrickle/](dbt-pgtrickle/) · [docs/integrations/dbt-hub-submission.md](docs/integrations/dbt-hub-submission.md) |

> **I3 subtotal: ~2–4 hours** — ✅ Complete

### Hash-Join Planner Hints (PH-D2) — ✅ Done

> **In plain terms:** Added `pg_trickle.merge_join_strategy` GUC that lets
> operators manually override the join strategy used during MERGE. Values:
> `auto` (default heuristic), `hash_join`, `nested_loop`, `merge_join`.
> The existing delta-size heuristics remain the default (`auto`).

| Item | Description | Effort | Ref |
|------|-------------|--------|-----|
| PH-D2 | **Hash-join planner hints.** Added `merge_join_strategy` GUC with manual override for join strategy during MERGE. `auto` preserves existing delta-size heuristics; `hash_join`/`nested_loop`/`merge_join` force specific strategies. | 3–5d | [PLAN_PERFORMANCE_PART_9.md §Phase D](plans/performance/PLAN_PERFORMANCE_PART_9.md) |

> **PH-D2 subtotal: ~3–5 days** — ✅ Complete

### Shared-Memory Template Cache Research Spike (G14-SHC-SPIKE)

> **In plain terms:** Every new database connection that triggers a refresh
> pays a 15–50ms cold-start cost to regenerate the MERGE SQL template. With
> PgBouncer in transaction mode, this happens on every refresh cycle. This
> milestone scopes a research spike only: write an RFC, build a prototype,
> measure whether DSM-based caching eliminates the cold-start. Full
> implementation stays in v0.16.0.

| Item | Description | Effort | Ref |
|------|-------------|--------|-----|
| G14-SHC-SPIKE | **Shared-memory template cache research spike.** Write an RFC for DSM + lwlock-based MERGE SQL template caching. Build a prototype benchmark to validate cold-start elimination. Full implementation deferred to v0.16.0. | 2–3d | [plans/performance/REPORT_OVERALL_STATUS.md §14](plans/performance/REPORT_OVERALL_STATUS.md) |

> **G14-SHC-SPIKE subtotal: ~2–3 days** -- ✅ RFC complete (plans/performance/RFC_SHARED_TEMPLATE_CACHE.md)

### TRUNCATE Capture for Trigger-Mode CDC (TRUNC-1)

> **In plain terms:** WAL-mode CDC detects TRUNCATE on source tables and
> marks downstream stream tables for reinitialization. But trigger-mode CDC
> has no TRUNCATE handler — a `TRUNCATE` silently leaves the stream table
> stale. Adding a DDL event trigger that catches TRUNCATE and flags affected
> STs closes this correctness gap.

| Item | Description | Effort | Ref |
|------|-------------|--------|-----|
| TRUNC-1 | ~~**TRUNCATE capture for trigger-mode CDC.** Add a DDL event trigger or statement-level trigger that detects TRUNCATE on source tables in trigger CDC mode and marks downstream STs for `needs_reinit`.~~ ✅ Done — CDC TRUNCATE triggers write `action='T'` marker; refresh engine detects and falls back to FULL. | 4–6h | [plans/adrs/PLAN_ADRS.md](plans/adrs/PLAN_ADRS.md) ADR-070 |

> **TRUNC-1 subtotal: ✅ Completed**

### Volatile Function Policy GUC (VOL-1)

> **In plain terms:** Volatile functions (`random()`, `clock_timestamp()`,
> etc.) are correctly rejected at stream table creation time in DIFFERENTIAL
> and IMMEDIATE modes. But there’s no way for users to override this — some
> want volatile functions in FULL mode. Adding a `volatile_function_policy`
> GUC with `reject`/`warn`/`allow` modes gives operators control.

| Item | Description | Effort | Ref |
|------|-------------|--------|-----|
| VOL-1 | ~~**`pg_trickle.volatile_function_policy` GUC.** Add a GUC with values `reject` (default), `warn`, `allow` to control volatile function handling. `reject` preserves current behavior; `warn` emits WARNING but allows creation; `allow` silently permits (user accepts correctness risk).~~ ✅ Done | 3–5h | [plans/sql/PLAN_NON_DETERMINISM.md](plans/sql/PLAN_NON_DETERMINISM.md) |

> **VOL-1 subtotal: ✅ Completed**

### Spill-Aware Refresh (PH-E2)

> **In plain terms:** After PH-E1 adds pre-flight cost estimation, PH-E2
> adds post-flight monitoring: track `temp_bytes` from `pg_stat_statements`
> after each refresh cycle and auto-adjust if spill is excessive.

| Item | Description | Effort | Ref |
|------|-------------|--------|-----|
| PH-E2 | ~~**Spill-aware refresh.** Monitor `temp_bytes` from `pg_stat_statements` after each refresh cycle. If spill exceeds threshold 3 consecutive times, automatically increase `per-ST work_mem` override or switch to FULL. Expose in `explain_st()` as `spill_history`.~~ ✅ Done | 1–2 wk | [PLAN_PERFORMANCE_PART_9.md §Phase E](plans/performance/PLAN_PERFORMANCE_PART_9.md) |

> **PH-E2 subtotal: ✅ Completed**

### ORM Integration Guides (E5)

> **In plain terms:** Documentation showing how popular ORMs (SQLAlchemy,
> Django, etc.) interact with stream tables — model definitions, migrations,
> and freshness checks. Documentation-only work.

| Item | Description | Effort | Ref |
|------|-------------|--------|-----|
| E5 | ORM integrations guide (SQLAlchemy, Django, etc.) | 8–12h | [PLAN_ECO_SYSTEM.md §5](plans/ecosystem/PLAN_ECO_SYSTEM.md) |

> **E5 subtotal: ✅ Done**

### Flyway / Liquibase Migration Support (E4)

> **In plain terms:** Documentation showing how standard migration frameworks
> interact with stream tables — CREATE/ALTER/DROP patterns, handling CDC
> triggers across schema migrations. Documentation-only work.

| Item | Description | Effort | Ref |
|------|-------------|--------|-----|
| E4 | Flyway / Liquibase migration support | 8–12h | [PLAN_ECO_SYSTEM.md §5](plans/ecosystem/PLAN_ECO_SYSTEM.md) |

> **E4 subtotal: ✅ Done**

### JOIN Key Change + DELETE Correctness Fix (EC-01) — ✅ Done (pre-existing)

> **In plain terms:** The phantom-row-after-DELETE bug was fixed in v0.14.0
> via the R₀ pre-change snapshot strategy. Part 1 of the JOIN delta is split
> into 1a (inserts ⋈ R₁) + 1b (deletes ⋈ R₀), ensuring DELETE deltas always
> find the old join partner. The fix was extended to all join depths via the
> EC-01B-1 per-leaf CTE strategy, and regression tests (EC-01B-2) cover
> TPC-H Q07, Q08, Q09.

| Item | Description | Effort | Ref |
|------|-------------|--------|-----|
| EC-01 | **R₀ pre-change snapshot for JOIN key change + DELETE.** Part 1 split into 1a (inserts ⋈ R₁) + 1b (deletes ⋈ R₀). Applied to INNER/LEFT/FULL JOIN. Closes G1.1. | — | [GAP_SQL_PHASE_7.md §G1.1](plans/sql/GAP_SQL_PHASE_7.md) |

> **EC-01 subtotal: ✅ Complete (implemented in v0.14.0)**

### Multi-Level ST-on-ST Testing (STST-3)

> **In plain terms:** FIX-STST-DIFF (v0.14.0) fixed 2-level
> stream-table-on-stream-table DIFFERENTIAL refresh. Some 3-level cascade
> tests exist, but systematic coverage for 3+ level chains — including
> mixed refresh modes, concurrent DML at multiple levels, and DELETE/UPDATE
> propagation through deep chains — is missing. This adds a dedicated test
> matrix to prevent regressions as cascade depth increases.

| Item | Description | Effort | Ref |
|------|-------------|--------|-----|
| STST-3 | **Multi-level ST-on-ST test matrix (3+ levels).** Systematic coverage: 3-level and 4-level chains, INSERT/UPDATE/DELETE propagation, mixed DIFFERENTIAL/FULL modes, concurrent DML at multiple levels, correctness comparison against materialized-view baseline. | 3–5d | [e2e_cascade_regression_tests.rs](tests/e2e_cascade_regression_tests.rs) |

> **STST-3 subtotal: ✅ Done**

### Circular Dependencies + IMMEDIATE Mode (CIRC-IMM)

> **In plain terms:** Circular dependencies are rejected at creation time
> (EC-30), but the interaction between near-circular topologies (e.g.
> diamond dependencies with IMMEDIATE triggers on both sides) and IMMEDIATE
> mode is untested territory. This adds targeted testing and, if needed,
> hardening to ensure IMMEDIATE mode doesn't deadlock or produce incorrect
> results on complex dependency graphs. **Conditional P1 — can slip to
> v0.16.0 if no issues surface during other IMMEDIATE-mode work.**

| Item | Description | Effort | Ref |
|------|-------------|--------|-----|
| CIRC-IMM | **Circular-dependency + IMMEDIATE mode hardening.** Test: diamond deps with IMMEDIATE triggers, near-circular topologies, lock ordering under concurrent DML. Add deadlock detection / timeout guard if issues found. | 3–5d | [PLAN_EDGE_CASES.md §EC-30](plans/PLAN_EDGE_CASES.md) · [PLAN_CIRCULAR_REFERENCES.md](plans/sql/PLAN_CIRCULAR_REFERENCES.md) |

> **CIRC-IMM subtotal: ✅ Done**

### Cross-Session MERGE Cache Staleness Fix (G8.1)

> **In plain terms:** When session A alters a stream table's defining query,
> session B's cached MERGE SQL template remains stale until B encounters a
> refresh error or reconnects. Adding a catalog version counter that is
> bumped on every ALTER QUERY and checked before each refresh closes this
> race window.

| Item | Description | Effort | Ref |
|------|-------------|--------|-----|
| G8.1 | ~~**Cross-session MERGE cache invalidation.** Add a `catalog_version` counter to `pgt_stream_tables`, bump on ALTER QUERY / DROP / reinit. Before each refresh, compare cached version to catalog; regenerate template on mismatch.~~ ✅ Done — existing `CACHE_GENERATION` counter + `defining_query_hash` provides cross-session + per-ST invalidation without a schema change. | 4–6h | — |

> **G8.1 subtotal: ✅ Completed**

### `explain_st()` Enhancements (EXPL-ENH) — ✅ Done

> **In plain terms:** Small quality-of-life improvements to the diagnostic
> function: refresh timing statistics, partition source info, and a dependency-graph
> visualization snippet in DOT format.

| Item | Description | Effort | Ref |
|------|-------------|--------|-----|
| EXPL-ENH | **`explain_st()` enhancements.** Added: (a) refresh timing stats (min/max/avg/latest duration from last 20 refreshes), (b) source partition info for partitioned tables, (c) dependency sub-graph visualization in DOT format. | 4–8h | [PLAN_FEATURE_CLEANUP.md](plans/PLAN_FEATURE_CLEANUP.md) |

> **EXPL-ENH subtotal: ~4–8 hours** — ✅ Complete

### CNPG Operator Hardening (R4)

> **In plain terms:** Kubernetes-native improvements for the CloudNativePG
> integration: adopt K8s 1.33+ native ImageVolume (replacing the init-container
> workaround), add liveness/readiness probe integration for pg_trickle health,
> and test failover behavior with stream tables.

| Item | Description | Effort | Ref |
|------|-------------|--------|-----|
| R4 | **CNPG operator hardening.** Adopt K8s 1.33+ native ImageVolume, add pg_trickle health to CNPG liveness/readiness probes, test primary→replica failover with active stream tables. | 4–6h | [PLAN_CLOUDNATIVEPG.md](plans/ecosystem/PLAN_CLOUDNATIVEPG.md) |

> **R4 subtotal: ~4–6 hours** -- ✅ Complete

> **v0.15.0 total: ~52–90h + ~2–3d bulk create + ~3–5d planner hints + ~2–3d cache spike + ~3–4wk parser + ~1–2wk watermark + ~2–4wk delta cost/spill + ~2–3d EC-01 + ~3–5d ST-on-ST + ~3–5d CIRC-IMM**

**Exit criteria:**
- [x] At least one external test corpus (sqllogictest, JOB, or Nexmark) passes
- [x] Complete documentation review done
- [x] G15-BC: `pgtrickle.bulk_create(definitions JSONB)` creates all STs and CDC triggers atomically; tested with 10+ definitions in a single call
- [x] G13-PRF: `parser.rs` split into 5 sub-modules; zero behavior change; all existing tests pass
- [x] WM-7: Stuck watermarks detected and downstream STs paused; `watermark_stuck` alert emitted; auto-resume on watermark advance
- [x] PH-E1: Delta cost estimation via capped COUNT on delta subquery; `max_delta_estimate_rows` GUC; FULL downgrade + NOTICE when threshold exceeded
- [x] PH-E2: Spill-aware auto-adjustment triggers after 3 consecutive spills; `spill_info` exposed in `explain_st()`
- [x] PH-D2: `merge_join_strategy` GUC with manual override (`auto`/`hash_join`/`nested_loop`/`merge_join`)
- [x] G14-SHC-SPIKE: RFC written; prototype benchmark validates or invalidates DSM-based approach
- [x] I2: Complete documentation review done -- CONFIGURATION.md GUCs documented (40+), SQL_REFERENCE.md gaps filled, FAQ refs fixed
- [x] TRUNC-1: TRUNCATE on trigger-mode CDC source marks downstream STs for reinit; tested end-to-end
- [x] VOL-1: `volatile_function_policy` GUC controls volatile function handling; `reject`/`warn`/`allow` modes tested
- [x] I3: `dbt-pgtrickle` prepared for dbt Hub; submission guide written; Hub listing pending separate repo + hubcap PR
- [x] E4: Flyway / Liquibase integration guide published in `docs/integrations/flyway-liquibase.md`
- [x] E5: ORM integration guides (SQLAlchemy, Django) published in `docs/integrations/orm.md`
- [x] EC-01: R₀ pre-change snapshot ensures DELETE deltas find old join partners; unit + TPC-H regression tests confirm correctness
- [x] STST-3: 3-level and 4-level ST-on-ST chains tested with INSERT/UPDATE/DELETE propagation; mixed modes covered
- [x] CIRC-IMM: Diamond + near-circular IMMEDIATE topologies tested; no deadlocks or incorrect results
- [x] G8.1: Cross-session MERGE cache invalidation via catalog version counter; tested with concurrent ALTER QUERY + refresh
- [x] EXPL-ENH: `explain_st()` shows refresh timing stats, source partition info, and dependency sub-graph (DOT format)
- [x] R4: CNPG operator hardening — ImageVolume, health probes, failover tested
- [x] G13-PRF: `parser.rs` split into 5 sub-modules; all ~750 `unsafe` blocks have `// SAFETY:` comments; zero behavior change; all existing tests pass
- [x] Extension upgrade path tested (`0.14.0 → 0.15.0`)
- [x] `just check-version-sync` passes


---

