Skip to content

fix(scripts): add recover-dev-db.sql for fork-branch migration mismatch#15

Closed
Benniphx wants to merge 1 commit into
42tg:mainfrom
Benniphx:fix/recover-dev-db-from-fork-migrations
Closed

fix(scripts): add recover-dev-db.sql for fork-branch migration mismatch#15
Benniphx wants to merge 1 commit into
42tg:mainfrom
Benniphx:fix/recover-dev-db-from-fork-migrations

Conversation

@Benniphx
Copy link
Copy Markdown
Collaborator

Problem

Local dev DBs that were grown on a fork branch can fail to start bun run dev after switching back to main:

SQLiteError: no such column: default_model_selection_json
ProviderSessionDirectoryPersistenceError: Unknown persisted provider 'claudeCode'.

The Effect-SQL migrator tracks migrations by numeric ID only. If a DB has IDs 14–18 recorded from a fork timeline that used different migrations under those IDs, main's 14–18 are silently skipped — leaving the schema half-converted.

Concrete divergence I hit (Tobias' fork → main):

ID Fork (recorded in DB) main (never runs)
14 ProjectionThreadActivityParentAndItemId ProjectionThreadProposedPlanImplementation
15 ProjectionThreadsJiraTicket ProjectionTurnsSourceProposedPlan
16 ReviewComments CanonicalizeModelSelections (the painful one)
17 ReviewRequests ProjectionThreadsArchivedAt
18 ReviewRequestsPrMeta ProjectionThreadsArchivedAtIndex

Plus the claudeCodeclaudeAgent provider rename was code-only, so legacy rows still reference claudeCode.

What this PR does

Adds scripts/recover-dev-db.sql — a single-transaction recovery script that:

  1. Renames claudeCodeclaudeAgent in provider_session_runtime (provider_name + adapter_key), projection_thread_sessions.provider_name, and orchestration_events.payload_json.
  2. Applies the missing schema deltas for main migrations 14–18:
    • 14 projection_thread_proposed_plans: implemented_at, implementation_thread_id
    • 15 projection_turns: source_proposed_plan_thread_id, source_proposed_plan_id
    • 16 CanonicalizeModelSelections — adds default_model_selection_json (projects), model_selection_json (threads), drops default_model/model, and ports orchestration_events payloads (project.created, project.meta-updated, thread.created, thread.meta-updated, thread.turn-start-requested) to the new modelSelection shape.
    • 17 projection_threads.archived_at
    • 18 idx_projection_threads_project_archived_at
  3. Does not touch effect_sql_migrations — IDs 14–21 are already recorded as applied, so no migration will be re-run.

Recommended path: keep your data (Option B)

This is the path teammates should default to — wiping the DB loses all local sessions.

# 1. Stop the dev server
# 2. Backup
cp ~/.t3/dev/state.sqlite ~/.t3/dev/state.sqlite.bak-$(date +%Y%m%d-%H%M%S)
# 3. Apply
sqlite3 ~/.t3/dev/state.sqlite < scripts/recover-dev-db.sql
# 4. Start
bun run dev

Fallback only: nuke and re-create (Option A)

Use this only if Option B somehow doesn't work for you. You will lose all local dev sessions.

TS=$(date +%Y%m%d-%H%M%S)
mv ~/.t3/dev/state.sqlite     ~/.t3/dev/state.sqlite.bak-$TS
mv ~/.t3/dev/state.sqlite-shm ~/.t3/dev/state.sqlite-shm.bak-$TS 2>/dev/null
mv ~/.t3/dev/state.sqlite-wal ~/.t3/dev/state.sqlite-wal.bak-$TS 2>/dev/null
bun run dev

Verification

After running the script:

sqlite3 ~/.t3/dev/state.sqlite "
  SELECT 'default_model_selection_json:', COUNT(*) FROM pragma_table_info('projection_projects') WHERE name='default_model_selection_json'
  UNION ALL SELECT 'model_selection_json:', COUNT(*) FROM pragma_table_info('projection_threads')  WHERE name='model_selection_json'
  UNION ALL SELECT 'archived_at:',          COUNT(*) FROM pragma_table_info('projection_threads')  WHERE name='archived_at'
  UNION ALL SELECT 'claudeCode rest:',      COUNT(*) FROM provider_session_runtime WHERE provider_name='claudeCode';
"

First three should be 1, last should be 0. Tested locally on a real fork-grown DB (~3.7 MB, ~200 events with claudeCode references) — all checks green, dev server starts cleanly, sessions intact.

Out of scope (but worth a follow-up)

The root cause — migrator keying only on ID — is what makes this class of break possible. A future change could verify recorded migration names match expected ones at startup and warn or fail fast.

Local dev DBs that were built up on a fork branch can have migration
IDs 14-18 recorded that map to different migrations than the ones on
main. The Effect-SQL migrator only tracks numeric IDs, so main's
migrations 14-18 never run on those DBs, producing errors like:

  SQLiteError: no such column: default_model_selection_json
  ProviderSessionDirectoryPersistenceError: Unknown persisted provider
    'claudeCode'.

This script applies the missing schema deltas (migrations 14-18) and
renames the legacy 'claudeCode' provider to 'claudeAgent', without
touching effect_sql_migrations.

Usage:
  cp ~/.t3/dev/state.sqlite ~/.t3/dev/state.sqlite.bak
  sqlite3 ~/.t3/dev/state.sqlite < scripts/recover-dev-db.sql
@github-actions github-actions Bot added size:L vouch:unvouched PR author is not yet trusted in the VOUCHED list. labels Apr 13, 2026
@Benniphx
Copy link
Copy Markdown
Collaborator Author

Closing in favor of a same-repo PR — got WRITE access in the meantime.

@Benniphx Benniphx closed this Apr 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant