A real migration · Postgres 13 → 16

Six weeks, twenty-three branches, one timeline.

Mapped from a real beta workspace. Names changed.

w1
w2
w3
w4
w5
w6
Audit + plan@rena
complete
Replica setup@marko
complete
JSONB path migrationclaude
complete
pgvector upgradecodex
complete
Index rebuild · prodcursor + on-call
in progress
Cutover@rena
queued
Decommission v13@marko
queued
Pinned constraints

Things you only learn at 2am.

The hard-won gotchas of a long migration. Statefulai pins them as procedural rules and resurfaces them at every relevant turn — even six weeks later, in a different branch, by a different agent.

discovered · w2 thu 02:11 Avoid ->>'$.path' on large JSONB columns; v16 still seq-scans without a partial expression index. Found during w2 perf test. Killed throughput on the events table. ▸ rule · @no-jsonb-path-on-events · used 14×
discovered · w3 mon pgvector ≥ 0.6 required — older versions silently drop nullable indexes during dump/restore. Caught by a missing-row count during shadow restore. ▸ rule · @pgvector-min-version · used 9×
discovered · w3 sat 23:48 Set maintenance_work_mem = 2GB per session before REINDEX CONCURRENTLY, then reset. Default 64MB makes large indexes take 40× longer. ▸ rule · @reindex-memory · used 6×
discovered · w4 wed Connection pool needs min_pool_size raised to 20 during cutover; v16 prepared-statement cache warms slowly. First minute of cutover spiked p99 to 3s without this. ▸ rule · @pool-warm-cutover · used 3×
discovered · w4 fri Always backfill created_at on the audit table before swapping the write target. Default got rewritten silently by ORM upgrade. ▸ rule · @audit-backfill-ts · used 2×
discovered · w5 tue Watch pg_stat_progress_create_index rather than waiting on the lock. Index rebuilds that look "stuck" are often progressing. Saved 90 minutes of unnecessary panic. ▸ rule · @watch-create-index · used 1×
None of these would survive a context window reset. They were learned by humans at uncomfortable hours, written nowhere except a Slack thread, and would have been re-discovered the hard way in week 5 — without Statefulai to pin them.
Branch-aware memory

Twenty-three branches, no cross-pollination.

A migration spawns a lot of throw-away branches. Statefulai tags every memory write with its branch and prefers same-branch hits at retrieval time — so the agent never tells you to do something that was true on a discarded branch three weeks ago.

main migration/replica-setup migration/jsonb-path-fix migration/pgvector-upgrade migration/index-rebuild (active) branches scoped · memory follows the diff
Picking it back up

Week 5, 11pm, on-call. Agent already knows the plan.

~ migration/index-rebuild · week 5
cold start
$ codex run "continue index rebuild for billing_events" \
    --with-context --branch migration/index-rebuild

↳ retrieved plan (6 weeks, 23 branches)
↳ retrieved 6 pinned constraints
↳ next step from plan: REINDEX CONCURRENTLY billing_events_idx_path
↳ applying pinned rules:
    @no-jsonb-path-on-events  → drop expression index first
    @reindex-memory           → SET maintenance_work_mem = 2GB
    @watch-create-index       → use pg_stat_progress for progress

ok dry-run passes · ready to execute · ETA 38m
plan continuity
100%
across 6 weeks
pinned rules
23
surfaced when relevant
re-derived gotchas
0
vs 6 historic mean
cold-start time
90s
to "ready to execute"
Multi-week · consistent

Ship the migration your agents won't forget halfway.

Get early accessRead the docs