# v0.87.17 - Versioned Row Identity V2 Hardening and Recreation

> **Status:** Implemented
> **Scope:** 9 person-weeks
> **User promise:** *"I can adopt exact row identities with a clear, repeatable rebuild and no silent compatibility trap."*
> **Blocked by:** [v0.87.16](v0.87.16.md)
> **Proposal:** [Exact, Versioned Row Identity V2](../proposals/PROPOSAL_VERSIONED_ROW_IDENTIFY_ENCODING.md)

## Theme

Turn the V2 implementation into a releasable pre-1.0 contract. Exercise all
producer and consumer paths together, finish the index and encoder performance
matrix, and make the intentional breaking upgrade operationally boring through
preflight checks and a documented stream-table recreation workflow. External
replication, outbox, DuckLake, and dbt consumers receive explicit resnapshot
requirements, while diagnostics and permissions acknowledge that canonical
identities can contain reversible source values.

There is no in-place V1/V2 migration protocol in this release. The supported
upgrade drops V1 stream-table state in reverse dependency order, preserves source
tables and data, installs the V2 extension, recreates the graph in dependency
order, and establishes a fresh V2 initial-refresh baseline.

## Scope allocation

| Work package | Person-weeks |
|---|---:|
| Cross-path correctness and failure hardening | 2.00 |
| Performance, capacity, and regression gates | 1.50 |
| Recreation preflight and operator workflow | 2.00 |
| External consumer and replication compatibility | 1.25 |
| Privacy, privileges, documentation, and examples | 1.00 |
| Release review and full CI closure | 1.25 |
| **Total** | **9.00** |

## Items

### ROWID-12: Prove cross-path V2 equivalence

Run the exact same source histories through trigger CDC, WAL CDC, FULL,
differential, and IMMEDIATE paths and compare complete table contents and
identity bytes with the v0.87.14 oracle. Cover joins, aggregates, set
operations, windows, keyless multiplicity, enum DDL invalidation, source
updates that change keys, and common-prefix identities.

**Dependencies:** v0.87.16; v0.87.14 correctness and replay gates.
**Schema change:** No.

### ROWID-13: Close performance and capacity gates

Complete the encoder, probe, index, refresh, and replication benchmark matrix.
Measure throughput, allocations, index size, buffer hits, WAL, encoded-ID
storage, change-buffer growth, cached and cold MERGE latency, and full old-row
cost for `REPLICA IDENTITY FULL`. Freeze only the simplest strategy that meets
correctness and capacity requirements; retain direct full-ID indexes for proven
bounded identities and do not ship speculative generated-column machinery.

**Dependencies:** v0.87.16 benchmark scaffolding and selected probe version.
**Schema change:** No.

### ROWID-14: Implement recreation preflight and workflow

Add a small fail-safe preflight that validates identity types, typmods,
collations, source-key eligibility, operator classes, PostgreSQL-major registry
contracts, index-size readiness, and remaining V1 stream tables or buffers.
The preflight is read-only: it may inspect catalogs and report blockers, but it
must not alter source data, stream-table state, indexes, buffers, grants, or
external objects. It must also require a recorded inventory and acknowledgment
of external consumers before teardown begins.
Provide an ordered procedure to pause scheduling, export definitions and
metadata, drop V1 stream tables in reverse dependency order, clean unused V1
buffers, install/restart the V2 binary, recreate stream tables in dependency
order, and perform fresh initial refreshes. Never change source data or
partially convert a stream table.

**Dependencies:** v0.87.16 version guards; existing lifecycle and catalog APIs.
**Schema change:** Yes.

### ROWID-15: Publish external consumer resnapshot contracts

Inventory and document the breaking `BYTEA` contract for logical replication,
publications, outbox consumers, DuckLake sinks, dbt models, and user SQL that
assumes numeric row IDs. Require consumer schema updates and resnapshot after
recreation; provide no numeric cast because V1 values do not contain the
original logical identity. The inventory must record the consumer owner,
affected stream tables, whether `__pgt_row_id` or storage layout is consumed,
required schema change, resnapshot status, and acknowledgment before V1 state
is dropped. Include pause/write-window guidance and state that writes during
recreation are not replayed by V2.

**Dependencies:** ROWID-14; existing publication, outbox, DuckLake, and dbt
integration contracts.
**Schema change:** No.

### ROWID-16: Harden identity privacy and diagnostics

Keep `__pgt_row_id` out of default user-facing views and grants unless explicit
ownership or privilege permits access. Logs, errors, traces, monitoring,
support bundles, and dump guidance expose length and a short diagnostic
fingerprint rather than complete bytes or reversible prefixes. Document that
pass-through and keyless identities may retain source or projected values and
must receive the same controls as those source columns.

**Dependencies:** v0.87.15 opaque-identity contract; v0.87.16 storage exposure.
**Schema change:** No.

### ROWID-17: Make V2 a release-gated contract

Add release checks for exact vectors, cross-path table equality, version
mismatch rejection, supported-major validation, direct/probe classification,
replication behavior, recreation preflight, external-consumer documentation,
privacy review, and benchmark regressions. Check that older binaries reject V2
catalog state and V2 binaries reject leftover V1 state. Keep the supported
matrix and the recreation runbook synchronized with generated SQL and package
artifacts. The gate must verify the checked-in wire-format artifact against the
encoder, the named initial structural support/rejection matrix, interval's
complete 128-bit comparison encoding, all source-key eligibility cases, the
authoritative uniqueness rule, and planning-time rejection of unique unbounded
`IMMEDIATE` identities.

**Dependencies:** ROWID-12 through ROWID-16; all prior correctness and lifecycle
security release gates.
**Schema change:** No.

## Correctness

| ID | Title | Effort | Priority |
|---|---|---:|:---:|
| CORR-8 | Compare every maintenance path | L | P0 |
| CORR-9 | Verify enum DDL transitions | M | P0 |
| CORR-10 | Protect recreation baselines | M | P0 |

**CORR-8** compares exact rows, multiplicities, identity bytes, and refresh
outcomes across all CDC and refresh modes. The oracle treats effective mode,
fallback, skipped work, and errors as part of the result rather than accepting a
row-count match alone.

**CORR-9** warms enum encoder caches, renames enum labels in another session,
checks DDL invalidation and syscache visibility, and verifies that dependent
stream tables are reinitialized before consuming new label bytes.

**CORR-10** proves that a failed or interrupted recreation leaves source data
unchanged and produces a repairable incomplete V2 graph. Recovery drops the
incomplete graph and reruns the documented procedure; it never attempts a
partial V1-to-V2 replay.

## Stability

| ID | Title | Effort | Priority |
|---|---|---:|:---:|
| STAB-7 | Make preflight non-destructive | M | P0 |
| STAB-8 | Handle restart and stale binaries | M | P0 |
| STAB-9 | Bound repair diagnostics | S | P1 |

**STAB-7** runs every unsupported-type, bad-collation, stale-buffer,
missing-relation, wrong-major, and insufficient-index-size check in a read-only
preflight before state mutation. It verifies that no catalog, relation, index,
buffer, grant, or external object changes, and that the report is complete
enough for an operator to repair all blockers in one pass.

**STAB-8** tests PostgreSQL restart after shared-library replacement, backend
reuse, scheduler pause/resume, old binary readers, and extension-update order.
Any V1/V2 ambiguity fails closed with no partial refresh or source mutation.

**STAB-9** keeps fingerprints, candidate counts, and identity lengths bounded in
logs and support artifacts. Error paths never dump complete identities by
accident, and cleanup remains idempotent after a failed recreation.

## Performance

| ID | Title | Effort | Priority |
|---|---|---:|:---:|
| PERF-7 | Gate identity regressions | M | P0 |
| PERF-8 | Measure replication cost | M | P1 |
| PERF-9 | Validate cold-index behavior | M | P1 |

**PERF-7** adds Criterion and PostgreSQL benchmark thresholds for encoder
throughput, allocations, CDC overhead, direct/probe lookup, and refresh latency.
Baselines are checked in CI and changes that exceed the agreed budget require an
explicit review.

**PERF-8** measures publication, WAL, network, and subscriber apply cost for
bounded unique identity indexes versus `REPLICA IDENTITY FULL`. Results become
part of operator guidance for unbounded and keyless schemas.

**PERF-9** runs MERGE and exact-recheck workloads with indexes larger than
`shared_buffers`, ordered wide values, random wide values, and long common
prefixes. It confirms the selected probe improves candidate lookup without
making correctness depend on digest behavior.

## Scalability

| ID | Title | Effort | Priority |
|---|---|---:|:---:|
| SCAL-7 | Rebuild large dependency graphs | M | P0 |
| SCAL-8 | Stress multi-consumer buffers | M | P1 |
| SCAL-9 | Test wide keyless workloads | M | P1 |

**SCAL-7** recreates deep and branching stream-table DAGs in dependency order,
checking bounded catalog work, scheduler pause behavior, cleanup, and fresh
initial-refresh capacity.

**SCAL-8** exercises one source buffer with many bounded and unbounded
consumers, concurrent refreshes, cleanup, and retries. Consumer index strategy
must not alter shared source identity bytes or cause cross-consumer state leaks.

