Skip to content

cmux-next: FolderChanges arms its watches before events() returns (fixes BackdropCatalogStoreTests hang) - #26320

Merged
teamleaderleo merged 3 commits into
feat-cmux-nextfrom
ib/backdrop-catalog
Oct 28, 2026
Merged

teamleaderleo merged 3 commits into
feat-cmux-nextfrom
ib/backdrop-catalog

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes a flake in the cmux-next swift test job (seen in job 122287803089, the full parallel CmuxNextAppTests run):

✘ Test theCatalogIsBuiltOnceAndRebuiltOnlyWhenAFolderChanges() recorded an issue at BackdropCatalogStoreTests.swift:28:6: Time limit was exceeded: 60.000 seconds
✘ Test theCatalogIsBuiltOnceAndRebuiltOnlyWhenAFolderChanges() recorded an issue at BackdropCatalogStoreTests.swift:46:9: Expectation failed: store.catalog.choices.contains { $0.id.contains("mine.png") }

Cause: FolderChanges.events() returned its stream and armed the folder watches (the DispatchSource file-system sources) later, inside a .utility detached task. BackdropCatalogStore calls events(), builds the catalog and bumps builds. The test waits for that build and then adds mine.png. Under a loaded parallel run, the utility task could start only after the file was added. That change was never seen, so the wait for a second build never ended and the 60 s limit fired. The test already waits on a condition (an observed build counter) and not on a sleep. The missing event was the bug, so the time limit stays as it is.

Change: events() now arms the watches before it returns, and the loop task takes them over. So any change made after events() returns is buffered and seen. FolderChangesTests drops the 100 ms head start it gave the loop to arm, which this makes unnecessary.

Proof (fleet suite gate): at head 90b418c, BackdropCatalogStoreTests passed 5/5 and FolderChangesTests passed 5/5 (no head-start sleep). The suite gate runs one suite per process, so the load that starved the utility task in the full parallel run does not happen there. The base red is only in CI's parallel run, and at base 68f7028 the gate passes 5/5 too. The fix rests on the ordering argument above, not on a reproduction.

No capture: nothing visible changes. This is the timing of a folder watch, and the wallpaper list looks the same before and after.

Review: Codex (gpt-6-astra, xhigh) gave SHIP at head 90b418c. It checked that the non-Sendable FolderWatch moves into Task.detached under region isolation.

Risk: low. The watch opens a few microseconds earlier, on the caller's thread rather than the loop's. Cancellation and re-arming are unchanged. The only other user is BackdropCatalogStore.

Agent session: agent session

🤖 Generated with Claude Code

BackdropCatalogStoreTests.theCatalogIsBuiltOnceAndRebuiltOnlyWhenAFolderChanges
hung for its 60 s limit under the parallel run: the watch was armed by a
utility-priority detached task, which under load could start after the test
added mine.png, so no event ever came. events() now arms before it returns,
so a change after the call is always seen; FolderChangesTests drops the
100 ms head start it gave the loop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-cc8bff1c3ddb4ec353c5
@cursor

cursor Bot commented Oct 27, 2026

Copy link
Copy Markdown

Bugbot needs on-demand usage enabled

Bugbot 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.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Owned by an agent session · session

@github-actions

github-actions Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

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

Review f6e6430: 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=pr26320 -f ref=ib/backdrop-catalog -f platform=macos -f backend_mode=local

@github-actions github-actions Bot added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Oct 27, 2026
@blacksmith-sh

blacksmith-sh Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Found 6 test failures on Blacksmith runners:

Failures

Test View Logs
main.WiringTests.test_both_deferrals_ask_the_script/
test_both_deferrals_ask_the_script
View Logs
main.WiringTests.test_the_pool_matches_the_daemon_test_job/
test_the_pool_matches_the_daemon_test_job
View Logs
build_only must be a conjunctive exclusion on the helper, signing, and publication jobs
, and must not gate the unsigned app build/
build_only must be a conjunctive exclusion on the helper, signing, and publication jobs
, and must not gate the unsigned app build
View Logs
nightly.yml must derive the embedded key and sign every appcast through the helper/
nightly.yml must derive the embedded key and sign every appcast through the helper
View Logs
test_the_nightly_is_covered (main.NightlyStepCoverageTests.test_the_nightly_is_cove
red)/
test_the_nightly_is_covered (main.NightlyStepCoverageTests.test_the_nightly_is_cove
red)
View Logs
test_the_repository_highlights_validate (main.DigestTests.test_the_repository_highl
ights_validate)/
test_the_repository_highlights_validate (main.DigestTests.test_the_repository_highl
ights_validate)
View Logs

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

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Bead cx-2p0oi1 created for this PR by the cmux coordinator.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex gpt-6-astra xhigh, next-review): SHIP at 90b418c

No blocking findings.

@teamleaderleo
teamleaderleo merged commit 39d216b into feat-cmux-next Oct 28, 2026
145 of 151 checks passed
@teamleaderleo
teamleaderleo deleted the ib/backdrop-catalog branch October 28, 2026 01:56
@github-actions

Copy link
Copy Markdown
Contributor

The feat-cmux-next push run https://github.com/manaflow-ai/cmux/actions/runs/40618800354 at 46f5779 failed: cmux-next checks (god files, concurrency, crash safety, l10n), cmux-next generated files.
Those jobs last passed at af80466. This pull request is one of 3 merged since: #26320, #26071, #26338.
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.

@github-actions

github-actions Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

teamleaderleo added a commit that referenced this pull request Oct 29, 2026
…ered (#26517)

* cmux-next: FolderChanges.events() returns once its watches are registered

Post-landing review of #26320: arm() resumed each DispatchSource and returned,
but resume() only schedules the kernel registration, and dispatch_source(3)
says events before it completes are missed, so a write right after events()
returned could still be lost. arm() now waits for every source's registration
handler (events() is async), and the loop re-arms the same way before it
yields. A new test holds the watches' queue suspended and checks events()
returns only after it is let go, and that a write the moment it returns is
seen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-cc8bff1c3ddb4ec353c5

* test: start events() with Task.immediate so the readiness check is deterministic

Review of the fix: the 200 ms sleep did not guarantee the task ran while the
queue was held, so a regression could still pass. Task.immediate runs events()
on the test's thread until its first suspension, which with the fix is the
wait for registration and without it is never reached before it returns.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-cc8bff1c3ddb4ec353c5

* FolderChanges: a caller cancelled during registration gets a finished stream

Review of the fix: a cancelled caller that never iterates would leave the
detached loop holding the watch and its descriptors. events() now cancels the
watch and finishes the stream instead of starting the loop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-cc8bff1c3ddb4ec353c5

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: cmux-bot[bot] <348531757+cmux-bot[bot]@users.noreply.github.com>

This branch was previously deployed

1 inactive deployment
ci-routing-agent — 469f7dc1 Deployed Oct 27, 2026 by teamleaderleo via wait for same-key tree publisher #67852
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk:normal Risk tier (docs/ci/risk-tiers.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant