fix(sidebar): a placement whose workspace ended is dropped, never toasted

Merged
TE
3d ago
+38-5

Leo's words: "what's that newest error message situation?" (a "Move did not apply: set-personal-workspace: workspace.place failed: no workspace matches "ws_…"" notice).

Cause. cmux agent open ID --placement top under load: the app made the workspace, queued its top placement (NewWorkspacePlacements.expect → SidebarBridge.place → placePersonal), then closed the half-made workspace when its first chat tab missed its deadline. The queued workspace.place ran against an id the daemon no longer had (selector.not_found), the placement failed, and the app showed the failure under the "Move" title, after the fact, for a move nobody asked for. The failure also stopped every placement queued after it.

Fix. A placement step whose workspace the daemon answers selector.not_found (scope workspace) for is dropped with a log line: no notice, the placement settles as applied, and the placements queued behind it still run. DaemonError.isWorkspaceNotFound names that answer. A placement for a workspace that has not appeared yet already waits for it (applyPendingPlacements only places a listed, non-provisional workspace), so nothing else changes.

Test (red first). PlacementWriteOrderTests.aPlacementForAGoneWorkspaceIsDroppedWithoutANotice: the scripted daemon answers k2's placement with selector.not_found; before this change the run fails, a notice shows and k3/k4 never apply; after it, no failure, no notice, and k3 and k4 are written.

Risk: small, one call site in the personal placement queue; any other placement error still reports as before.

Agent session: agent session

šŸ¤– Generated with Claude Code


Autofix with [code]smith
Open in [code]smith Ā· Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

Fixes queued personal workspace placements failing with a misleading "Move did not apply" toast when the workspace had already ended.

Under load, cmux agent open ID --placement top creates a workspace, queues its top placement, then closes the half-made workspace when its first chat tab misses its deadline. The queued workspace.place hits the daemon's selector.not_found, the failure gets toasted as a failed move, and every placement queued after it is skipped. A placement whose workspace is gone is now dropped with a log line instead, and the placements behind it still run.

  • DaemonError.isWorkspaceNotFound recognizes selector.not_found scoped to a workspace.
  • Any other placement error still reports as before.

Tests

  • PlacementWriteOrderTests.aPlacementForAGoneWorkspaceIsDroppedWithoutANotice covers the case: no failure, no toast, and the later placements still apply.