**SCAL-9** tests large keyless rows, duplicate logical outputs, nested values,
and high change volume under the configured memory and disk budgets. The system
must report resource failures before data correctness is compromised.

## Ease of Use

| ID | Title | Effort | Priority |
|---|---|---:|:---:|
| UX-7 | Ship the recreation runbook | M | P0 |
| UX-8 | Provide consumer migration guidance | S | P0 |
| UX-9 | Document identity data handling | S | P0 |

**UX-7** provides a copyable operator sequence for pause, export, reverse-order
drop, V1 cleanup, binary installation and restart, dependency-order recreation,
initial refresh, consumer resnapshot, and resume. It states precisely which
source data is preserved and which writes must be paused.

**UX-8** gives publication, outbox, DuckLake, dbt, logical-replication, and
user-SQL examples for changing numeric storage to `BYTEA`, updating schemas, and
resnapshotting. It calls out that old numeric IDs cannot be cast into V2
identities.

**UX-9** warns that opaque does not mean secret. Documentation covers explicit
privileges, logs and support bundles, pass-through identities, keyless rows,
short fingerprints, dump handling, and source-column data classification.

## Test Coverage

| ID | Title | Effort | Priority |
|---|---|---:|:---:|
| TEST-8 | Build the cross-mode matrix | L | P0 |
| TEST-9 | Test destructive-window recovery | M | P0 |
| TEST-10 | Verify consumer resnapshot paths | M | P0 |
| TEST-11 | Enforce release artifacts | M | P0 |

**TEST-8** runs the full source-history matrix through trigger and WAL capture,
FULL, differential, and IMMEDIATE refresh, including all supported DVM operator
families, source-key changes, keyless duplicates, enum DDL, non-deterministic
collation rejection, every accepted/rejected source-index form, interval's
128-bit value, unique-unbounded `IMMEDIATE` rejection, and probe-shaped
collisions.

**TEST-9** injects failures before and after pause, export, drop, cleanup,
install, restart, recreation, initial refresh, and resume. It verifies source
preservation, idempotent cleanup, no mixed graph, and actionable repair state.

**TEST-10** uses representative publications, outbox consumers, DuckLake sinks,
and dbt models to check schema migration, old-ID rejection, resnapshot ordering,
and the documented write pause. No external consumer is allowed to infer a
numeric identity from V2 bytes. It verifies that the inventory and resnapshot
acknowledgment are recorded before V1 teardown.

**TEST-11** makes vectors, support matrices, generated SQL, docs links,
benchmark baselines, package contents, and upgrade guards part of CI. The
release cannot pass with stale V1 references or missing recreation instructions;
the wire artifact, spec-to-code check, structural support matrix, exact DML
predicate checks, and identity eligibility rules are release inputs rather than
untracked design notes.

## Conflicts & Risks

- The accepted upgrade is breaking and destructive only to stream-table state,
  never to source tables. Operators must acknowledge external resnapshot work
  and the write window before dropping V1 state.
- V2 does not replay writes made during recreation. This is deliberate and must
  remain prominent in the runbook rather than being hidden behind an upgrade
  helper.
- Unique unbounded `IMMEDIATE` mode remains rejected until a separate locking
  RFC proves concurrency and capacity behavior; no release gate may weaken that
  rule.
- Identity bytes can contain reversible source values. Privacy controls and
  default grants are part of correctness of the shipped feature, not optional
  documentation polish.
- The release changes no unrelated DVM semantics. Failures in the row-identity
  gate block v0.88.0 because vectorized aggregates and delta planning depend on
  exact matching.

## Exit criteria

- [x] All CDC and refresh paths agree with the exact oracle across the complete
      supported operator and identity matrix.
- [x] The checked-in wire-format artifact, encoder, identity/probe version
      values, structural support matrix, interval 128-bit rule, and source-key
      eligibility cases agree and are independently tested.
- [x] Encoder, probe, index, replication, and refresh benchmarks meet reviewed
      regression budgets.
- [x] Direct-index uniqueness is derived from validated identity semantics, and
      unique unbounded `IMMEDIATE` identities are rejected before mutation.
- [x] Non-destructive preflight and the pause/drop/recreate/resnapshot workflow
      are tested with injected failures and documented for operators; preflight
      is read-only and external-consumer acknowledgment precedes teardown.
- [x] External consumers, permissions, diagnostics, and privacy warnings match
      the `BYTEA` contract.
- [x] Older binaries reject V2 state, V2 binaries reject leftover V1 state, and
      no mixed graph can refresh.
- [x] Full unit, integration, light/full E2E, benchmark, documentation, package,
      and release CI gates pass.
