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

## v0.38.0 — EC-01 Correctness Sprint (Hard Gate)

**Status: Released.** This focused release closes EC-01 (join phantom rows)
with unconditional PH-D1 cleanup, plan-time `RowIdSchema` verification, and a
DIFF-vs-FULL join convergence property test. Derived from
[plans/PLAN_OVERALL_ASSESSMENT_8.md](plans/PLAN_OVERALL_ASSESSMENT_8.md) §4.1
(Finding C1).

> **Release Theme**
> v0.38.0 is a correctness-only release. Operational truthfulness, SQLSTATE
> rollout, generated docs, OpenTelemetry operator docs, and ecosystem coverage
> remain in v0.39.0+ so EC-01 is not deferred behind unrelated work.

---

### Features (EC-01 Only)

| ID | Title | Effort | Priority |
|----|-------|--------|----------|
| C38-1 | Unconditional PH-D1 cleanup plus join row-id convergence | L | P0 |
| C38-2 | Plan-time `RowIdSchema` verification in DVM planner | M | P0 |

**C38-1 — Unconditional PH-D1 cleanup plus join row-id convergence.**

Non-deduplicated join deltas now run PH-D1 cross-cycle cleanup after every
differential apply for keyed, non-partitioned stream tables. The cleanup wraps
the defining query with the same row-id expression used by FULL refresh and
removes stream-table row IDs that no longer exist in the current full result
set. This makes the cleanup unconditional for the EC-01 class instead of gating
it on join shape, refresh count, or previously detected residuals.

The join delta row-id formula continues to use canonical left-first/right-second
key hashing across Part 1a, Part 1b, Part 2, and correction terms. PH-D1 cleanup
is the final convergence guard for historical phantoms whose row IDs are absent
from the current delta.

**Acceptance criteria:**
- [x] Join deltas either return `is_deduplicated: true` or are cleaned by
      unconditional PH-D1
- [x] Q07 and Q15 are no longer in `IMMEDIATE_SKIP_ALLOWLIST`
- [x] No phantom accumulation across random mixed-DML join property cycles

**C38-2 — Plan-time `RowIdSchema` verification in DVM planner.**

`RowIdSchema` is now exercised at DVM planning time. The planner infers row-id
schemas for scans, transparent operators, projections, joins, aggregates, set
operations, CTEs, recursive plans, lateral plans, and scalar subqueries before
generating the delta SQL. Any incompatible row-id pipeline fails planning with a
clear `RowIdSchema verification failed` error instead of reaching refresh time
as silent drift.

### Test Coverage (EC-01 Only)

| ID | Title | Effort | Priority |
|----|-------|--------|----------|
| T-C38-1 | EC-01 DIFF-vs-FULL property generator for joins | L | P0 |

**T-C38-1 — EC-01 DIFF-vs-FULL property generator (Release Gate).**

`tests/e2e_ec01_property_tests.rs` creates matching FULL and DIFFERENTIAL
stream tables over a three-table join aggregate, then runs 100 deterministic
mixed-DML cycles by default. Each cycle includes inserts, updates, left/right
deletes, and co-deletes across join participants. After every refresh cycle,
the test verifies both stream tables against the defining query and compares
FULL vs DIFFERENTIAL with multiset equality. Divergence diagnostics include row
counts, row IDs, changed action counts, and sample extra/missing rows.

The test is part of the light-E2E allowlist so CI runs it on every PR.

### What's NOT in v0.38.0

- Operational truthfulness (backpressure, wake, docs) — v0.39.0
- SQLSTATE-first retry classification rollout — v0.39.0
- Generated `CONFIGURATION.md` / `UPGRADING.md` drift checks — v0.39.0+
- OpenTelemetry operator guide — v0.39.0
- pg_partman / TimescaleDB ecosystem coverage — v0.40.0+
- `history_prune_interval_seconds` wiring — v0.40.0

### Recommended Implementation Order

1. ✅ C38-1 — PH-D1 cleanup and join convergence guard
2. ✅ C38-2 — plan-time `RowIdSchema` verification
3. ✅ T-C38-1 — EC-01 DIFF-vs-FULL property release gate
4. ✅ Release metadata, upgrade SQL, changelog, and roadmap status

### Implementation Status

| ID | Title | Status |
|----|-------|--------|
| C38-1 | Unconditional PH-D1 cleanup plus join row-id convergence | ✅ Done |
| C38-2 | Plan-time `RowIdSchema` verification in DVM planner | ✅ Done |
| T-C38-1 | EC-01 DIFF-vs-FULL property generator for joins | ✅ Done |

### Exit Criteria

- [x] C38-1: Non-deduplicated join outputs no longer accumulate phantom rows in random property workloads
- [x] C38-2: Incompatible `RowIdSchema` pipelines fail planning with a clear reason
- [x] T-C38-1: DIFF-vs-FULL property convergence test is present and CI-covered
- [x] Q07 and Q15 removed from `IMMEDIATE_SKIP_ALLOWLIST`
- [x] Extension upgrade path added (`0.37.0 -> 0.38.0`)
- [x] CHANGELOG.md entry written
- [x] ROADMAP.md v0.38.0 row marked ✅ Released
- [x] `just check-version-sync` passes
