Skip to content

fix(ssh): link a folder workspace the server kept when a re-created SSH host moves again (#27285) - #30017

Merged
nwparker merged 3 commits into
mainfrom
nwparker/fix-27285-recreated-target
Oct 17, 2026
Merged

nwparker merged 3 commits into
mainfrom
nwparker/fix-27285-recreated-target

Conversation

@nwparker

@nwparker nwparker commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 2 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​252 $\color{#cf222e}{\Huge{\mathbf{−}}}$​30 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​222
Prod 9 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​275 $\color{#cf222e}{\Huge{\mathbf{−}}}$​63 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​212

ELI5

When you delete an SSH host in Orca and add it back, Orca gives it a new internal id. The next time you move that host onto its managed server, the server may still hold the projects and folder workspaces from the first move. Projects were already recognised as "the same thing the server already has" (#29586), but folder workspaces were not: the server refused the move with orcad_migration_folder_workspace_id_conflict as soon as the desktop's copy of a folder workspace differed in any way (a comment, an unread flag, its last-activity time). This PR makes folder workspaces follow the same rule as projects.

What Changed

Before: delete and re-add an SSH host that was moved once, use a folder workspace on it, then reconnect. The move fails with orcad_migration_stage_failed:orcad_migration_folder_workspace_id_conflict:<id> and keeps failing on every retry.

After: the move goes through. The server keeps its own copy of the folder workspace, which is newer because it has been in use there. This desktop's copy stays in its retained rows, as linked projects' copies already do, so a downgraded build still has it. The connect's "left behind on this desktop" list now names linked folder workspaces next to linked projects.

Mechanism:

  • Before committing a move, the desktop already reads the server's projects (repo.list) and links any it already holds. It now also reads the server's folder workspaces and project groups (folderWorkspace.list, projectGroup.list, both existing methods) and links a folder workspace only when the server holds the same id at the same folder and, using the existing resolveFolderWorkspaceHost resolver over the server's catalog, that folder is on the server's own machine. Anything else under that id (a different folder, or a folder the server reaches on another host) is still sent and still refused, so two different folders are never quietly merged.
  • Linked folder ids are journaled in a new optional linkedFolderWorkspaceIds field next to linkedRepositoryIds. The relinked manifest leaves them and their saved state out, and later delta moves never offer them again.
  • The relay-terminal guard now reads folder:<id> lease keys, so a bridged terminal in a linked folder blocks the link (naming the folder) and a terminal in a folder that is still moving no longer blocks it.
  • resolveFolderWorkspaceHost now accepts the narrower row shape a wire read returns. The types are widened only; its behaviour is unchanged.

About the report itself: the reporter's later error, orcad_migration_dormant_id_conflict:worktree_meta:<repo>::<path>, came from a desktop build (1.4.227 or earlier) that didn't yet have #29586. I reproduced it against a real orcad with same-id linking off, and current main already links that project and its saved state (see Testing). This PR closes the remaining folder-workspace version of the same failure.

Why

The server's copy of a row it already imported has been in live use since then, so it's the authoritative one. Re-importing over it would lose those edits, and refusing leaves the host stuck. Linking matches what #29586 already does for projects and groups, reuses the same journal, view and delta subtraction, and needs no new wire method: the desktop only calls list methods every migration-capable orcad already serves. Alternatives considered:

  • Accept a same-id folder on the server side whatever its contents. Rejected: it only helps once the host's orcad is updated, and it would also hide real collisions.
  • Compare only some fields (ignore lastActivityAt, isUnread and similar). Rejected: those aren't the only fields that change, and the server's newer copy would still be overwritten or refused.

Linked Issue

Fixes #27285

Visual Proof

N/A: no UI change. The existing connect notice gains folder entries in its list.

Testing

  • Unit, with real SQLite Store fixtures for the desktop and the server (orcad-migration-repository-link.test.ts):
    • Re-created target with a project and saved worktree state: links on main. This test passes on main and fails with the pre-fix(ssh): reconnecting a host after stopping its managed server no longer fails the move (#29560) #29586 behaviour, giving the reporter's exact dormant_id_conflict:worktree_meta error.
    • Re-created target with a folder workspace edited on both sides: before this PR it fails with folder_workspace_id_conflict. After, it converts, the server keeps its own copy, the journal records linkedFolderWorkspaceIds, and the connect names the folder.
    • A later delta move never offers the linked folder again.
    • Relay terminals: a terminal in a moving folder still allows the link (it failed before the lease-key fix), and a terminal in the linked folder refuses with linked_project_terminals:<folder>.
    • Pure checks: same id at a different folder, a different id, and a folder another host holds (its own host fields, its group's connection, or a nested SSH project in its group) are never linked.
  • orcad-migration-catalog-client.test.ts: the server folder catalog read.
  • Live, on omarchy (Linux x86_64) under isolation: I copied a real orcad build (0.1.0+379585478820, Oct 16) and its bundled Node into /tmp, ran it with a throwaway HOME, ORCA_USER_DATA and XDG directories on a free port, and reached it through a private ssh -L tunnel. Against it, real desktop Stores and the real catalog client did a first move, deleted and re-created the target, edited it, then moved again:
    • project only, with no linking (the reporter's build): orcad_migration_stage_failed:orcad_migration_dormant_id_conflict:worktree_meta:.... With main's linking: converted.
    • with a folder workspace and no folder linking (main): orcad_migration_stage_failed:orcad_migration_folder_workspace_id_conflict:.... Retrying the same fenced move with this PR: converted, and the server still lists the folder at its original path.
    • Not exercised live: the Electron UI connect flow and the deploy/tunnel steps (mocked in the harness). Everything was removed afterwards.

Notes

  • Wire compatibility: no new methods or fields. The desktop calls existing list methods, and the journal field is optional and local to the desktop.
  • SSH and folder workspaces are the whole subject here. Git worktrees are unaffected.

@coderabbitai

coderabbitai Bot commented Oct 17, 2026

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 9b884c35-b22c-4dec-8802-9514caf06300
📥 Commits

Reviewing files that changed from the base of the PR and between 0f9c12d and 3c1090a.

📒 Files selected for processing: 11
  • src/main/ipc/ssh-host-server-on-connect-wiring.ts
  • src/main/ssh/orcad-migration-catalog-client.test.ts
  • src/main/ssh/orcad-migration-catalog-client.ts
  • src/main/ssh/orcad-migration-delta-plan.ts
  • src/main/ssh/orcad-migration-repository-link.test.ts
  • src/main/ssh/orcad-migration-repository-link.ts
  • src/main/ssh/orcad-runtime-conversion-wiring.ts
  • src/main/ssh/orcad-runtime-conversion.ts
  • src/main/ssh/orcad-server-folder-workspaces.ts
  • src/shared/folder-workspace-execution-host.ts
  • src/shared/orcad-migration-source-cutover.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 2 remain after this review.

📝 Walkthrough

This change adds server folder-workspace catalog support to Orcad migration. The code can now fetch remote folder-workspace and project-group rows, pass that catalog through SSH runtime conversion, and record linked folder workspace IDs in cutover data. Migration linking now treats retained server folder workspaces like retained repositories for matching, project-group retention, relay-terminal checks, linked-project reporting, and later delta-source exclusion. Tests now cover folder catalog retrieval, recreated SSH targets, linked folder behavior, and folder host matching.

Priority: ➖ Normal

Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to 3c109

The catalog integration has no identified issue requiring a fix before merge. Normal checks remain appropriate.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 59.26% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 27 functions across 11 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title clearly identifies the SSH migration fix for linking a server-retained folder workspace after recreating an SSH host.
Description check Passed The description is detailed and covers the user impact, implementation, rationale, linked issue, testing, live validation, and compatibility notes. It omits some template checklist sections, but the r…
Linked Issues check Passed Issue #27285 requires safe handling when a recreated SSH target re-migrates rows that the server already holds under the same identity and path. The reviewed code preserves the existing repository lin…
Out of Scope Changes check Passed The changes support the migration reconciliation described by issue #27285. Catalog retrieval, resolver type widening, journal fields, delta filtering, terminal checks, wiring, and tests are required …
Full details: Description check

Explanation

The description is detailed and covers the user impact, implementation, rationale, linked issue, testing, live validation, and compatibility notes. It omits some template checklist sections, but the required change information is substantially complete.

Full details: Linked Issues check

Explanation

Issue #27285 requires safe handling when a recreated SSH target re-migrates rows that the server already holds under the same identity and path. The reviewed code preserves the existing repository linking path and adds folder-workspace linking only for the same ID, normalized folder path, and server-local host resolution. Other ID or host collisions remain import conflicts. The journal, delta source, connect notice, relay-terminal guard, and catalog tests cover the new folder behavior. The added migration test also verifies that server-side repository state remains authoritative.

Full details: Out of Scope Changes check

Explanation

The changes support the migration reconciliation described by issue #27285. Catalog retrieval, resolver type widening, journal fields, delta filtering, terminal checks, wiring, and tests are required to link retained folder workspaces safely. The changes do not introduce an unrelated feature or unrelated data path.

  • Fix all pre-merge checks with AI
✨ Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

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.

orcad_migration_repository_id_conflict after the SSH target is re-created with the same repos

1 participant