Skip to content

fix(server): move fork migrations to their own ledger - #48

Merged
asfires merged 2 commits into
mainfrom
t3code/fork-migration-ledger
Aug 18, 2026
Merged

fix(server): move fork migrations to their own ledger#48
asfires merged 2 commits into
mainfrom
t3code/fork-migration-ledger

Conversation

@asfires

@asfires asfires commented Aug 18, 2026

Copy link
Copy Markdown
Owner

Problem

The six fork-only migrations were numbered 041-046 in upstream's sequence. The Effect Migrator is a high-water mark: it runs every loaded migration with an id above the largest id already in effect_sql_migrations. So the next upstream migration at 041 is either skipped as already applied or crashes on tables that do not exist at the following sync (this already happened once). Upstream's pending orchestrator rewrite (pingdotgg/t3code#2829) ships 041-049. Renumbering the fork's into a high range would be worse: the mark would sit above every future upstream migration.

Fix

  • Fork migrations move to apps/server/src/persistence/ForkMigrations/ with ids 1-6 and their own ledger table effect_sql_migrations_fork, run through a second Migrator by runAllMigrations after upstream's. Migrations.ts is byte-identical to upstream again.
  • First boot on this code moves the six legacy rows out of upstream's ledger into the fork ledger, matched by id and name and keeping created_at. Nothing re-runs; upstream's high-water mark drops back to 040.
  • Sqlite.ts boot and migrate-dev-db (which mirrors boot) call runAllMigrations. The migration tests move alongside and target the fork ledger; a new ForkMigrations.test.ts covers fresh DBs, pre-split DBs (adoption without re-run, timestamps preserved, projections untouched), and an upstream row that reuses a legacy fork number.
  • One bullet in the fork-local section of AGENTS.md so future threads do not put fork migrations back into Migrations.ts.

Verification

  • vp test run on src/persistence/, ProjectionSnapshotQuery, TurnRetractionReactor, ProjectionPipeline, ProviderSessionDirectory, scripts/migrate-dev-db: 28 files, 110 tests green. Server typecheck and lint clean on the touched scope.
  • Against a VACUUM INTO snapshot of the real database (legacy rows 41-46, cursor 47081, 78 threads): dev server boots in ~2s, upstream ledger ends at 40, fork ledger holds 1-6 with the original timestamps, projection cursor and thread count unchanged, no rebuild.

Model: Claude Fable 5 via Claude Code.

The six fork-only migrations were numbered 041-046 in upstream's sequence.
The Effect Migrator is a high-water mark (runs ids above the largest applied
id), so the next upstream migration at 041 would either be skipped as already
applied or crash on missing tables at the following sync; upstream's pending
orchestrator rewrite ships 041-049. Renumbering into a high range would be
worse, parking the mark above every future upstream migration.

Fork migrations now live in persistence/ForkMigrations/ with ids 1-6 and their
own ledger table (effect_sql_migrations_fork), run by runAllMigrations after
upstream's. On first boot the six legacy rows are moved out of upstream's
ledger by id and name, so nothing re-runs and upstream's mark drops back to
040. Migrations.ts is upstream-identical again; migrate-dev-db mirrors boot.

Verified against a snapshot of the real database: boot adopts the rows in
place, no projection rebuild, cursor and thread count unchanged.

Model: Claude Fable 5 via Claude Code.
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L labels Aug 18, 2026
…lots

Adoption inserted with OR IGNORE and deleted the legacy row unconditionally,
so a fork ledger already holding that slot under another name would drop the
only record that the migration ran. It now fails with a BadState
MigrationError inside the transaction, leaving every legacy row in place.

migrate-dev-db's slot-collision check now covers the fork ledger as well as
upstream's, with the ledger named in the error.

Findings from an advisory GPT-5.6 Sol review of the PR.

Model: Claude Fable 5 via Claude Code.
@asfires
asfires merged commit b9273dc into main Aug 18, 2026
7 of 11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant