Summary
Severity: high — silent bookkeeping drift.
When a pending EQL config contains more than one renamed column on a single table, `stash encrypt cutover --column X` physically renames all of the renamed columns (because `eql_v2.rename_encrypted_columns()` operates on the entire pending config in one go), but only emits a `cut_over` event in `cs_migrations` for the column named on the CLI. The other columns are renamed in the database but their lifecycle bookkeeping is left at `backfilled`.
Repro
- Register two encrypted twins on the same table (e.g. `users.email` and `users.first_name`).
- Backfill both → both reach phase `backfilled`.
- Update the schema to rename both encrypted columns into the primary names; `stash db push` writes the new shape as pending.
- `stash encrypt cutover --table users --column email`.
Expected: both `email` and `first_name` are physically renamed AND both have a `cut_over` event in `cs_migrations`.
Actual: both are physically renamed, the pending config is promoted to active, but only `email` has a `cut_over` row in `cs_migrations`. `first_name` stays at `backfilled`.
Downstream symptoms
- `stash encrypt cutover --column first_name` fails with "No pending EQL configuration" (the pending was already promoted).
- `stash encrypt drop --column first_name` refuses because `cs_migrations` still says `backfilled`.
- The only workaround today is a manual `INSERT INTO cipherstash.cs_migrations` for the orphaned column.
Surfaced by the 2026-05-04 spike.
Root cause
`packages/cli/src/commands/encrypt/cutover.ts:99-108` calls `renameEncryptedColumns(client)` and then `appendEvent` for the single `{tableName, columnName}` pair from the CLI flags. The rename is config-wide; the event log entry is per-column.
Proposed fix (recommended)
After `renameEncryptedColumns()` runs, walk the just-promoted pending config and emit a `cut_over` event for every column whose `_encrypted` sibling was renamed — not just the one named on the CLI.
Concretely:
- Before the `BEGIN` block, snapshot the pending config's column set.
- After `renameEncryptedColumns` / `migrateConfig` / `activateConfig`, walk the pending columns and `appendEvent` for each.
- Validate that the explicitly-named `--column` is in that set; if not, fail loudly (current behaviour for an unknown column should be preserved).
Side benefit: lets users batch multi-column cutovers with `stash encrypt cutover --table T` and no `--column` flag (cuts over every pending column on that table).
Alternatives considered
- Per-column rename: change `rename_encrypted_columns()` (or add a per-column variant in EQL) so cutover stays one-column-at-a-time. Bigger surface — touches EQL.
- Refuse multi-column pending: `cutover` errors out unless `--all-pending` is passed. Conservative; introduces a new flag and a new failure mode.
The recommended fix matches the apparent intent of the existing code without a new flag or an EQL change.
Acceptance criteria
Related
- Followups doc §3.6 — integration tests for the new pending/active flow (this fix should ship a new test in that suite).
Summary
Severity: high — silent bookkeeping drift.
When a pending EQL config contains more than one renamed column on a single table, `stash encrypt cutover --column X` physically renames all of the renamed columns (because `eql_v2.rename_encrypted_columns()` operates on the entire pending config in one go), but only emits a `cut_over` event in `cs_migrations` for the column named on the CLI. The other columns are renamed in the database but their lifecycle bookkeeping is left at `backfilled`.
Repro
Expected: both `email` and `first_name` are physically renamed AND both have a `cut_over` event in `cs_migrations`.
Actual: both are physically renamed, the pending config is promoted to active, but only `email` has a `cut_over` row in `cs_migrations`. `first_name` stays at `backfilled`.
Downstream symptoms
Surfaced by the 2026-05-04 spike.
Root cause
`packages/cli/src/commands/encrypt/cutover.ts:99-108` calls `renameEncryptedColumns(client)` and then `appendEvent` for the single `{tableName, columnName}` pair from the CLI flags. The rename is config-wide; the event log entry is per-column.
Proposed fix (recommended)
After `renameEncryptedColumns()` runs, walk the just-promoted pending config and emit a `cut_over` event for every column whose `_encrypted` sibling was renamed — not just the one named on the CLI.
Concretely:
Side benefit: lets users batch multi-column cutovers with `stash encrypt cutover --table T` and no `--column` flag (cuts over every pending column on that table).
Alternatives considered
The recommended fix matches the apparent intent of the existing code without a new flag or an EQL change.
Acceptance criteria
Related