# v0.68.0 Full Details — Assessment-13 Correctness & Durability Sprint

> **Summary:** [v0.68.0.md](v0.68.0.md)
> **Assessment source:** [plans/PLAN_OVERALL_ASSESSMENT_13.md](../plans/PLAN_OVERALL_ASSESSMENT_13.md)

---

## Motivation

The v0.67.0 overall assessment (Assessment 13) found 36 findings across the
codebase. This release resolves the subset of HIGH-severity findings where a
code defect produces silent data loss, audit integrity loss, or incorrect
operator-visible behaviour. The principle is: fix the bugs before delivering
more features.

The six items resolved here were selected because:
- COR-001 leaves fused refresh completely invisible in the audit history.
- COR-003/ARCH-001 makes a documented GUC a no-op, which is a lie to operators.
- COR-004 silently destroys timestamp values in the DuckLake export path.
- SCAL-001 keeps dead code that would break on activation.
- TEST-003 and TEST-004 are the verification gates for the above fixes.

---

## Detailed Implementation

### COR-001: Fused Refresh Audit Trail

**Files changed:**
- `sql/pg_trickle--0.67.0--0.68.0.sql` — add `'SCHEDULER_FUSED'` to the
  `initiated_by` CHECK constraint; add `merge_strategy` column to
  `pgt_refresh_history`
- `sql/pg_trickle--0.68.0.sql` — full schema update
- `src/scheduler/mod.rs` — pass `Some("SCHEDULER_FUSED")` to
  `RefreshRecord::insert()`; convert audit insert errors to `pgrx::warning!`
  plus abort the tick
- `src/catalog.rs` — add `merge_strategy: Option<&str>` to `RefreshRecord`,
  extend `insert()` accordingly
- `tests/e2e_refresh_tests.rs` — new `test_fused_refresh_scheduler_audit_trail`
- `tests/common/mod.rs` — add `SCHEDULER_FUSED` and `fused_chain` to harness
  schema

**Acceptance criteria:**
1. `cargo test -p pg_trickle -- test_fused_refresh_scheduler_audit_trail` passes.
2. The upgrade migration applies cleanly on a database with existing
   `pgt_refresh_history` rows.
3. `just test-integration` passes with zero new failures.

---

### COR-003 / ARCH-001: `change_buffer_durability` Wired

**Files changed:**
- `src/config.rs` — add deprecation warning to `pg_trickle_unlogged_buffers()`;
  map old bool to `ChangeBufferDurability` at read time
- `src/cdc/mod.rs` — replace `pg_trickle_unlogged_buffers()` call with
  `pg_trickle_change_buffer_durability()` in `create_change_buffer_table()` and
  `create_st_change_buffer_table()`; handle `Sync` variant
- `docs/GUC_CATALOG.md` — deprecate `unlogged_buffers`; document migration path
  to `change_buffer_durability`
- `tests/e2e_unlogged_buffer_tests.rs` — add `test_change_buffer_durability_*`
  tests; mark existing bool tests as testing the backward-compat shim

**Acceptance criteria:**
1. `SET pg_trickle.change_buffer_durability = 'unlogged'` creates a buffer with
   `relpersistence = 'u'`.
2. `SET pg_trickle.change_buffer_durability = 'logged'` creates a buffer with
   `relpersistence = 'p'`.
3. `SET pg_trickle.unlogged_buffers = true` emits a `WARNING` and maps to
   `change_buffer_durability = 'unlogged'`.

---

### COR-004: DuckLake Timestamp Fix

**Files changed:**
- `src/ducklake_sink.rs` — `fetch_stream_table_rows()`: detect
  `timestamp`/`timestamptz` type OIDs and emit
  `EXTRACT(EPOCH FROM "col"::timestamptz) * 1000000` in the SELECT list;
  `write_parquet_bytes()`: parse the fetched value as `i64` directly
- `tests/e2e_ducklake_tests.rs` — new `test_ducklake_sink_timestamp_roundtrip`
  that creates a stream table with a `timestamptz` column, inserts a known
  value, triggers a sink write, reads the Parquet output file with the Arrow
  reader, and asserts the timestamp value is preserved

**Acceptance criteria:**
1. The round-trip test passes.
2. NULL columns remain NULL; non-NULL timestamps map to the correct microsecond
   epoch.

---

### SCAL-001: Pool Path Deletion

**Files changed:**
- `src/scheduler/pool.rs` — deleted
- `src/scheduler/mod.rs` — remove `mod pool` and any `use pool::` references
- `plans/adrs/PLAN_ADRS.md` — add ADR-004 documenting the pool-worker design
  decision and explaining why dynamic workers are the sole supported path

**Acceptance criteria:**
1. `cargo build` with no new warnings.
2. `just test-unit` passes.

---

### TEST-003: Scheduler-Driven Fused Refresh E2E

**Files changed:**
- `tests/e2e_refresh_tests.rs` — new
  `test_fused_refresh_scheduler_audit_trail`: wait for scheduler dispatch,
  query `pgt_refresh_history`, assert valid `initiated_by`/`merge_strategy`

---

### TEST-004: Durability Mode E2E Tests

**Files changed:**
- `tests/e2e_unlogged_buffer_tests.rs` — three new test functions

---

## Upgrade Notes

```sql
-- Run the upgrade migration:
ALTER EXTENSION pg_trickle UPDATE TO '0.68.0';

-- Migrate any application code that mirrors the old CHECK constraint:
-- Old: CHECK (initiated_by IN ('SCHEDULER','MANUAL','INITIAL','SELF_MONITOR'))
-- New: CHECK (initiated_by IN ('SCHEDULER','MANUAL','INITIAL','SELF_MONITOR','SCHEDULER_FUSED'))
```

The `pg_trickle.unlogged_buffers` GUC continues to work and is still valid; the
only visible change is a `WARNING` message in the PostgreSQL log advising
migration to `pg_trickle.change_buffer_durability`. No action is required before
v1.0.

---

## Implementation Status

| ID | Title | Status |
|----|-------|--------|
| COR-001 | Fused refresh audit trail — add `SCHEDULER_FUSED` to CHECK constraint | ✅ Done |
| COR-003 / ARCH-001 | `change_buffer_durability` GUC wired into CDC buffer creation | ✅ Done |
| COR-004 | DuckLake timestamp NULL fix — microsecond-epoch serialisation | ✅ Done |
| SCAL-001 | Pool path deleted (`pool.rs` removed, ADR-024 written) | ✅ Done |
| TEST-003 | Scheduler-driven fused refresh E2E audit trail test | ✅ Done |
| TEST-004 | Durability mode E2E tests (unlogged / logged / legacy shim) | ✅ Done |

---

## Exit Criteria

- [x] All P0 items ✅ Done
- [x] `just test-unit` passes (2305 tests)
- [x] `just lint` passes with zero warnings
- [x] `just check-version-sync` exits 0
- [x] CHANGELOG.md entry written
- [x] ROADMAP.md v0.68.0 row marked ✅ Released
