Skip to content

base red: Cmd-N row slot tests (a placement's own rows pass drew the new row at the end) - #31069

Merged
lawrencecchen merged 1 commit into
feat-cmux-nextfrom
fix/base-red-cmdn-row-slot
Nov 1, 2026
Merged

lawrencecchen merged 1 commit into
feat-cmux-nextfrom
fix/base-red-cmdn-row-slot

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Base red on feat-cmux-next (priority zero). Three tests check that a Cmd-N workspace row draws only at its slot from the first frame. On the base they fail often but not every time:

  • CmdNRowSlotTests.cmdNDrawsTheNewRowOnlyAtItsSlot (:79)
  • CmdNInGroupRowSlotTests.cmdNInAGroupDrawsTheNewRowOnlyAtItsSlotInTheGroup (:209)
  • ColdCmdNRowSlotTests.firstColdCmdNWithRestoredFollowingRowsStaysAfterCurrent (:148)

What the person sees: after Cmd-N, the new row draws at the end of the sidebar for one frame, then jumps to its slot.

Cause (an old race, not a recent commit). A temporary event-order trace (scratch branch, job c8b7e3ff4e954c8e515b260c) shows this order in every failing run:

  1. The press draws the pending row at its slot.
  2. Reconcile: the daemon's workspace is now live. applyPendingPlacements takes the placement and clears its entry.
  3. The placement's own refresh (placementSections → sameTurn.syncNow) runs a rows pass before the move is sent. The sidebar document does not place the item yet and does not lag (version == shownVersion). So PendingWorkspaceRows.listing lists the workspace's own row at the daemon's place (the end), forget drops the pending row, and that frame is drawn.
  4. bridge.place sends the move, and the next frame has the row at its slot.

A run passes only when the store still shows the stand-in at step 3. The tests also fail at #27014's merge (58af48e51d8) and at every later point tried (jobs 501830b7, 76d6a7e9, fdc057e7, 989300c4). No single commit caused it, so there is nothing to revert.

Fix. A workspace whose slot WindowManager.pendingPlacements holds keeps its pending row at that slot until the placement is sent:

  • The entry is now cleared after the placement's refresh, right before the move. It used to be cleared before the refresh.
  • PendingWorkspaceRows.unsentSlots, observed and kept per id from pendingPlacements' didSet, makes listing keep the pending row while the id is in the set. When the id leaves the set, the rows pass runs again, also for a placement that sends no move.
  • An anchor wait now updates its entry in place.

There is no wait, retry or delay. The change adds no lines to CmuxNextApp (arch-ratchet app_lines).

The same fix makes NewInNewGroupRowSlotTests pass: it is red at the base and green at the head.

Testing

All runs are on the fleet (scripts/ci/package-test-lane.sh suite Packages/macOS/CmuxNext …). Each suite runs in its own process, by filter CmuxNextAppTests.<Suite>.

The base has an unrelated compile red: AgentSessionAttachClientCloudRepositoryTests from #30832, being reverted in #31023. Because of it, red and green ran on a scratch pair that is never merged:

  • base b33ff910 = origin/revert/30832-cloud-repo-tests
  • head bf5d0de3 = this branch merged with that revert
  • The diff between the two is exactly this PR's 3 files.
  • Compile 568bf412e4357b6fb3679c3a passed (CmuxNext, app, cmux-tui).
suite (runs) base b33ff910, job 569b74b326d9215319079152 head bf5d0de3, job d6325ac61aa65ab5f2b77b46
CmdNRowSlotTests (3) 1 red (:79) 3 green
CmdNInGroupRowSlotTests (3, 4 cases each) 3 red (:209, afterCurrent, both instantPage) 3 green
ColdCmdNRowSlotTests (5) 1 red (:148) 5 green

More runs:

  • Head 42e34005985a404cb3bcb9db: ColdCmdN 4 of 4 green (9 of 9 at head in total); CmdNInGroup 2 of 2 green isolated.
  • Base 9c948a88edacbc16cef0a662: cmdNDraws red (:79).
  • Earlier reds at feat commits: 396855a8a312466572ff426d and 7adc0ba81250c7e855359687 (ColdCmdN 3 of 5 red), both at e25bc25a. Isolated at feat tip 86989822: 78e8567a77c1016de04b3237.

Neighbour suites (17), base 5a165f5875349b38b22b60ac vs head 05a772b114c67a826903c257:

  • 15 pass at both (CmdNTopInGroupFrames, ColdCmdNWindowRows, NewWorkspacePlacementRace, PlacementStaleRows, PlacementWriteOrder, PlacementDocumentWriteOrder, RapidWorkspaceCreate, SidebarDocNewWorkspaceTop, AgentSessionGroupPlacement, AgentSessionGroupDirectPlacement, ChiefSidebarPlacement, CmdNSlowDaemonKeepsWorkspace, CmdNAlwaysNewWorkspace, LinkOpenPlacement, SidebarInsertBurst as run without a display).
  • NewInNewGroupRowSlotTests is red at base and green at head.
  • SidebarCmdNumberTwiceTests is red at both: a base finding, not caused by this PR.

Observed / Not run

  • Observed: the table above. In the 3-suite process run at head (42e34005), one case failed at CmdNRowSlotTests.swift:162: the test's own New Group setup did not form within 30 s. That is a different check from the slot. The same :162 failure shows at base commits without this change (34129b0c at d2b6b65d, 55865c40 at 49f371cc), so it is a separate intermittent base finding.
  • Not run: SidebarInsertBurstTests on a display worker (it skips without one); a tagged dogfood build. Not measured: frame timing (the change removes one rows pass's wrong frame; it adds no work on the main thread except a set update when a placement is added or removed).

Changelog

Fixed: A new workspace's sidebar row no longer appears at the bottom for a moment before it moves to its place after Cmd-N.

Proof

No capture: the bug lasts one frame and depends on timing. The frame-by-frame row checks in the three tests above are the evidence. A still image cannot show a one-frame jump.


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


Summary by cubic

Fixes a one-frame race where, after Cmd-N, a new workspace row drew at the end of the sidebar before jumping to its slot. A workspace whose slot WindowManager.pendingPlacements holds now keeps its pending row at that slot until the placement is sent, so the row draws correctly on the first frame.

Bug Fixes

  • The placement entry is cleared after the rows pass, right before the move, instead of before it.
  • PendingWorkspaceRows.unsentSlots keeps the pending row listed while the placement is unsent, and re-runs the rows pass when the placement goes, even for placements that send no move.
  • Anchor waits now update their entry in place instead of clearing and resetting it.
  • No wait, retry, or delay is added; the change stays line-neutral in CmuxNextApp.

Written for commit 4cad31b. Summary will update on new commits.

View guided diff Turn on auto-fix

… rows pass runs

CmdNRowSlotTests, CmdNInGroupRowSlotTests (3 of 4 cases) and
ColdCmdNRowSlotTests (intermittent, 3 of 5) were red on feat-cmux-next.
Austin's report: a new workspace row draws at the end of the list, then
moves to its slot.

Event order in the failing runs (SLOTDBG trace, job c8b7e3ff4e954c8e515b260c):
1. The press draws the pending row at its slot.
2. Reconcile: the daemon's workspace is live in the store;
   applyPendingPlacements takes its entry and clears it.
3. The placement's own refresh (placementSections, sameTurn.syncNow) runs
   a rows pass before the move is sent. The sidebar document does not
   place the item yet and does not lag (version == shownVersion), so
   PendingWorkspaceRows.listing listed the workspace's own row at the
   daemon's place (the end) and forget dropped the pending row. That frame
   was drawn.
4. bridge.place sends the move; the next frame has the row at its slot.
A passing run is one where the store still showed the stand-in at step 3.
The race is old (red at 58af48e, #27014, and at every later point
tried), not a recent regression.

Fix: a workspace whose slot WindowManager.pendingPlacements holds keeps
its pending row at that slot until the placement is sent. The entry is
cleared after the refresh, right before the move, and the observed
PendingWorkspaceRows.unsentSlots (kept per id from pendingPlacements'
didSet, so two apps in one process do not overwrite each other) re-runs
the rows pass when it goes, also for a placement that sends no move.
An anchor wait now updates its entry in place instead of clearing and
setting it again. No wait, retry or delay.

Line-neutral in CmuxNextApp (arch-ratchet app_lines).
@lawrencecchen lawrencecchen added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Nov 1, 2026
@github-actions

github-actions Bot commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

cmux status (one comment, edited in place; each line is updated by the job that owns it)

Review 4cad31b: HQ feed card · gallery before/after runs · checks

App of this head (app.zip, kept 1 day)

gh workflow run reload-build.yml -R manaflow-ai/cmux --ref feat-cmux-next -f tag=pr31069 -f ref=fix/base-red-cmdn-row-slot -f platform=macos -f backend_mode=local

Shipped in:

The feat-cmux-next push run https://github.com/manaflow-ai/cmux/actions/runs/41330433296 at e0fa06f failed: cmux-next crash ends the app.
Those jobs last passed at 45b916e. This pull request is one of 5 merged since: #31133, #30400, #31145, #31066, #31069.
If the failure is in your change, fix forward on feat-cmux-next. Pull requests run only the tiers their paths reach (docs/ci/cmux-next-tiers.md); label a batch PR full-ci to run them all before merging.

@blacksmith-sh

blacksmith-sh Bot commented Nov 1, 2026

Copy link
Copy Markdown

Found 4 test failures on Blacksmith runners:

Failures

Test View Logs
main.DeveloperIdAppTests.test_download_prints_the_active_profile_only/
test_download_prints_the_active_profile_only
View Logs
stubs: /System/Library/Frameworks/Security.framework/Versions/A/Security has no stub/
Security has no stub
View Logs
stubs: CoreFoundation.tbd: install-name /System/Library/Frameworks/
CoreFoundation.framework/Versions/A/CoreFoundation, missing 1: ['_CFRetain']/
CoreFoundation, missing 1: ['_CFRetain']
View Logs
stubs: CoreFoundation.tbd: install-name /System/Library/Frameworks/
CoreFoundation.framework/Versions/A/CoreFoundation, missing 1: ['_log2']/
CoreFoundation, missing 1: ['_log2']
View Logs

Fix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

@lawrencecchen

Copy link
Copy Markdown
Contributor Author

Independent review subagent (correctness first), read-only, no build on the reviewing machine. It checked every path that sets or clears pendingPlacements, observation and re-entry of unsentSlots, the per-id set update with two apps in one process, the anchor-wait in-place edit, and rows held longer than before. Non-blocking note: while a placement waits for the current workspace's row (at most 3 s, anchorWaitLimit), the new row now stays hidden instead of drawing at the end; that end frame is the bug these tests catch. tooling-tests red on this head is a base finding (tests/test_ci_overflow_pick_reads_pool.py: "linux-guard lane is not run by any workflow"), not from this PR. Fleet compile of the head: 1ced2b42d6ec67a8aab7ed46 done.

Review (subagent, correctness first): SHIP at 4cad31b

@lawrencecchen lawrencecchen added the ready-to-land Reviewed at this head; next-autoland merges it into feat-cmux-next when its only reds are base reds label Nov 1, 2026
@lawrencecchen

Copy link
Copy Markdown
Contributor Author

Review (subagent, correctness first): SHIP at 4cad31b

  • unsentSlots mirrors pendingPlacements keys through its didSet, so every removal path also clears it: the move send (+Placement:93), the stale anchor-wait cleanup (:54), window gone (:67), WorkspaceSpawn:131. In-place anchor-wait updates keep the same keys and are no-ops. The 3 s limit ends in the move send. A held entry only hides the daemon row while the pending row shows at its slot, so no row can stay hidden.
  • No sleep, wait, retry or debounce is added; the existing retryDeferredPlacements timer is unchanged. The main-thread cost is one set update per add or remove.
  • The race is closed. syncNow now runs with the entry held, so listing keeps the pending row at its slot. The entry then clears right before bridge.place, and the observed set re-runs the rows pass, also when no move is sent.
  • Minor, not blocking: unsentSlots lives in the shared singleton, so an abandoned WindowManager leaves its ids there (test-only; ids are unique).
  • The body evidence is consistent: red at base and green at head per suite, with job ids, on the scratch pair because of the unrelated Add Swift tests for Cloud repository forwarding #30832 compile red. SidebarCmdNumberTwice is red at both and is called out as a base finding.
  • mergeable is MERGEABLE. feat has no commits on the three files since the merge-base.

This branch was previously deployed

1 inactive deployment
ci-routing-agent — 4cad31b7 Deployed Nov 1, 2026 by lawrencecchen via namespace autofix #11394
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-to-land Reviewed at this head; next-autoland merges it into feat-cmux-next when its only reds are base reds risk:normal Risk tier (docs/ci/risk-tiers.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant