Skip to content

fix(cmux-tui-core): the public fold compares a restated tab decorated, as the journal writes it - #36212

Open
lawrencecchen wants to merge 3 commits into
feat-cmux-nextfrom
fix/fold-prunes-decorated-tab-restates
Open

lawrencecchen wants to merge 3 commits into
feat-cmux-nextfrom
fix/fold-prunes-decorated-tab-restates

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Base red on feat-cmux-next (Linux, in full runs): cmux-tui-core state::group_update_latency_tests::a_persons_requests_answer_while_busy_sessions_saturate_the_registry fails in 9 of 31 full runs I have logs of: "a person's request waited behind the busy sessions: new-conversation-tab ... p95=209 to 368 ms" (budget 200 ms). Only the chat tab misses its budget.

Cause (product): a chat tab create journaled every other chat tab of its pane again. The public fold holds the decorated values the journal states (99e3260: the journal decorates every upsert with its state rows, so the fold folds the same decorated values). The projection restates a pane's browser tabs raw, and prune_stated_topology_deltas compared them raw: a chat tab's stored value has content_kind: conversation and extra.conversation / extra.browser_profile_id, its raw restatement content_kind: browser and neither, so it never matched and was never pruned. A probe on the shrunk case showed it: with 100 restated rows in, about 20 tab upserts kept per create, each an unchanged chat tab (stored conversation, restated browser, all else equal). Every create then paid the journal append, its subject index rows and the fold's decoration for every chat tab of the pane, under the registry lock that a person's next click waits on, and the 4 cmux agent open callers of the test each grew their own pane. (Workspaces already had a special case for the same mismatch: without_workspace_key.)

Fix: an upsert that the raw compare keeps and the fold holds is decorated with state::values::decorate_value (as the journal append decorates it) and compared again. The kept changes are journaled raw as before (the append decorates them). All callers take the registry connection after the prune.

Measured (probe, a pane grown to 400 chat tabs, 850 creates, Linux): journaled changes per create mean 108 -> 4 (max 402 -> 64), journal step mean 8.3 -> 2.5 ms (max 41 -> 10 ms), whole commit mean 12.9 -> 6.8 ms (max 59 -> 28 ms); a chat tab in the 400-tab pane 117 -> 92 ms. The rest of the per-pane cost is the per-tab validation of the pane's order, fixed in its own PR (linked below), and the tab-added entity build.

Commits: (1) red test state::group_update_latency_tests::a_chat_tab_create_restates_no_other_chat_tab_of_its_pane (31 tabs restated by one create on the base); (2) the fix.

Testing

  • Deterministic red, test commit 9da5a5a: Linux (job dd4f43c0dc7ae07c66e38059) and macOS (job bc1ebe45e05038393031a24b): "one chat tab create restated 31 tabs".
  • Green at 83bc15f: Linux all filter state:: (job 3a4db4b216c59e96d3c88332): 295 of 295 pass, fmt pass, clippy pass; macOS filter state:: (job 6a59e6ca2ab921260dbac0b7): 295 of 295; panic ratchet (job cbd24ba1cd0028736e4c4107) pass; projection tests filter projection (job 4a3f4afaae3b4182445e1c7a) 111 of 111, the debug cross-check included.
  • Full Linux (job b8986116df12ab276fdb86b4): the latency test passes; 4 reds outside this change (acpmux::session_pool a_dead_pooled_host..., acpmux::subsessions_safety ..., and the two machine_update_journey reds that test(cmux-tui): the machine journeys' older machine is older by its build time (#36044) #36151 fixes). More full runs for the latency test are in a comment below.
  • python3 scripts/cmux-next/prepush-gate.py: no new violations.

Observed / Not run

  • Observed: the results above and the probes (branches diag/brs-busy-nomerge, diag/brs-busy-fix-nomerge, not for merge).
  • Not run: a bench outside the test suite. 8 parallel copies of the latency test at once are red at base and head alike (base 12 of 24, head 7 of 24): 8 copies of 52 threads saturate the runner, so that is not this test's real load; full-suite runs are the load it is red under.

Changelog

Fixed: Opening a chat tab no longer slows down as its pane fills with chat tabs, and a busy session's writes keep less of the agent daemon's store lock.


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


Summary by cubic

Fixes a latency regression where each chat tab create in a pane re-journaled every other chat tab of that pane, adding the journal and fold decoration cost for all of them under the registry lock.

The public fold holds decorated values, but the projection restated tabs raw, so upserts never matched and were never pruned. Kept upserts are now decorated before comparison, as the journal would write them, and journaled raw as before. Adds a regression test asserting one chat tab create restates at most its own tabs.

Written for commit 83bc15f. Summary will update on new commits.

View guided diff Turn on auto-fix

…its pane (red: every chat tab of the pane is journaled again)
… decorated, as the journal writes it

The fold holds the decorated values the journal states (99e3260); the projection restates
a pane's browser tabs raw. A raw upsert of a decorated tab (a chat tab: content_kind
conversation, its agent session and browser profile in extra) never equalled its fold value,
so every chat tab of a pane was journaled again on each create in it: with 400 tabs in a pane,
about 400 journaled changes and a 41 ms journal step for one tab. An upsert the raw compare
keeps and the fold holds is now decorated (state/values.rs decorate_value, as the journal
append does) and compared again.
@lawrencecchen lawrencecchen added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Nov 5, 2026
@github-actions

github-actions Bot commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

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

Review fe8a354: 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=pr36212 -f ref=fix/fold-prunes-decorated-tab-restates -f platform=macos -f backend_mode=local

web and size checks: green

@blacksmith-sh

blacksmith-sh Bot commented Nov 5, 2026 •

Copy link
Copy Markdown

Found 3 test failures on Blacksmith runners:

Failures

Test View Logs
nightly cmux-tui resolve/nightly cmux-tui resolve View Logs
test_every_compile_job_names_its_warm_sources (main.ThisTree.test_every_compile_job
_names_its_warm_sources)/
test_every_compile_job_names_its_warm_sources (main.ThisTree.test_every_compile_job
_names_its_warm_sources)
View Logs
the six-hour schedule must allow 90 minutes for a cold cache build and use the matching
runner, Xcode, and visible timing output/
the six-hour schedule must allow 90 minutes for a cold cache build and use the matching
runner, Xcode, and visible timing output
View Logs

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

…lone could make it stated (review)

A held tab, screen or workspace whose raw value differs from the fold in a field decoration does
not set (a tab whose index moved after a close or drag) is kept without a query; only one that
differs at most in content_kind and extra is decorated and compared again. Panes, terminals
and browsers are never decorated.
@lawrencecchen

Copy link
Copy Markdown
Contributor Author

Review (independent review subagent, Claude): SHIP at fe8a354

Pruning is exact: the prune decorates with the state before the commit and the journal after the patch; they can differ only if the same commit writes a state row of a held resource, and none of the four commit paths does (patch apply touches only new workspaces and closed browsers, whose tabs change raw too; group-close state concerns only closing members; terminal close adds deletes). Every decoration join reads a field the raw value carries. State writes without their own delta are still journaled (the prune compares the current decoration with the fold's old value). The connection lock is reentrant and every caller opens its transaction after the prune; a decoration error fails the commit as the journal append already did. Review point taken in fe8a354: decorate only a workspace, tab or screen whose held value differs from the raw one at most in content_kind and extra, so a tab whose index moved costs no query. No new panic site. Read only; no build or test by the reviewer.

Fleet at this head: Linux all filter state:: (job 375b940b5441a35ec7bee676) 295 of 295, fmt pass, clippy pass; macOS (job 8d97e7e040957179af221314) 295 of 295; panic ratchet (job 174ae51bc4e5dd5052c41a24) pass; projections (job ebd26c220acca8536e9b1126) 111 of 111; full Linux (job 56ccc5279be1eb4b9d16143a) 17227 of 17230, the latency test passes, reds outside this change (session_pool, 2 machine_update_journey fixed by #36151). The latency test also passed in 4 more full runs at 83bc15f (jobs 6fbf33e414bb895664773a3c, 455f9e0ce5cfb8d657f7a833, 37edc52b2393ad1cc39c0828, b1d6cd581ee6171dd106beef): 5 of 5 full runs green, against 9 of 31 red on the base.

This branch was previously deployed

3 inactive deployments
artifacts — fe8a3545 Deployed Nov 5, 2026 by lawrencecchen via publish tree-addressed cmux-tui #114779
sentry-dsyms — fe8a3545 Deployed Nov 5, 2026 by lawrencecchen via upload cmux-tui dSYMs to Sentry #114779
ci-routing-agent — fe8a3545 Deployed Nov 5, 2026 by lawrencecchen via cmux-tui tree preflight #114779
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