Skip to content

mux: purge per-surface agent/notification tables on close (follow-up to #7584) - #7593

Merged
lawrencecchen merged 1 commit into
mainfrom
feat-mux-verbs-cleanup
Jul 8, 2026
Merged

lawrencecchen merged 1 commit into
mainfrom
feat-mux-verbs-cleanup

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Jul 8, 2026 •

Copy link
Copy Markdown
Contributor

Follow-up to #7584, which merged before its review-bot findings could be folded in.

Real bug (flagged independently by Greptile P2, Cursor Bugbot, and CodeRabbit): the new per-surface agent_records and surface_notifications maps are populated by report-agent/notify but were never purged when a surface closes or is reaped. Because SurfaceId is monotonic, every closed tab leaked an entry for the daemon's whole lifetime, and unfiltered list-agents kept returning dead surfaces as live agents. This adds purge_surface_side_tables, called from close_surface/close_surfaces, plus a regression test asserting both side tables drop the surface on close while sibling tabs survive.

Also folds in the smaller review findings:

  • Conformance fixture implemented-verbs-json gains a requires block (the 8 protocol-6 verbs) so it skips gracefully on older servers instead of failing hard, matching the existing move-tab fixture convention.
  • Sidebar unread dot now colors by notification severity (error > warning > info), matching the tab-bar cue, instead of always using notification_info.
  • Escape the pipe characters in the spec/cli.md verb table so it renders as a table.

cargo test -p mux-core -p mux-tui green (104 tests); cargo fmt --check clean.


Note

Medium Risk
Touches the surface-close path in mux-core and fixes stale agent/notification state that affected control-socket behavior; UI and doc tweaks are low risk.

Overview
Fixes a daemon-lifetime leak where agent_records and surface_notifications were never cleared when a tab closed, so list-agents could keep reporting dead surfaces and maps grew without bound.

mux-core adds purge_surface_side_tables and invokes it from close_surface and close_surfaces, with a regression test that agent and notification entries disappear while a sibling tab remains.

Conformance implemented-verbs-json now declares a requires.commands list (protocol-6 verbs) so older servers skip the fixture instead of failing.

mux-tui sidebar unread dots use the highest-severity unread color (error > warning > info), aligned with the tab bar.

spec/cli.md escapes | in verb-table cells so Markdown renders correctly.

Reviewed by Cursor Bugbot for commit 2cbd7d2. Bugbot is set up for automated code reviews on this repo. Configure here.


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.


Summary by cubic

Fixes a leak and stale agent listings by purging per-surface agent_records and surface_notifications when a surface closes. Also aligns the sidebar unread dot with notification severity, updates the conformance fixture to skip on older servers, and fixes CLI spec table rendering.

  • Bug Fixes
    • Add purge_surface_side_tables and call it from close_surface/close_surfaces to remove closed surfaces from side tables, preventing unbounded growth and keeping list-agents accurate; includes a regression test.
    • Sidebar unread dot now colors by severity (error > warning > info) to match the tab bar.
    • Conformance fixture implemented-verbs-json adds a requires block for protocol-6 verbs to skip gracefully on older servers; escape | in spec/cli.md verb table for correct rendering.

Written for commit 2cbd7d2. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features

    • Workspace unread indicators now use severity-based colors, highlighting higher-priority notifications more clearly.
  • Bug Fixes

    • Closing a surface now fully clears its related agent and notification state, preventing stale entries from appearing later.
    • The sidebar only shows unread markers when there’s room to display them.
  • Documentation

    • Updated CLI command reference formatting for clearer table rendering.

…view findings

- Purge agent_records + surface_notifications when a surface is closed or
  reaped (close_surface/close_surfaces), fixing an unbounded per-SurfaceId
  leak and stale list-agents results for dead tabs. Flagged by Greptile,
  Cursor Bugbot, and CodeRabbit. Adds a regression test.
- Conformance fixture implemented-verbs-json gains a requires block so it
  skips gracefully on pre-protocol-6 servers instead of failing hard.
- Sidebar unread dot now colors by notification severity, matching the
  tab-bar cue instead of always using notification_info.
- Escape pipe chars in spec/cli.md verb table so it renders correctly.
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@vercel

vercel Bot commented Jul 8, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Building Building Preview, Comment Jul 8, 2026 5:21am
cmux-staging Building Building Preview, Comment Jul 8, 2026 5:21am

@coderabbitai

coderabbitai Bot commented Jul 8, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: dcc55814-8613-4955-8046-adace942e854

📥 Commits

Reviewing files that changed from the base of the PR and between def4098 and 2cbd7d2.

📒 Files selected for processing (4)
  • mux/bindings/conformance/fixtures.json
  • mux/crates/mux-core/src/mux.rs
  • mux/crates/mux-tui/src/ui/sidebar.rs
  • mux/spec/cli.md

📝 Walkthrough

Walkthrough

This PR purges agent and notification side tables when surfaces close in mux-core, refactors the TUI sidebar's unread indicator into a severity-ranked color helper (error/warning/info), adds a requires constraint to a conformance fixture, and escapes pipe characters in CLI spec documentation tables.

Changes

Surface Side-Table Cleanup

Layer / File(s) Summary
Purge helper and close-path wiring
mux/crates/mux-core/src/mux.rs
Adds purge_surface_side_tables helper that removes a surface's entries from agent_records and surface_notifications; calls it in close_surface and close_surfaces before killing surfaces and emitting TreeChanged; adds a test verifying cleanup while unrelated surfaces remain.

Sidebar Unread Indicator

Layer / File(s) Summary
Severity color helper and rendering
mux/crates/mux-tui/src/ui/sidebar.rs
Replaces workspace_has_unread boolean with workspace_unread_color, which scans tabs for unread notifications and returns the theme color for the highest severity (error > warning > info); rendering draws the dot with bold only when a color is returned.

Conformance Fixture and CLI Docs

Layer / File(s) Summary
Fixture requirements and table escaping
mux/bindings/conformance/fixtures.json, mux/spec/cli.md
Adds a requires/commands constraint to the implemented-verbs-json fixture; escapes literal pipe characters in CLI verb table rows for copy, ids, notify, list-agents, and report-agent.

Estimated code review effort: 2 (Simple) | ~12 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant Mux
  participant AgentRecords
  participant SurfaceNotifications
  Caller->>Mux: close_surface(surface_id)
  Mux->>Mux: purge_surface_side_tables(surface_id)
  Mux->>AgentRecords: remove entries for surface_id
  Mux->>SurfaceNotifications: remove entries for surface_id
  Mux->>Mux: kill surface
  Mux-->>Caller: emit MuxEvent::TreeChanged
Loading

Possibly related PRs

  • manaflow-ai/cmux#7347: Both PRs modify mux/bindings/conformance/fixtures.json requires/gating logic and mux/spec/cli.md formatting.
🚥 Pre-merge checks | ✅ 25
✅ Passed checks (25 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: purging per-surface agent and notification tables on close.
Description check ✅ Passed The description includes summary, why, testing, review trigger, and checklist; only the demo video section is omitted.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Cmux Swift Actor Isolation ✅ Passed No Swift files changed in the PR, so no actor-isolation issues were introduced or worsened.
Cmux Swift Blocking Runtime ✅ Passed The diff touches only JSON, Rust, and Markdown files; no Swift files or runtime blocking primitives were introduced.
Cmux Browser Automation Off-Main ✅ Passed PR only changes mux side tables, sidebar, fixtures, and CLI docs; no browser.*/WebKit/mainActor/socket-worker automation code is touched.
Cmux Expensive Synchronous Load ✅ Passed The diff only touches Rust/JSON/Markdown files; no Swift files or main-actor/interactive load paths were added or moved.
Cmux Cache Substitution Correctness ✅ Passed Only Rust/JSON/Markdown files changed; no Swift/TS/JS production cache substitution path is present.
Cmux No Hacky Sleeps ✅ Passed PR only changes Rust, JSON, and markdown; no TypeScript/JS/shell/runtime delay code or fixed sleeps were introduced.
Cmux Algorithmic Complexity ✅ Passed The runtime changes add only O(1) purges and a sidebar scan that matches the prior unread check; no new nested or repeated scalable-collection scans were introduced.
Cmux Swift Concurrency ✅ Passed Diff touches only Rust, JSON, and Markdown; no cmux Swift code was introduced or modified.
Cmux Swift @Concurrent ✅ Passed No Swift files are changed in the PR diff, so the Swift @concurrent annotation rule is not applicable.
Cmux Swift File And Package Boundaries ✅ Passed No Swift files were changed; the touched files are JSON, Rust, and Markdown, so the Swift boundary rule is not applicable.
Cmux Swiftpm Lockfiles ✅ Passed Diff only touches JSON, Rust, and markdown files; no Package.resolved, Package.swift, Xcode, .gitignore, or workflow changes were present.
Cmux Swift Logging ✅ Passed PASS: The diff touches only JSON/Rust/Markdown files; no Swift app/runtime code or logging statements were added or changed, so the Swift logging rule isn't implicated.
Cmux User-Facing Error Privacy ✅ Passed No added user-facing error/alert/output text in the diff; changes are internal mux cleanup and UI/docs tweaks with only generic visible strings.
Cmux Full Internationalization ✅ Passed No new user-facing localized text was added; the PR only changes behavior, tests, fixtures, and a docs/table formatting tweak, all allowed.
Cmux Swiftui State Layout ✅ Passed No SwiftUI/AppKit bridge files were changed; the patch only touches Rust TUI, JSON, and Markdown, so the SwiftUI state-layout rule doesn't apply.
Cmux Architecture Rethink ✅ Passed PASS: The PR only changes Rust/JSON/Markdown files, not Swift code, so the Swift architectural-rethink rule is not applicable.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed No Swift files were changed; the PR only touches JSON, Rust, Markdown, and TUI sidebar code, so the auxiliary-window shortcut rule is not applicable.
Cmux Source Artifacts ✅ Passed All changed paths are intentional source/docs/fixture files; no logs, caches, build output, temp dirs, or copied artifacts were added.
Cmux No Test Or Debug Seam In Production Source ✅ Passed No Swift production Sources/ files were changed; the diff only touches Rust, JSON, and markdown, so the seam rule is not applicable.
Cmux No Ambient Global State ✅ Passed No new ambient global state: the only new file-scope helper is private and pure, and the side-table purge lives as an instance method; no singleton or global mutable state was added.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat-mux-verbs-cleanup

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR cleans up per-surface mux state and small UI/docs follow-ups.

  • Purges agent and notification side tables when surfaces close.
  • Adds a regression test for close-time cleanup.
  • Colors sidebar unread dots by notification severity.
  • Adds conformance requirements for protocol-6 verbs.
  • Escapes literal pipes in the CLI Markdown table.

Confidence Score: 5/5

The changed flow looks mergeable after a small shutdown cleanup.

  • Normal close, bulk close, and reap paths clear the new side tables.
  • Shutdown can still leave killed surfaces visible through direct side-table reads during teardown.
  • The sidebar, fixture, and docs changes match the inspected data shapes.

mux/crates/mux-core/src/mux.rs

Important Files Changed

Filename Overview
mux/crates/mux-core/src/mux.rs Adds close-time cleanup for agent and notification side tables, but shutdown still kills surfaces without using the same cleanup.
mux/crates/mux-tui/src/ui/sidebar.rs Changes the workspace unread dot from a boolean info-colored marker to severity-aware color selection.
mux/bindings/conformance/fixtures.json Adds command requirements so the implemented-verbs-json fixture can skip older servers.
mux/spec/cli.md Escapes literal pipe characters inside CLI table cells.

Reviews (1): Last reviewed commit: "mux: purge per-surface agent/notificatio..." | Re-trigger Greptile

Comment on lines +424 to +427
fn purge_surface_side_tables(&self, surface: SurfaceId) {
self.agent_records.lock().unwrap().remove(&surface);
self.surface_notifications.lock().unwrap().remove(&surface);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Shutdown Leaves Stale Side Tables

When shutdown() kills the remaining surfaces, it does not call this cleanup path. If the Mux is still queried during teardown, list_agents and surface_notification can still return entries for surfaces that were just killed.

This branch was successfully deployed

1 active deployment
Preview – cmux — 2cbd7d28 Deployed Jul 8, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant