Skip to content

cmux NEXT: no daemon intent error reaches a toast; a slow move retries quietly - #34630

Merged
teamleaderleo merged 4 commits into
feat-cmux-nextfrom
lane/intent-timeout-no-toast
Nov 4, 2026
Merged

teamleaderleo merged 4 commits into
feat-cmux-nextfrom
lane/intent-timeout-no-toast

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Nov 4, 2026 •

Copy link
Copy Markdown
Collaborator

What

Leo, on a toast that read "Move did not apply: set-personal-workspace: timed out: workspace.place (no reply within 2.0 seconds)": "did you encounter this? i dont like chatty unactionable toasts".

No daemon intent error reaches a toast any more. Every notice for an ActionWorkFailure now goes through one policy, ActionWorkFailure.notice(title:) (CmuxNextActions):

Failure Before After
Reply missed its deadline (timed out, may still apply) "Move did not apply: set-personal-workspace: timed out: workspace.place (no reply within 2.0 seconds)" nothing; the change is taken back and the rows re-read the store
Connection down or dropped " did not apply: : not connected to cmux-tui" " did not apply." (a click is never silently undone, #26406)
Daemon refusal (its own text, ids, codes) " did not apply: : failed: [code]" " did not apply.", or "That did not apply." for a key or menu run with no title (the daemon text stays in the log and for CLI/MCP callers)
A reason in the person's words (typed refusal, disk full, restore still running, a capability the daemon lacks) shown shown, unchanged

A workspace placement (a sidebar drag, the "Move" above) whose reply misses its deadline is now sent again quietly, using the retry policy and backoff #34405 added for group folds (SidebarFoldPersist.resending; about 0.5/1/2/4 s, 4 retries). A placement sets an absolute place, so a second send does no harm. After the last retry the queue gives up and re-reads the rows, with no notice.

Choke points that now apply the policy:

  • ActionRegistry reporters: settle (.applied, .appliedResult), reportFailure (every sidebar save: move, fold, rename, pin, delete, ungroup), trackOrReport and trackOrRefuse.
  • New ActionRegistry.refuse(failure:) for handlers that showed refuse(failure.message). Moved over: the app context's report (now trackOrRefuse), Stash, the agent pane create, Copy Notification, and remote New Terminal. The screenshot HUD uses notice(title:).
  • Failures that put a command label, code or op into a person-facing message are now ActionWorkFailure(log:reason:): Cloud handlers, App Store refusals, agent configuration, the pending-pane create, and DaemonService's not-connected path.

#34628 (merged while this was open) gives a missed deadline a reason in words ("may still apply"). That reason stays on the failure for other surfaces; the toast for a missed deadline still shows nothing. A workspace close that may still happen (#30915) keeps its notice.

Prior art: #34405 (fold retry; its policy is reused, not duplicated), #32942 (a gone workspace's placement is dropped, not toasted), #26406 (optimistic rollback with an error, still kept: a real refusal still says the change did not apply). #34425 could not be found as a PR in this repo, so this PR does not depend on it.

Tests

Red at base, green at head (they use only base APIs):

  • ActionWorkFailureNoticeTests.aMissedDeadlineShowsNoNotice (CmuxNextActionsTests)
  • DaemonFailureNoticeTests.aTimedOutPlacementShowsNoNotice, .aLostConnectionSaysOnlyThatTheMoveDidNotApply, .aDaemonRefusalShowsOnlyThatTheMoveDidNotApply, .noDaemonErrorLeaksIntoANotice (CmuxNextAppTests)

New with this API:

  • SidebarResendingTests.aSlowSaveIsSentOnceMoreAndSucceeds: one simulated timeout gives exactly one retry and no notice. Also .aRefusalIsNotSentAgain and .aSaveThatNeverAnswersStopsAfterTheRetries.
  • ActionWorkFailureNoticeTests.aDaemonRefusalSaysOnlyThatTheChangeDidNotApply, .aReasonInThePersonsWordsShows, .noNoticeCarriesAnOpNameOrATimeout, .aMissedDeadlineOnARunNobodyAwaitsRefusesQuietly.
  • Guard: ActionWorkFailureNoticeTests.sourcesNeverShowARawFailureMessage fails when a new source line shows refuse(failure.message) or refusalHUD.show(failure.message). A typed ActionFailure caught just above is allowed, since that is already the person's sentence. noNoticeCarriesAnOpNameOrATimeout fails when any notice has a kebab-case or dotted op name, "timed out", "no reply within" or a [code].

Risk

risk:normal. This changes which failures show a notice. A real daemon refusal still shows " did not apply.", but without the daemon's reason text. CLI, socket and MCP callers still get the full message. A placement now holds the placement queue for up to about 7.5 s of backoff while a slow daemon catches up; later placements wait behind it instead of failing. The daemon's own latency (why workspace.place takes over 2 s) is handled separately.

Follow-up (not in this PR): web pages show raw host error text in several inline notices (settings, home channels, files); filed separately.

Observed / Not run

Observed: prepush gate clean; fleet compile check of CmuxNext passed at 437a066 (merged with base). Fleet Mac suites at that head (cmux-ci run --class light --script scripts/ci/package-test-lane.sh --arg=suite, step 80cbd35af5846b3223eeb947): ActionWorkFailureNoticeTests, SidebarResendingTests, SidebarFoldPersistTests, DaemonFailureNoticeTests, DaemonTimeoutToastCopyTests, ActionEffectTests, SidebarOptimisticSaveTests, CloudDiffPlacementTests and PlacementWriteOrderTests all passed.
Not run: the red run at base (those tests are red at base by reading: base shows the raw "Move did not apply: set-personal-workspace: timed out: ..." notice); no live DEV-build repro of the slow daemon.

Review (subagent opus-5.5 medium): SHIP at 437a066. The first review BLOCKed at 9fb6029 on MainActor isolation and on silent not-connected failures; both were fixed in 65a5fc9 and b358178.
Follow-ups: #34632 (web notices). From the review: DaemonError.isLostConnection includes .timedOut, and only the order in ActionWorkFailure(_:_:) keeps a timeout silent; a test pinning that is worth adding. Also from the review: a placement step can be held for up to about 17 s while it retries.

Changelog

Fixed: A slow or failed background change no longer shows an internal error toast; a slow sidebar move is retried quietly.

Agent session: agent session

🤖 Generated with Claude Code

…s sent again quietly

A timed-out or failed daemon intent showed its raw text as a notice:
"Move did not apply: set-personal-workspace: timed out: workspace.place
(no reply within 2.0 seconds)". Every notice for an ActionWorkFailure now
goes through one policy (ActionWorkFailure.notice(title:)): a missed
deadline or a lost connection shows nothing (the change is taken back and
the rows re-read), a daemon refusal shows only "<change> did not apply.",
and a reason in the person's words (a typed refusal, a disk-full reason)
shows as before. The registry's reporters and the app's refusal sites use
it (ActionRegistry.refuse(failure:)). A workspace placement whose reply
missed its deadline is sent again quietly on #34405's fold policy
(SidebarFoldPersist.resending) before the queue gives up and re-reads.

Agent-Session: https://cmux.com/deeplink/agent?id=aid-cb01117327fc358e9ba1
Sidebar-Look: no visual change
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cursor

cursor Bot commented Nov 4, 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 teamleaderleo added leo-named-fix An obvious fix to something Leo named: lands on push, review runs in parallel risk:normal Risk tier (docs/ci/risk-tiers.md) labels Nov 4, 2026
@github-actions

github-actions Bot commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

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

Review 437a066: 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=pr34630 -f ref=lane/intent-timeout-no-toast -f platform=macos -f backend_mode=local

Shipped in:

…s daemon connection can use it

Sidebar-Look: no visual change
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-cb01117327fc358e9ba1
… did not apply

Review (subagent) found #26406's rollback-with-error tests and the diff-open
failure test rely on a not-connected failure showing a notice: a click is
never silently undone. Only a missed deadline is silent now; a refusal or a
lost connection shows "<change> did not apply." (or "That did not apply."
with no title), never the daemon's text.

Sidebar-Look: no visual change
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-cb01117327fc358e9ba1
Conflict in ActionWorkFailure+Daemon.swift with #34628, which gives a missed
deadline a reason in words. Both kept: the reason stays on the failure, and
notice(title:) shows nothing for a missed deadline (Leo: no chatty
unactionable toasts). A close that may still happen (#30915) keeps its notice.

Sidebar-Look: no visual change
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-cb01117327fc358e9ba1
@teamleaderleo
teamleaderleo merged commit 29c134a into feat-cmux-next Nov 4, 2026
181 of 194 checks passed
@teamleaderleo
teamleaderleo deleted the lane/intent-timeout-no-toast branch November 4, 2026 04:32
@lawrencecchen

Copy link
Copy Markdown
Contributor

Swift logic follow-up (tip watcher swift-logic row, STATUS 13027 "SWIFT LOGIC CULPRITS"; lead order).

This PR raised swift_logic_lines by +65 at its merge commit 29c134a: scripts/cmux-next/swift-logic-lines.py before and after the merge, with the same table (the tip's, c6be943). The largest declarations of the rise:

  • extension ActionWorkFailure in Packages/macOS/CmuxNext/Sources/CmuxNextActions/ActionWorkFailure+Notice.swift: +29 lines counted as Swift logic (its row counts it as logic)
  • extension SidebarFoldPersist in Packages/macOS/CmuxNext/Sources/CmuxNextSidebar/Logic/SidebarFoldPersist.swift: +19 lines counted as Swift logic (no row in plans/cmux-next/swift-logic-classes.tsv)
  • enum ActionWorkCause in Packages/macOS/CmuxNext/Sources/CmuxNextActions/ActionWorkFailure+Notice.swift: +10 lines counted as Swift logic (no row in plans/cmux-next/swift-logic-classes.tsv)
  • extension ActionRegistry in Packages/macOS/CmuxNext/Sources/CmuxNextActions/ActionRegistry+Work.swift: +8 lines counted as Swift logic (its row counts it as logic)

plans/cmux-next/layer-ownership.md says Rust owns every state, list and rule and Swift is the platform shell. Please move the new logic of this PR in a follow-up: to Rust (the crate that owns the area; the owner lists in STATUS 12147-12165 name the target per area), or at least out of the app into its package. If a declaration above is platform code (AppKit, Core Animation, OS calls, strings, tokens, DEBUG tooling), reply here and the table gets a PLATFORM row with your reason instead.

The swift-logic row is a report, not a gate: nothing blocks on this comment.

This branch was previously deployed

1 inactive deployment
ci-routing-agent — 437a0663 Deployed Nov 4, 2026 by teamleaderleo via wait for same-key tree publisher #109041
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

leo-named-fix An obvious fix to something Leo named: lands on push, review runs in parallel risk:normal Risk tier (docs/ci/risk-tiers.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants