Repository navigation
cmux-next: classic upgrade checks wait for placements to land, not for 0.5 s of quiet (#28425) - #28526
Conversation
…r 0.5 s of quiet (#28425) ClassicUpgradeSimulator.settle() treated 0.5 s with no visible change as done. Since #26153 a classic import places each new workspace at its slot through the sidebar document (a `sidebar.apply` move, not the personal placement queue that #28362 waits for), so on a loaded runner a move or an adopt could still be on its way after the quiet window: the check then read an order or a group the late move changed (#28331 attempts 1 and 2, #28425). Product: SidebarDocStore counts the changes and adopts it sent and not yet answered, and exposes isSettled and settled() (event-driven: each reply and load end resumes the waiters). The classic import waits for it after the personal queue, so a workspace's group write is sent after its slot move is committed on the daemon, on the document path too. Tests: settle() and the ClassicMigrationTests launch helper read the tree only once every completion signal holds: the personal queue through, the document settled, the store applied through an event barrier taken after those replies, no live workspace still waiting for its new-workspace placement, and the document projection current. No quiet window; a hang guard records an issue naming what is still in flight. SidebarDocSettledTests covers the new signal. SidebarDocStore.swift: the remote protocol, its reply and SidebarDocUnsettled move to SidebarDocRemote.swift (no change), so the file shrinks. Sidebar-Look: no visual change Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Review App of this head (app.zip, kept 1 day)
Not verified: macOS jobs did not run: compile and app tests were skipped on CI passes on Written by Shipped in:
|
…ent (#28425) The import now waits for its slot move's reply before each group write. ClassicUpgradeSimulationPerfTests records that wait per imported workspace through a DEBUG bench span (BenchSpans, nothing recorded in release) and prints n, sum and p50/p95/max per profile (CLASSIC-SIM-PERF-SETTLE). The perf suite also holds the one SidebarDocStore (.ownsSidebarDocStore), as the other live-daemon suites do. Sidebar-Look: no visual change Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…or settle()'s quiet window (#28425) backgroundWorkspacesStartTheirTerminalsWhenFirstShown read the parked state right after settle(). `park-tabs` answers before the shells exit; a tab shows dead with `relaunch` only once its host exited. The 0.5 s quiet window that settle() no longer has covered that by chance; without it the last workspace (scratch) read "not parked: [(false, nil)]" in 5 of 10 runs at 7f80b7d (step 815e7963). The check now reads once every background tab shows parked (the daemon's tab-exit events), with a 60 s hang ceiling. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bugbot needs on-demand usage enabledBugbot uses usage-based billing for this team and requires on-demand usage to be enabled. A team admin can enable on-demand usage in the Cursor dashboard. |
…held past any quiet window ClassicUpgradeHeldDocumentTests holds every sidebar document change and adopt (the classic import's slot moves) for 4 s before it reaches the daemon, as a loaded runner's slow reply does. The check must read the tree only once those moves have landed. The test seam is a `wrapping:` parameter of SidebarDocWiring.start (default: the daemon remote as is), passed by ClassicUpgradeSimulator(documentRemote:). The red is on a scratch never-merge ref (this test over the base logic: settle() with its 0.5 s quiet window and the import without the document wait); this commit and the branch head are the green. Sidebar-Look: no visual change Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…waits for it, with no clock (#28425) The 4 s hold was still a clock. HeldRemote now holds every sidebar document change and adopt until someone waits in SidebarDocStore.settled() (the store's onSettledWait test seam releases them; one sent while someone waits goes on at once). The test reads, in the turn settle() returns in, how many are still held: a settle that does not wait on the document returns with moves held (red, every time); the new one cannot return until they are released and answered (green). placementsSettled() now awaits the store's settled() instead of polling isSettled. Sidebar-Look: no visual change Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…28425) placementsSettled() keeps the event-driven SidebarDocStore.settled() wait, with a 60 s hang guard (a ceiling, not a quiet window): if the store has not settled by then, the recorded issue says what is pending, from the store's new pendingDescription (changes and adopts unanswered, a running load, changes a load must settle, waiters). The held-document test prints it in its failure too. Sidebar-Look: no visual change Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…acement wait (#28425) Compile 6af24f9b at 08910ed failed: - ClassicUpgradeHeldDocumentTests.swift:73: the trailing closure matched ClassicUpgradeSimulator.init's `ssh:` parameter (String), not `documentRemote:`; it is passed by label now. - AppServices+PlacementsSettled.swift:41,50: `pendingDescription` is internal to CmuxNextSidebar; the helper imports it @testable. Also: the held remote's `waiting` is typed @mainactor, the local in release() no longer shadows it, and after the read nothing more is held (shutdown's changes go through). The await on the settled wait is its own statement. Sidebar-Look: no visual change Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Found 1 test failure on Blacksmith runners: Failure
|
|
Red checks on head 81c60ec, classified (none caused by this PR's changes):
Fleet proof (red/green with no clock) is in the description once the steps end. |
|
Review (Claude Opus 5.5 reviewer): SHIP at 81c60ec Scope: read-only review of 7c41ce3..81c60ec (7 commits, 11 files). I did not compile or run anything on a local Mac. Correctness
Rules
Discriminator
Evidence checked
Non-blocking
|
![Fix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/fix-with-codesmith-light-3x.png)
Closes #28425.
Cause
ClassicUpgradeSimulator.settle()(Packages/macOS/CmuxNext/Tests/CmuxNextAppTests/ClassicUpgradeSimulator.swift:316-332 at base 7c41ce3) treated 0.5 s with no visible change as done. #28362 made it also wait for the personal placement queue, but a classic import places each new workspace at its slot through the sidebar document when the daemon servessidebar-doc-v1(asidebar.applymove orsidebar.adoptsent from a task inSidebarDocStore.submit/adopt), not throughPersonalPlacementQueue. Nothing told the test that those requests were still on their way, so on a loaded runner a late move or adopt changed the order after the quiet window. That is the failure on #28331 (anOffSSHHostDoesNotHoldBackLocalWorkspaces:239,aDowngradeAndReUpgradeMergesOnlyNewWork:184) and the ones #28425 lists.The same gap was in the product:
importClassicWorkspacewaited for the personal queue before it wrote the workspace's classic group (#28362), but on the document path the slot move could still be in flight, and the daemon's document write-back of a late move takes the workspace out of its group again.Fix
SidebarDocStorecounts the changes and adopts it sent and not yet answered.isSettled(none in flight and no load running) andsettled()(event-driven: each reply and load end resumes the waiters; no polling) are the completion signal.SidebarDocStore.current?.settled()after the personal queue, so the group write goes after the committed slot move on the document path too.AppServices.placementsSettled()(the test helper thatsettle()and theClassicMigrationTestslaunch helper use) waits for every completion signal at once: the personal queue through, the document settled, the store applied through an event barrier taken after those replies, no live workspace waiting for its new-workspace placement, and the document projection current (shownVersion == version). The 0.5 s quiet window is gone. A 60 s hang guard records an issue that names what is still in flight.SidebarDocSettledTests: a move or an adopt keeps the store unsettled until its reply;settled()returns with the reply.SidebarDocStore.swiftshrinks: the remote protocol, its reply andSidebarDocUnsettledmove unchanged toSidebarDocRemote.swift.The known issue that stays (
aRunCutOffMidwayFinishesWithoutDuplicates, "a resumed run makes its workspaces last in the sidebar") is a declared product limit from f9021c8, not the #28425 quarantine; #28445 already lifted that one.Owners: sidebar store (
CmuxNextSidebar) is a590 (sidebar-owner agent); onboarding/classic migration has no row in OWNERS.md (Performance Lead).Performance
No perf numbers: import cost: in-process timing pending; not measured. The import now waits once per workspace for the reply to that workspace's own
sidebar.applyslot move, which the old code already sent and did not wait for. Base and head cannot be paired on one fleet host, so two numbers from two hosts are not evidence.ClassicUpgradeSimulationPerfTestsprints the per-workspace wait (CLASSIC-SIM-PERF-SETTLE, a DEBUG bench span inplacementLanded) for a later run. perf claim: none.Evidence
Both suites (
ClassicUpgradeSimulationTests,ClassicMigrationTests) 10 times at the base and 10 times at the head, on the fleet (--pool perf-lead). The Mac proof steps are submitted under the alias branchfix/feat-classic-sim-settle(the same commit as this PR's head): the lead'sfix/feat-*entry in the Mac-lane allow list covers feat base-red fixes (lead decision, 2026-10-30 21:0xZ).Red evidence (base) is from CI, not from the fleet loop.
anOffSSHHostDoesNotHoldBackLocalWorkspaces:239, first two workspaces swapped.aDowngradeAndReUpgradeMergesOnlyNewWork:184,small: order differs: [... "new in new group", "new in old group"] for [... "new in old group", "new in new group"].theFirstLaunchBringsEverythingOnce(:45, :52),theImportActionDuringTheLaunchRunMakesNothingTwice(:206) andClassicMigrationTests.aReUpgradeMergesOnlyWhatClassicMadeMeanwhileon CI runners.Base on the fleet did not reproduce it. Step d2dac36053cc5e74b8f9029b at base 7c41ce3 (cmux22-perf, an idle M6 perf host): 20 of 20 suite runs passed (10x
ClassicUpgradeSimulationTests, 10xClassicMigrationTests). There were 0 recorded issues and nogate-notice: FLAKYline; the only issues recorded were 11 known issues at :158, the declared resumed-run order limit. The race needs a loaded runner. So this step shows no base red, and the base-to-head comparison rests on the CI failures above.First head, 7f80b7d (step 815e7963cc9c634d2d4e6762, aws-m4pro-4): order and group checks passed in all 20 runs, but
ClassicUpgradeSimulationTestsfailed 5 of 10, all at :286 inbackgroundWorkspacesStartTheirTerminalsWhenFirstShown("scratch is not parked: [(false, nil)]").park-tabsanswers before the shells exit, and the removed 0.5 s window had covered that by chance. Fixed in 2dad1ec: the check waits for the tab-exit events (60 s hang ceiling).ClassicMigrationTestspassed 10 of 10.Forced-order proof, no clock (
ClassicUpgradeHeldDocumentTests: every sidebar document change and adopt is held until someone waits inSidebarDocStore.settled(); the test reads, in the turnsettle()returns, how many are still held):ClassicUpgradeHeldDocumentTests5/5,ClassicUpgradeSimulationTests10/10,ClassicMigrationTests10/10. 0 recorded issues, nogate-notice: FLAKY; only the 15 declared known issues at :158.settle()with its 0.5 s window, baseplacementsSettled(), import without the document wait; branchnever-merge/classic-sim-settle-red-v3): 5 of 5 runs red (step 9aed3d6aa9bafe68300e000d, cmuxs-Mac-mini-4.local): 4 failed at ClassicUpgradeHeldDocumentTests.swift:91 withsettle() returned with 6 sidebar document changes still held (settled: false; 6 sidebar document changes or adopts unanswered, load running: false, ...); 1 stalled until the suite's time limit (198 s) with the moves still held. No run passed; nogate-notice: FLAKY.cmux-next daemon testson 81c60ec: pass (job 124333768628), the lane fix(next): Ctrl-`, Cmd-D and New Row always give a terminal; a refusal is never silent; no stand-in id reaches the daemon (P0) #28331 failed in.Observed / Not run
Observed: the fleet steps above (job logs read in full, every attempt counted). The base loop did not reproduce the CI red.
prepush-gate.py: no new violations.check-no-godfiles.sh --base: no file grew.Not run: nothing compiled or tested on a local Mac. The full daemon-tests job runs in CI on this PR.
No capture: no UI change (a store completion signal, an import ordering wait and test helpers).
Changelog
none