| 1 | # Legacy orchestration migration |
| 2 | |
| 3 | Orchestration v2 snapshots `state.sqlite` into `statev2.sqlite` before opening writable persistence |
| 4 | on its first launch. Only the copy receives v2 migrations; the original remains available to v1. |
| 5 | Subsequent launches reuse the copy without refreshing it from v1. It creates v2 thread shell events first and imports |
| 6 | the complete user and assistant transcript lazily when a client reads or continues the thread. The |
| 7 | v1 projection tables remain the import source and provide a read-only recovery source if an import |
| 8 | needs investigation. |
| 9 | |
| 10 | ## Imported data |
| 11 | |
| 12 | The shell import preserves the project and thread identifiers, title, provider and model selection, |
| 13 | runtime and interaction modes, branch, worktree path, creation and update times, archive and delete |
| 14 | times, settlement override and timestamps, snooze timestamps, pin timestamp and order, and linked |
| 15 | pull request. The metadata repair path fills snooze, pin order, `unsettledAt`, and linked pull request |
| 16 | fields for threads imported before those fields were covered. |
| 17 | |
| 18 | Transcript import reads user and assistant rows from `projection_thread_messages`. It preserves |
| 19 | message identifiers, text, supported attachments, timestamps, role, and ordering. A message that was |
| 20 | still streaming becomes an interrupted turn item. |
| 21 | |
| 22 | The importer does not translate provider session identity, native provider runs, checkpoints and |
| 23 | diffs, activities and tool calls, approvals, or proposed plans. V2 therefore must not present those |
| 24 | records as migrated history. |
| 25 | |
| 26 | ## First continuation |
| 27 | |
| 28 | A migrated thread has no active provider thread. Its first continuation creates a fresh provider |
| 29 | session and sends a legacy handoff built only from user and assistant messages. The handoff selects |
| 30 | the newest transcript suffix within a 32,000-character budget, including section labels and the |
| 31 | import notice. This budget is separate from portable provider handoffs. |
| 32 | |
| 33 | ## Client and server cutover |
| 34 | |
| 35 | Clients and servers must agree on `ORCHESTRATION_PROTOCOL_VERSION` (currently 2). The client |
| 36 | runtime appends `orchestrationProtocol=2` to the socket URL, and the `/ws` route rejects a missing |
| 37 | or mismatched version with HTTP 426 (`orchestration_protocol_incompatible`) before any RPC or auth |
| 38 | work runs. The client checks the environment descriptor the same way: a missing version means the |
| 39 | host predates protocol 2, and a different version means both sides need updating. Either direction |
| 40 | blocks the connection as `unsupported` with a message naming the machine to update rather than |
| 41 | running half-upgraded. See `packages/client-runtime/src/connection/compatibility.ts` and |
| 42 | `apps/server/src/ws.ts`. |
| 43 | |
| 44 | ## Divergent migration ids |
| 45 | |
| 46 | `effect_sql_migrations` records `migration_id` and `name`, but the migrator compares ids only: |
| 47 | rows at or below the recorded maximum are skipped without checking names. A database that ran a |
| 48 | local or fork migration under an id this build later assigns to a different migration therefore |
| 49 | never receives this build's migration at that id. After repairing supported V2 preview ledgers, |
| 50 | [`runMigrations`](../../apps/server/src/persistence/Migrations.ts) rejects history that would skip a |
| 51 | requested migration at id 55 or later without its expected name. Earlier name clashes remain |
| 52 | warnings for compatibility with site-local migrations. There is no safe id range for a fork inside |
| 53 | this ledger: any id at or below a future upstream id masks it forever, so fork schema changes belong |
| 54 | in a separate migration table or outside the migrator entirely. |
| 55 | |
| 56 | ## Recovery |
| 57 | |
| 58 | There is no supported whole-thread export API. Recovery uses an untouched copy of the environment's |
| 59 | `userdata` directory and opens that copy with SQLite's read-only mode. The user guide documents the |
| 60 | queries against `projection_threads` and `projection_thread_messages`. Never start a server against |
| 61 | the recovery copy because startup can run migrations and write new state. |