Implement mobile project switching and sync catalog#172
Merged
Conversation
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements the mobile multi-project home and desktop sync catalog flow. Desktop now advertises a project catalog to paired phones, handles project switch requests, and keeps sync host status fresh when switching projects. The iOS app now launches through an ADE-style project home, can open connection settings from that surface, lists desktop projects, switches between them, and can return to the active app surface. The mobile runtime scopes cached lanes, sessions, files, PRs, and snapshots to the active project so stale data from another desktop project does not bleed into the current mobile view.
This also includes the branch’s desktop onboarding/computer-use and web editorial updates that were already part of this work branch, plus the CodeRabbit follow-up fixes for project switching rollback/cancellation, onboarding tour cleanup, mission preflight fallback messaging, and web accessibility/reduced-motion details.
Validation
mobile-lanes-tab-2d82c012, verified project-scoped Work data, and checked the simulator SQLite database for project/lane/session counts and foreign-key health.git diff --checkxcrun swiftc -parse apps/ios/ADE/App/ContentView.swift apps/ios/ADE/Models/RemoteModels.swift apps/ios/ADE/Services/Database.swift apps/ios/ADE/Services/SyncService.swift apps/ios/ADE/Views/Components/ADEDesignSystem.swift apps/ios/ADE/Views/AttentionDrawer/AttentionDrawerButton.swift apps/ios/ADE/Views/Work/WorkRootScreen.swift apps/ios/ADETests/ADETests.swiftplutil -lint apps/ios/ADE/Info.plistxcodebuild test -quiet -project apps/ios/ADE.xcodeproj -scheme ADE -destination 'platform=iOS Simulator,name=iPhone 17 Pro,OS=26.2' -only-testing:ADETests/ADETests/testDatabaseListsMobileProjectsAndScopesCachedRuntimeByActiveProject -only-testing:ADETests/ADETests/testSyncServiceProjectHomeUsesCachedProjectsAndLocalSelection -only-testing:ADETests/ADETests/testDatabaseFetchSessionsHidesSessionsWhenLaneRowIsMissingasdf exec npm --prefix apps/desktop run typecheckasdf exec npm --prefix apps/desktop run buildasdf exec npm --prefix apps/desktop run lintexits with 0 errors and the existing warning backlogTopBar, sync host/protocol, computer-use control plane, onboarding, queue landing, and rerun failed shardsasdf exec npm --prefix apps/ade-cli run typecheckasdf exec npm --prefix apps/ade-cli run testasdf exec npm --prefix apps/ade-cli run buildasdf exec npm --prefix apps/web run typecheckasdf exec npm --prefix apps/web run buildnode scripts/validate-docs.mjscoderabbit review --agent --type uncommitted; valid findings were fixed. A final rerun hit the service rate limit after the fix pass.Notes
The full
coderabbit --agentbranch review cannot run on this PR shape because the service rejects diffs over 150 files; the uncommitted scoped reviews were used before committing and valid findings from those passes were addressed.Summary by CodeRabbit
New Features
Bug Fixes
Documentation
Refactor
Greptile Summary
This PR implements end-to-end mobile multi-project switching: the desktop now builds and advertises a project catalog to paired phones, handles
project_switch_requestmessages, and manages per-project sync host lifecycle; the iOS app gains a Project Home screen, catalog merging with dedup, and project-scoped DB reads for lanes, sessions, files, PRs, snapshots, and integration proposals. Previous review concerns (sequentialPromise.all,fetchIntegrationProposalsscoping, rollback/cancellation guard) are all resolved.handleDropstale-closure inTopBar.tsx: iffetchRecentfires on window focus mid-drag, the drop handler splices from a stalerecentProjectssnapshot and persists a wrong order viareorderRecent.Confidence Score: 4/5
Safe to merge after addressing the stale-closure drag-reorder bug in TopBar; iOS offline fallback race is low-probability and non-data-corrupting.
One P1 bug (drag reorder overwrites fresh server state with stale closure data on mid-drag focus event) keeps the score at 4. All prior P1 concerns from previous review rounds are resolved. The P2 iOS offline fallback race is a narrow edge case that only produces a user-visible error message, not data loss.
apps/desktop/src/renderer/components/app/TopBar.tsx — handleDrop stale recentProjects closure.
Important Files Changed
Sequence Diagram
sequenceDiagram participant Phone as iOS App participant Desktop as Desktop (main.ts) participant SyncHost as SyncHostService Phone->>Desktop: project_catalog_request Desktop->>Desktop: listMobileSyncProjects() Promise.all over contexts Desktop-->>Phone: project_catalog_response { projects[] } Phone->>Phone: refreshProjectCatalog() merge remote + cached, dedup by rootPath Phone->>Desktop: project_switch_request { projectId, rootPath } Desktop->>Desktop: prepareMobileSyncProjectConnection() ensureProjectContextForMobileSync() Desktop->>SyncHost: initialize() + getStatus() Desktop-->>Phone: project_switch_result { ok, connection, project } Phone->>Phone: switchToDesktopProject() save rollback state, setActiveProjectId Phone->>SyncHost: connectUsingProfile(new token/port) alt success Phone->>Phone: projectHomePresented = false else error and still current selection Phone->>Phone: rollback activeProjectId, token, profile then reconnectIfPossible else error and superseded by newer selection Phone->>Phone: rethrow no rollback endComments Outside Diff (3)
apps/ios/ADE/Services/Database.swift, line 1817-1845 (link)fetchIntegrationProposalsnot scoped to active projectUnlike
fetchLanes,fetchTerminalSessions, andfetchPullRequests, this function omits any project-id filter and returns all rows across every project in the local database. After a project switch, proposals whosesource_lane_ids_jsoncontains lanes from the previous project will still appear in the integration-proposals tab for the newly selected project. Theintegration_proposalstable has noproject_idcolumn, so a join through lanes is needed:Alternatively, add a
project_idcolumn at the schema layer if per-project isolation is desired here.Prompt To Fix With AI
apps/ios/ADE/Services/Database.swift, line 1830-1857 (link)fetchIntegrationProposalsnot scoped to active projectThe query returns every row in
integration_proposalsacross all projects. The table has aproject_id NOT NULLcolumn with an index (idx_integration_proposals_project), so a scoped filter is straightforward. After a project switch, proposals whosesource_lane_ids_jsonreferences lanes from the previous project will still appear in the integration-proposals tab for the newly selected project.Add a
WHERE project_id = ?filter directly — the join through lanes is not needed since the column exists:Prompt To Fix With AI
apps/desktop/src/renderer/components/app/TopBar.tsx, line 269-281 (link)handleDropcloses over stalerecentProjectshandleDropis wrapped inuseCallbackwith[dragIdx, recentProjects]as deps, which means on every render whererecentProjectschanges a new callback is created. However, if a focus-triggeredfetchRecentfires betweendragstartanddrop(e.g. the window re-focuses mid-drag),recentProjectsin state is replaced with a fresh server response, but thehandleDropclosure still holds the old snapshot. Thesplicethen produces a reordered list from the stale array, overwriting the just-refreshed data. ThereorderRecentIPC call will persist the stale order too.Consider using a
refto always read the latestrecentProjectsinsidehandleDrop, or suppressfetchRecentwhile a drag is in progress (dragIdx !== null).Prompt To Fix With AI
Prompt To Fix All With AI
Reviews (10): Last reviewed commit: "Allow cached mobile project ids during s..." | Re-trigger Greptile