fix(app): create new workspaces on the host you are already working on - #4173
mwhitman-steward wants to merge 6 commits into
Conversation
|
| Filename | Overview |
|---|---|
| packages/app/src/components/sidebar-workspace-list.tsx | Supplies active-host connection statuses to project rows and includes the new input in memoization boundaries. |
| packages/app/src/utils/sidebar-project-row-model.ts | Extends new-workspace target resolution to prefer the qualifying, online active host before applying the existing fallback. |
| packages/app/src/utils/sidebar-project-row-model.test.ts | Covers active-host preference and fallback when the active host is absent, offline, or unable to create the workspace. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
Click[Sidebar project +] --> Qualify[Find hosts that can create a workspace]
Qualify --> Active{Active host is qualifying and online?}
Active -->|Yes| Preferred[Select active host]
Active -->|No| Fallback[Select first qualifying host]
Preferred --> Route[Build /new route with serverId]
Fallback --> Route
Reviews (7): Last reviewed commit: "Merge branch 'main' into remember-new-wo..." | Re-trigger Greptile
The composer already remembers provider, model, mode and isolation, but the host reset on every visit. On a multi-host setup it defaulted back to the local daemon even when the user had been working on a remote host: a project registered on two hosts merges into a single project whose hosts[] array follows host-registry order, so the last-active-project walk and the first-online-host fallback both resolved local. Store the host alongside the other composer preferences and consult it after an explicit ?serverId= route param but before those fallbacks. Only an explicit pick in the host picker is stored — persisting an automatically resolved host would latch the preference onto whatever the resolver happened to choose. A stored host is used only while it is still registered and online, so a retired or unreachable host falls through to the existing chain. Host ordering is untouched: local-first ordering is correct for display, and this changes selection only.
e6582bb to
f9421fe
Compare
The sidebar's per-project "+" navigates with an explicit ?serverId=, which the composer honours outright, so the remembered host never got a say on the flow most people use to start a workspace. That serverId came from the same first-qualifying-entry walk over the project's hosts[], which follows host-registry order and so resolves local for a project registered on both machines. Pick the remembered host when the project is on it and it can host a new workspace there. Because the route param wins outright, an offline remembered host would stick with nothing to correct it, so the host must be online; otherwise the existing walk applies unchanged.
The composer and Cmd+N already follow the active workspace's host: the global action passes its serverId on the route, and the composer's last-active-project candidate is synthesised from that same workspace, so it carries exactly one host. The sidebar's per-project "+" was the only entry point that ignored it, walking the project's merged hosts[] in registry order and so resolving local for a project registered on both machines. Because that row navigates with an explicit ?serverId= the composer honours outright, the walk decided the host outright too. Use the active workspace's host there as well, when the project is on it and it can host a workspace there. It must be online, since the route param wins and nothing downstream would correct an unreachable host; otherwise the existing walk applies unchanged. This replaces the persisted host preference from the previous commits. Following the workspace you are looking at needs no stored state, cannot go stale, and self-corrects when you switch hosts.
|
Closing for now: this needs a live two-host check showing the sidebar + creates the workspace on the active remote host when the project exists on both. Please reopen with that evidence. |
|
Evidence added — and you were right to ask, because it caught something the unit tests could not. I had said the e2e harness could only fake a second host as offline. That was wrong: Live two-host result, both daemons online, project registered on both: Open the secondary host's workspace so it is the active one, then click the project row's "+":
On The sibling test opens the primary workspace and asserts the "+" follows back to Primary. It passes on Both tests assert the workspace is actually created on that host, not merely that the composer preselects it. Full spec on this branch: 9/9 passing. Two things I want to be straight about:
|
|
Please disregard the earlier invitation to reopen with evidence. This PR remains closed unless I explicitly follow up. You don't need to resubmit this PR or provide further QA evidence. Please read the updated PR policy. For now, I'm automatically closing feature PRs so I can focus more time on bug fixes and core improvements. Focused bug-fix PRs with a clear reproduction and QA are still welcome. The previous guide already asked contributors to submit only if they were comfortable with closure, and explained that unsolicited PRs could be closed without a detailed review. For feature ideas, please start in GitHub Discussions and share your workflow: what you're trying to do, how you do it today, and where Paseo gets in the way. If there's already a discussion about it, join in and share your use case. I'll periodically review discussions for highly requested workflows and use that feedback to shape the roadmap. I'll periodically look through closed PRs and choose which contributions to take forward, whether solicited or unsolicited. I may reopen a PR, including one closed by mistake, or use it as a reference for my own implementation, with attribution either way. There are hundreds of PRs alongside ongoing development. I can't commit to reviewing every submission, providing individual feedback or giving timelines. |
Linked issue
Closes #4172
Type of change
Reasoning
If you work across two machines and a project is registered on both, the sidebar's per-project "+" always creates the workspace on the local daemon — even when you are currently working on the remote host. You notice after the agent starts, against the wrong checkout, and have to throw the workspace away and start again.
Paseo already has one rule for this: new work happens on the host you are already working on. Cmd+N follows it, and so does the composer on a bare
/new. The sidebar "+" is the only entry point that ignores it, which is unfortunate because it is the one people reach for most.serverId, passed on the route (use-global-new-workspace-action.ts)/newlastActiveProject, synthesised from the active workspace byhostProjectFromWorkspace, so it carries exactly one hosthosts[]The sidebar row is the odd one out because its
SidebarProjectEntry.hostsis the merged multi-host array frombuildWorkspaceStructureProjects, ordered by host registry, which puts the local daemon first in the common case.resolveNewWorkspaceTargettakes the first qualifying entry, so a project on both machines always resolves local. That decision is final rather than a default: the row navigates with an explicit?serverId=, andresolveNewWorkspaceInitialServerIdhonours a route param outright, so the composer never gets a say.Goals
Non-goals
serverId, which is correct context propagation rather than the bug.orderHostsLocalFirstis untouched. Local-first ordering is correct for display; this changes selection only.QA
Reproduced before, confirmed after, on two live daemons.
sidebar-project-grouping.spec.tsboots a real second host viastartIsolatedHostDaemonand groups one repository across both through a shared Git remote, which is the exact precondition for this bug.Open the secondary host's workspace so it is the active one, then click the project row's "+":
mainOn
mainthe assertion fails withExpected substring: "Secondary Host"/Received string: "Primary Host". The sibling test opens the primary workspace and asserts the "+" follows back to Primary; it passes onmaintoo, becausemainalways picks primary. That asymmetry is what makes the pair conclusive rather than merely green. Both tests assert the workspace is actually created on that host, not just preselected.Commands run:
npm run typecheck— cleannpm run lint— cleannpm run format:check— cleannpx vitest run src/utils/sidebar-project-row-model.test.ts— 17 passednpx playwright test --project=browser e2e/browser/sidebar-project-grouping.spec.ts— 9 passednpx playwright test --project=browser e2e/browser/sidebar-workspace.spec.ts e2e/browser/new-workspace-entry.spec.ts— 1 pre-existing failure,new-workspace-entry.spec.ts› "Ctrl+P opens the project picker with search focused", reproduced identically on unmodifiedmainwith my changes revertedNo screenshots. There is no visual change — the sidebar row layout, host ordering and every label are untouched. The only difference is which
serverIdthe "+" puts on the route. I have not tested this on iOS or Android.Checklist
npm run typecheckpassesnpm run lintpassesnpm run formatpasses