Skip to content

feat(desktop): v1→v2 terminal migration — lazy recreation at the v1 cwd on workspace open - #5818

Merged
Kitenite merged 6 commits into
mainfrom
v1-migration-terminals
Jul 21, 2026
Merged

Kitenite merged 6 commits into
mainfrom
v1-migration-terminals

Conversation

@Kitenite

@Kitenite Kitenite commented Jul 21, 2026 •

Copy link
Copy Markdown
Member

Summary

PR 3 of the v1→v2 auto-migration (SUPER-1534 / SUPER-1577, decision D2: lazy terminal recreation, no boot-time PTY storm). Live v1 PTYs can't hand off (separate daemon owners), so the contract is a fresh shell at the persisted v1 cwd, in the right workspace.

  • migration.readV1TerminalPanes: first read of app-state.json by the migration router — resolves each v1 terminal pane to its workspace (via tab) and best-known cwd (cwd ?? initialCwd).
  • migrateTerminals stage in runV1Migration() (after settings, before presets): assigns each pane a fresh v2 terminal id (v1 pane ids double as v1 daemon session ids — reusing them risks colliding with the live v1 daemon; idempotence comes from the ledger, kind terminal, v1Id = paneId) and queues {terminalId, cwd} per mapped workspace via an injected terminalTarget. Panes on unmigrated workspaces defer to the next run. Never gates the flip.
  • pendingMigratedTerminals field on v2WorkspaceLocalState (healed like the other optional fields) + appendPendingMigratedTerminals helper that upserts the row (adopted-but-never-opened workspaces have none yet).
  • useCreatePendingMigratedTerminals hook on the v2 workspace page: on first open (layout-ready), creates the queued sessions via the existing idempotent launcher, removes created entries (failures stay queued), and invalidates terminal.listSessions so feat(desktop): auto-create panes for background terminal sessions on workspace open #5740's useAutoAdoptBackgroundSessions builds the panes immediately.

Dormant until the PR 4 boot trigger wires terminalTarget — the page hook no-ops on an empty queue for all existing users.

Test Plan

  • bun test src/renderer/lib/v1-migration/ — 21 tests incl. planTerminalMigration (fresh-id assignment, per-workspace grouping, unmapped deferral)
  • Desktop typecheck + root lint clean
  • CDP E2E once PR 4 wires the runner (v1 terminal panes → sessions at cwd → auto-adopted panes)

Summary by cubic

Adds v1→v2 terminal migration with lazy recreation: on first open of a migrated workspace, we spawn fresh v2 terminals at the v1 cwd instead of at boot to avoid a PTY storm. Aligns with Linear SUPER-1534 / SUPER-1577 (D2).

  • New Features
    • Migration router exposes readV1TerminalPanes to read v1 panes from app-state.json, mapped to workspace and cwd.
    • New migrateTerminals stage: assigns fresh v2 terminal IDs, records a terminal ledger entry per v1 pane, and queues {terminalId, cwd} per migrated workspace via an injected terminalTarget. Unmapped panes defer; never gates the flip.
    • Adds pendingMigratedTerminals to v2 workspace local state with an append helper; healed by schema defaults.
    • New useCreatePendingMigratedTerminals hook: on first workspace open, creates queued sessions via the idempotent launcher, prunes created entries, and invalidates terminal.listSessions so auto-adopt builds panes.
    • Tests cover terminal planning (fresh ID assignment, per-workspace grouping, unmapped deferral). Feature remains dormant until PR 4 wires terminalTarget.

Written for commit d0def35. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features
    • Migrating from v1 to v2 now includes terminal panes, preserving their workspace assignments and working directories where available.
    • Migrated terminals are queued and recreated automatically when the corresponding v2 workspace is opened.
    • Terminal migration progress is tracked, including deferred items and migration failures.
    • Duplicate terminal creation is prevented, and failed creations remain available for retry.
  • Tests
    • Added coverage for mapped and unmapped workspace terminal migrations.

@coderabbitai

coderabbitai Bot commented Jul 21, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 391fbe7d-48fb-43bc-9dd2-6f48b7668885

📥 Commits

Reviewing files that changed from the base of the PR and between 81e9745 and d0def35.

📒 Files selected for processing (10)
  • apps/desktop/src/lib/trpc/routers/migration/index.ts
  • apps/desktop/src/renderer/lib/v1-migration/index.ts
  • apps/desktop/src/renderer/lib/v1-migration/runV1Migration.ts
  • apps/desktop/src/renderer/lib/v1-migration/terminals.ts
  • apps/desktop/src/renderer/lib/v1-migration/v1-migration.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/useCreatePendingMigratedTerminals/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/useCreatePendingMigratedTerminals/useCreatePendingMigratedTerminals.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/page.tsx
  • apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/dashboardSidebarLocal/schema.ts
  • apps/desktop/src/renderer/stores/workspace-creates/appendPendingMigratedTerminals.ts

📝 Walkthrough

Walkthrough

Changes

Terminal Migration

Layer / File(s) Summary
Pane discovery and migration planning
apps/desktop/src/lib/trpc/routers/migration/index.ts, apps/desktop/src/renderer/lib/v1-migration/terminals.ts, apps/desktop/src/renderer/lib/v1-migration/index.ts, apps/desktop/src/renderer/lib/v1-migration/v1-migration.test.ts
Reads v1 terminal panes, maps them to workspaces, assigns new terminal IDs, and defers unmapped panes.
Migration pipeline stage
apps/desktop/src/renderer/lib/v1-migration/runV1Migration.ts
Adds terminal migration after settings, queues pending sessions, records ledger outcomes, and returns terminal results.
Pending terminal persistence
apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/dashboardSidebarLocal/schema.ts, apps/desktop/src/renderer/stores/workspace-creates/appendPendingMigratedTerminals.ts
Adds the persisted pending-terminal queue with healing, initialization, and terminal-ID deduplication.
Lazy session creation
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/useCreatePendingMigratedTerminals/*, apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/page.tsx
Creates queued terminal sessions after layout readiness, removes successful entries, and refreshes terminal sessions.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant appState
  participant MigrationRunner
  participant WorkspaceState
  participant WorkspacePage
  participant TerminalLauncher
  appState->>MigrationRunner: read mapped v1 terminal panes
  MigrationRunner->>WorkspaceState: append pending migrated terminals
  WorkspacePage->>TerminalLauncher: create queued sessions after layout readiness
  TerminalLauncher-->>WorkspacePage: return session creation results
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise, conventional, and accurately summarizes the main terminal migration change.
Description check ✅ Passed The description covers the change, rationale, testing, and checklist, though it uses custom headings instead of the exact template.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch v1-migration-terminals

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds lazy recreation of v1 terminals when a migrated v2 workspace opens. The main changes are:

  • Reads v1 terminal panes and their best-known working directories.
  • Assigns fresh terminal IDs and queues them by migrated workspace.
  • Persists pending terminals in workspace-local state.
  • Creates queued sessions on workspace open and triggers pane adoption.

Confidence Score: 4/5

The terminal recovery and queue-consumption paths need fixes before merging.

  • A process exit between queue persistence and ledger flush can duplicate migrated terminals.
  • Queue updates do not trigger the creation effect, so late entries can remain pending.
  • A rejected invalidation can leave the runner locked for the mounted workspace.

apps/desktop/src/renderer/lib/v1-migration/runV1Migration.ts; apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/useCreatePendingMigratedTerminals/useCreatePendingMigratedTerminals.ts

Important Files Changed

Filename Overview
apps/desktop/src/lib/trpc/routers/migration/index.ts Adds a main-process query that maps v1 terminal panes to workspaces and selects the best available cwd.
apps/desktop/src/renderer/lib/v1-migration/runV1Migration.ts Adds terminal migration and ledger reporting, but queue persistence can get ahead of the ledger and duplicate terminals after recovery.
apps/desktop/src/renderer/lib/v1-migration/terminals.ts Adds planning for fresh terminal IDs, workspace grouping, and deferral of unmapped panes.
apps/desktop/src/renderer/stores/workspace-creates/appendPendingMigratedTerminals.ts Creates missing local-state rows and appends pending terminals, with deduplication limited to terminal IDs.
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/useCreatePendingMigratedTerminals/useCreatePendingMigratedTerminals.ts Creates queued sessions and triggers adoption, but does not react to queue updates and can leave its runner locked after invalidation failure.
apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/dashboardSidebarLocal/schema.ts Adds the pending-terminal queue to workspace-local state with healing for existing rows.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant M as v1 Migration
    participant Q as Local Terminal Queue
    participant L as Migration Ledger
    participant W as v2 Workspace Page
    participant T as Terminal Service

    M->>M: Read panes and assign fresh IDs
    M->>Q: Append pending terminals
    M->>L: Flush successful mappings
    Note over Q,L: Exit between writes leaves queue ahead of ledger
    W->>Q: Read queue when layout is ready
    loop Pending terminals
        W->>T: Create session at saved cwd
    end
    W->>Q: Remove created entries
    W->>T: Invalidate session list for adoption
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant M as v1 Migration
    participant Q as Local Terminal Queue
    participant L as Migration Ledger
    participant W as v2 Workspace Page
    participant T as Terminal Service

    M->>M: Read panes and assign fresh IDs
    M->>Q: Append pending terminals
    M->>L: Flush successful mappings
    Note over Q,L: Exit between writes leaves queue ahead of ledger
    W->>Q: Read queue when layout is ready
    loop Pending terminals
        W->>T: Create session at saved cwd
    end
    W->>Q: Remove created entries
    W->>T: Invalidate session list for adoption
Loading
Prompt To Fix All With AI
Fix the following 3 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 3
apps/desktop/src/renderer/lib/v1-migration/runV1Migration.ts:550
**Queue And Ledger Diverge**

If the renderer exits after `appendPending` persists the queue but before the success outcomes are flushed, the next run assigns new UUIDs to the same v1 panes. The queue deduplicates only by `terminalId`, so both sets remain and opening the workspace creates duplicate shells and panes.

### Issue 2 of 3
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/useCreatePendingMigratedTerminals/useCreatePendingMigratedTerminals.ts:28-30
**Late Queue Entries Stay Pending**

The effect reads the queue once, but collection changes are not dependencies. If migration appends terminals after the layout-ready effect has read an empty queue, or while an existing batch is running, those entries do not trigger another pass and remain pending until the workspace is reopened.

### Issue 3 of 3
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/useCreatePendingMigratedTerminals/useCreatePendingMigratedTerminals.ts:61-63
**Invalidation Failure Locks Runner**

If session-list invalidation rejects after sessions are created, control never reaches the `runningRef.current = false` assignment. The hook then rejects every later effect run for this mounted workspace, so any terminals queued afterward cannot be created until the page remounts.

Reviews (1): Last reviewed commit: "feat(desktop): v1 terminal migration — l..." | Re-trigger Greptile

if (!projectId) {
throw new Error("workspace missing from host list");
}
target.appendPending({ id: v2WorkspaceId, projectId }, terminals);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Queue And Ledger Diverge

If the renderer exits after appendPending persists the queue but before the success outcomes are flushed, the next run assigns new UUIDs to the same v1 panes. The queue deduplicates only by terminalId, so both sets remain and opening the workspace creates duplicate shells and panes.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/lib/v1-migration/runV1Migration.ts
Line: 550

Comment:
**Queue And Ledger Diverge**

If the renderer exits after `appendPending` persists the queue but before the success outcomes are flushed, the next run assigns new UUIDs to the same v1 panes. The queue deduplicates only by `terminalId`, so both sets remain and opening the workspace creates duplicate shells and panes.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +28 to +30
const pending =
collections.v2WorkspaceLocalState.get(workspaceId)
?.pendingMigratedTerminals ?? [];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Late Queue Entries Stay Pending

The effect reads the queue once, but collection changes are not dependencies. If migration appends terminals after the layout-ready effect has read an empty queue, or while an existing batch is running, those entries do not trigger another pass and remain pending until the workspace is reopened.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/useCreatePendingMigratedTerminals/useCreatePendingMigratedTerminals.ts
Line: 28-30

Comment:
**Late Queue Entries Stay Pending**

The effect reads the queue once, but collection changes are not dependencies. If migration appends terminals after the layout-ready effect has read an empty queue, or while an existing batch is running, those entries do not trigger another pass and remain pending until the workspace is reopened.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +61 to +63
await utils.terminal.listSessions.invalidate({ workspaceId });
}
runningRef.current = false;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Invalidation Failure Locks Runner

If session-list invalidation rejects after sessions are created, control never reaches the runningRef.current = false assignment. The hook then rejects every later effect run for this mounted workspace, so any terminals queued afterward cannot be created until the page remounts.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/useCreatePendingMigratedTerminals/useCreatePendingMigratedTerminals.ts
Line: 61-63

Comment:
**Invalidation Failure Locks Runner**

If session-list invalidation rejects after sessions are created, control never reaches the `runningRef.current = false` assignment. The hook then rejects every later effect run for this mounted workspace, so any terminals queued afterward cannot be created until the page remounts.

How can I resolve this? If you propose a fix, please make it concise.

@Kitenite

Copy link
Copy Markdown
Member Author

CDP E2E verified: seeded a v1 terminal pane (cwd = the demo worktree) in app-state.json → runV1Migration({terminalTarget}) read it via readV1TerminalPanes, mapped it through the ledger to the v2 workspace (correct id+projectId), minted a fresh terminal id, recorded terminal → success in the ledger → queued in pendingMigratedTerminals → on opening the workspace the hook created the session and cleared the queue (pendingLeft 0), auto-adopt built the pane. Screenshot shows the terminal open in Feat Demo with the zsh prompt at …/v1-demo/feat-demo on branch feat-demo with !1 dirty marker; lsof -a -d cwd confirms the shell pid's cwd is exactly the v1 worktree path.

@Kitenite
Kitenite merged commit 2f98c6a into main Jul 21, 2026
11 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ⚠️ Neon database branch

Thank you for your contribution! 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant