Skip to content

fix(cmux-next): unify glass surface backgrounds - #17241

Merged
teamleaderleo merged 20 commits into
feat-cmux-nextfrom
fix/feat-cmux-next-background-unify
Oct 4, 2026
Merged

teamleaderleo merged 20 commits into
feat-cmux-nextfrom
fix/feat-cmux-next-background-unify

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 4, 2026 •

Copy link
Copy Markdown
Collaborator

Lawrence feedback showed the History page, tab strip, and split panes carrying different grounds over the same wallpaper. This PR makes the theme surface token the default for strips, terminal/internal page hosts, Home, App Store, Settings, and inactive pane overlays; the sidebar tonal step remains the only intentional step. It also adds optional per-surface appearance.surfaceBackgrounds.* settings, inherited through ThemeScope and applied live.

Validation: git diff --check; exact-head fleet compile and dense before/after captures will be posted before merge.

— Glassground (callsign pending)
Run: run_bg_surface_20261004
Intention: unify cmux-next glass surfaces and land the exact-head fix.


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


Summary by cubic

Unifies glass surface backgrounds so the tab strip, terminal, browser, internal pages, Home, App Store, Settings, and inactive pane overlays share the window's one surface token, fixing the History, tab-strip, and split-pane ground mismatch. The sidebar's tonal step remains the only intentional step; tab strips no longer add a tonal step.

New settings

  • Adds optional per-surface colors under appearance.surfaces.* for sidebar, tabStrip, terminal, browser, internalPage, and splitDivider.
  • New internalPage and splitDivider surfaces extend this to History, Bookmarks, Tasks, Feed, App Store, and the pane divider line.
  • Unset entries inherit the theme ground; overrides propagate through ThemeScope and apply live, with surface selection kept internal to app scenes.
  • Regenerated the settings schema, localization files, and MDM docs for the two new surfaces.

Written for commit d9390c2. Summary will update on new commits.

Review in cubic

Use one theme ground across strips and internal surfaces, while exposing opt-in per-surface overrides for users.
@coderabbitai

coderabbitai Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Repository: manaflow-ai/cmux/.coderabbit.yaml
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 2f8e6a51-706e-476c-af66-250502ab98d7

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@cursor

cursor Bot commented Oct 4, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Capture attempt on leased cmux-mac-mini-cua-3 was blocked before app launch: the mini has no cmux-ci client in PATH or ~/.local/bin (command not found and no artifact client on disk), so the controller-CAS artifact could not be pulled without violating the no-relay rule. No app was launched; the lease is released. This is reported to leoli-24 for mini provisioning repair. Fleet compile remains green at exact head cad45c0fe052c7660cc991cbf2a7ae68610db5ec (job e88b6a2951eabe84ad98a2e4).

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Fleet capture evidence (job e88b6a2951eabe84ad98a2e4, exact source cad45c0fe052c7660cc991cbf2a7ae68610db5ec) is committed in manaflow-ai/cmux-app-screenshots at 159d50a.

The wallpaper file is bundled wheat-field-with-cypresses.jpg (public-domain/CC0 catalog item); no ESA image appears in these frames.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Exact-head capture is now also posted at 94ecd5c, from artifact job d2200f117f333cbb6d186c74 for source e32b228ad072571ee8af9f540b406aa8cb952f5d; the painting frame is pr-17241-bg-exact-head.png. The app was quit and the leased mini released after capture.

@cursor

cursor Bot commented Oct 4, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

@cursor

cursor Bot commented Oct 4, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

@github-actions

github-actions Bot commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

merge-gate: ci-status is not successful on d9390c2. A fresh merge-override: comment from a write-access collaborator is required for: CLA Assistant, CLA policy guard, ci-status. For every check, name it and link a main run that fails the same check or write 'not on main', then add a real sentence explaining why it is safe.

…cmux-next-background-unify

# Conflicts:
#	Packages/macOS/CmuxNext/Sources/CmuxNextPages/PageWebView.swift
…cmux-next-background-unify

# Conflicts:
#	Packages/macOS/CmuxNext/Sources/CmuxNextPages/PageWebView.swift
#	schemas/settings/settings-schema.json
@cursor

cursor Bot commented Oct 4, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

merge-override:

Audit for exact head d9390c2e8bd1dcb3589aecaf5659d243ce1e6310:

  • cmux-next checks (god files, concurrency, crash safety, l10n): the current feat-cmux-next base at 990a6c8c2be has the same failing script-tests result in base run 37196619002. This PR does not change those scripts.
  • cmux-next swift test: the same feat-cmux-next base run fails its package-test step at 37196619002. The exact-head fleet compile and settings export both pass, so the background-token changes are not the cause.
  • package-conventions-lint: the base checkout at 990a6c8c2be reproduces the same namespace-type lint debt. The failure is recorded in the base feat-cmux-next validation at 37196619002. The reported violations are existing shared and CmuxNext namespace enums, outside this PR's surface-background changes.
  • ios-tests: this is the routing verdict for the failed base package-conventions-lint dependency. The same base validation is 37196619002; no iOS source is changed here.
  • ci-status: green on this exact head in the current PR run. The merge-gate red is only the missing override comment.
  • CLA Assistant and CLA policy guard: not present on the feat-cmux-next base run; this PR adds no contributor or license metadata.

Safe to merge: the change is limited to making one theme-derived background token consistent across glass surfaces and adding persisted per-surface overrides. The required exact-head fleet compile passed in job 9e4a090eef5909729b1c813a, and the dense painting before/after and Settings captures are posted in the PR.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

merge-override:

Fresh override for the merge-gate policy checks on exact head d9390c2e8bd1dcb3589aecaf5659d243ce1e6310:

  • CLA Assistant: not on the feat-cmux-next base run; this PR adds no contributor metadata.
  • CLA policy guard: not on the feat-cmux-next base run; this PR adds no contributor metadata.
  • backend migrations applied: not on the feat-cmux-next base run; this PR has no backend or migration files.
  • ci-status: the current exact-head check is green in run 37197266677.

The feature is safe to merge because the only source changes are theme-derived background unification and persisted surface overrides. The base-owned cmux-next checks, cmux-next swift test, package-conventions-lint, and downstream ios-tests failures are documented with the matching feat-cmux-next run in the preceding audit comment. Exact-head fleet compile job 9e4a090eef5909729b1c813a passed.

@teamleaderleo
teamleaderleo merged commit 705d44d into feat-cmux-next Oct 4, 2026
45 of 50 checks passed
@teamleaderleo
teamleaderleo deleted the fix/feat-cmux-next-background-unify branch October 4, 2026 11:21
lawrencecchen added a commit that referenced this pull request Oct 4, 2026
705d44d (unify glass surface backgrounds) added the schema rows
appearance.surfaces.internalPage.color/opacity and
appearance.surfaces.splitDivider.color/opacity, with xcstrings in all 21
locales, but did not regenerate webviews/src/pages/settings/generated/strings.json,
so gen-strings --check and two settings page tests failed on the base and
safe-push refused webviews pushes. No new translations were needed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
lawrencecchen added a commit that referenced this pull request Oct 4, 2026
…le check

Red: cmux-next-web-bundles.yml runs only on pushes and checks neither the page
strings nor the pages bundle (#17241 merged stale Settings strings).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
lawrencecchen added a commit that referenced this pull request Oct 4, 2026
… check

#17241 merged into feat-cmux-next with stale Settings strings: the bundle
workflow ran only on pushes and checked neither the page strings nor the
React pages bundle. scripts/cmux-next/check-web-bundles.sh runs the same five
checks as safe-push.sh (gen-strings.mjs, build-pages-web.sh,
build-webviews-app.sh, build-agent-pane-web.sh, build-agent-activity-web.sh),
reports every stale one, and cmux-next-web-bundles.yml runs it on pull
requests into feat-cmux-next as well as on pushes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
lawrencecchen added a commit that referenced this pull request Oct 9, 2026
…color, opaque

Root cause: ThemeTokens.derive set stripStep to black at alpha 0 (when #17241 made strips paint the window
ground), while design-tokens.json theme.stripStep is black at 0.22 dark / 0.05 light; the space marks
blended toward it with NSColor.blended, which also blends alpha, so inactive marks drew 20% transparent
and the softening never followed the token.

Fix: stripStep is the design token again (its only consumer is the marks; strips still paint
stripBackground == window). ProfileBarView.markColor paints the step over the mark color (full strength
for the other spaces, a quarter for the current one), so the result is opaque. Palette.stripStep is read
inside the bar's theme scope (performWithTheme), as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lawrencecchen added a commit that referenced this pull request Oct 9, 2026
…color, opaque

Root cause: ThemeTokens.derive set stripStep to black at alpha 0 (when #17241 made strips paint the window
ground), while design-tokens.json theme.stripStep is black at 0.22 dark / 0.05 light; the space marks
blended toward it with NSColor.blended, which also blends alpha, so inactive marks drew 20% transparent
and the softening never followed the token.

Fix: stripStep is the design token again (its only consumer is the marks; strips still paint
stripBackground == window). ProfileBarView.markColor paints the step over the mark color (full strength
for the other spaces, a quarter for the current one), so the result is opaque. Palette.stripStep is read
inside the bar's theme scope (performWithTheme), as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
teamleaderleo added a commit that referenced this pull request Oct 10, 2026
* cmux-next sidebar: space marks paint the strip step token over their color, opaque

Root cause: ThemeTokens.derive set stripStep to black at alpha 0 (when #17241 made strips paint the window
ground), while design-tokens.json theme.stripStep is black at 0.22 dark / 0.05 light; the space marks
blended toward it with NSColor.blended, which also blends alpha, so inactive marks drew 20% transparent
and the softening never followed the token.

Fix: stripStep is the design token again (its only consumer is the marks; strips still paint
stripBackground == window). ProfileBarView.markColor paints the step over the mark color (full strength
for the other spaces, a quarter for the current one), so the result is opaque. Palette.stripStep is read
inside the bar's theme scope (performWithTheme), as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(mux): move the public option/result types and small state guards out of mux.rs

Move-only:
- mux/results.rs: run placements and results, terminal close/resolve/place/move results, spawn options and env validation, workspace placement and mutation results, sidebar plugin options and status, config reload errors, and daemon handoff requests.
- mux/guards.rs: pending terminal host binding/release guards, the pending workspace surface guard, the resource wait wake, config reload state, and the client focus record.
Public paths stay the same through re-exports. mux.rs 1301 -> 975.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* test(cmux-next): the real-click All chats test orders its window front (as the Home first-click test)

* test(cmux-next): the empty query's Recent holds any row kind the user ran (red)

A workspace, tab or setting the user ran never showed in Recent, because
Recent took only rows shown for an empty query (catalog actions). Bead
cx-9ce.27.

* feat(cmux-next): the palette's Recent holds any row kind the user ran

Recent on the empty query took only rows shown for an empty query, so a
workspace, tab or setting the user ran never appeared. Now any enabled
row with usage may be Recent; a row gated by a query prefix never is.
Eval learning cases 9/9. Bead cx-9ce.27.

* test(optchat): the probe warns when the harness does not know the compactor model (red)

Claude Code 2.1.287 on cmux-lawrence-2 does not know claude-haiku-5-5:
it prices the model at its default rates (20x) and checks it with one
more max_tokens=1 request in every session. Warn-only: one host.log line
and a compactor.model_unknown trace event; the probe still passes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* test(acpmux): the Claude Code init reports its version in the session info (red)

OptChat's compactor probe needs it to warn when the session's Claude Code
is too old to know the compactor model.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(optchat): the compactor probe warns when Claude Code does not know the model

acpmux's Claude Code adapter now puts the init's claude_code_version in
the session info. The compactor probe compares it with the first version
known to list the compactor model (claude-haiku-5-5: 2.1.293; 2.1.287
does not): an older one writes one host.log line and a
compactor.model_unknown trace event {harness, cc_version, model}. Never
a note, never a failure.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* optchat-chief: each started subagent's spawn line carries its link, [a1](cmux://chief/<home id>/session/<session id>), and the Chief names subagents with it

Lawrence 2026-10-09: "you need to be able to link to a subagent so I can
just click here to get to it". The link is the app deeplink of the
subagent's session in this Chief home; the app opens the tab that shows it.

* scripts/cmux-next: chief-subagents-live.py passes this host's subrouter route env to the app, so its Chief and compactor sign in

* test: a Chief subagent link (cmux://chief/<home id>/session/<id>) is a Home link, its click runs link.open, and link.open selects only this Chief's subagent tab (red)

* app: a Chief subagent's link in Home opens its workspace and chat tab

Lawrence 2026-10-09: "you need to be able to link to a subagent so I can
just click here to get to it". One deeplink form, cmux://chief/<home id>/
session/<session id> (DeepLink .chiefSession), written by the Chief and
rendered by Home: MarkdownLinkPolicy's extraRule allows exactly that form
(HomeAppLinks), a click goes to the host (never NSWorkspace), and the app
runs it as this build's link.open, only for its own Chief home. link.open
selects the tab that shows the session with host chief:<home id>; another
home, a session no subagent tab shows, or this Mac's own tab is refused,
and no tab is made.

* cmux-tui: failing test for browser history commands during attach

browser-back, browser-forward and browser-reload sent while a browser
surface has no CDP session are acked at enqueue and then fail in the
worker ("browser is still starting"). The new test expects a refusal
before the ack. Also covers the navigation retry when a provider lease
replacement detaches the session while Page.navigate is in flight
(passes already), and that history commands are accepted once attached.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* cmux-tui: refuse browser history commands before the surface attaches

browser-back, browser-forward and browser-reload were acked at enqueue
and then failed in the worker when the surface had no CDP session yet.
They now refuse before the ack ("browser is still starting; retry after
it attaches", or "browser failed: ..." after a permanent bootstrap
failure). They are not held: history belongs to the attached page, and
attach already loads the record's URL. The raw server commands also
refuse frontend-rendered surfaces, like browser-navigate.

back/forward/reload (browser/surface_navigation.rs) go through enqueue_history in
browser/navigation_hold.rs; the
accepted-once-attached test now checks the commands reach CDP.

Rebased by the unlanded-work audit from feat-cmux-next-browser-history-hold
(ad1a410012b4, PR 17098) onto the browser.rs module split.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* docs(cmux-next): mux-rs refactor log, final landing SHA

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* test(cmux-tui-core): reproduce the frame/resize/render/mux-reader surface lock cycle (red)

The frame path calls mark_output_dirty (mux state lock) while it holds the
terminal lock; resize holds geometry and waits for the terminal; mux threads
hold state and read geometry. The test forces that interleaving through a
cfg(test) hook at the output event.

* fix(cmux-tui-core): never call into the mux while holding the surface terminal lock

Root cause: build_frame_locked called mark_output_dirty (emit_terminal_output
takes Mux::state) with the terminal lock held. Mux code holds Mux::state while
it reads Surface::size (geometry), and a resize holds geometry while it takes
the terminal lock, so frame -> reader -> resize formed a cycle and every render
then queued behind the terminal lock. The cycle predates the surface.rs split:
the same call has been in build_frame_locked since f7a6f559a4a4 (#8698); the
move-only split did not change it.

Fix: one documented lock order on PtyTerminalRuntime (Mux::state -> geometry
-> term -> runtime -> leaf locks); no path calls an emit_terminal_* method
while it holds term or geometry.
- build_frame_locked no longer calls out. build_producer_frame takes the
  terminal guard, builds, drops the guard, then marks output dirty; the frame
  producer and publish_final_frame use it, finish_hosted_exit marks after
  unlocking.
- The local PTY reader publishes title changes after unlocking, as the hosted
  reader already did.
- Parser on_bell callbacks (local and hosted) only count bells (PendingBells);
  the reader emits them after it releases term and geometry. This also stops
  hosted replay bells being emitted under the geometry lock.

* refactor(cmux-tui-core): move configure_agent_browser_session to surface/options.rs (move-only)

Keeps surface.rs under its godfile baseline (1690 -> 1678 lines) after the
lock-order fix.

* cmux-tui: TerminalReservationRequest.tab_id after the tip moved the type to mux/results.rs

* test(home): the subagent link passes the form check, the policy and the inline parser

* cmux-tui-core: move the terminal host's main loop to shared over a HostListener seam (cx-ko2e B16)

Move-only. serve_terminal_host_stdio (bootstrap, launch, endpoint bind,
record + lease publication, Ready, accept loop) leaves mod unix for
terminal_host_runtime/shared/host_serve/stdio.rs, so the Windows host
shares one accept loop. OS edges are sys seams with the old code behind
them:
- sys::HostListener (sys/unix/listener.rs): bind = UnixListener::bind +
  set_permissions 0600 + set_nonblocking, in that order; accept drops the
  peer address; wait(waker, timeout) is the old two-descriptor poll (None
  = -1, Some(remaining) = millis + 1 saturating) and returns whether the
  waker fired, or the raw poll error. The loop keeps its handling: a
  non-EINTR error backs off, success resets the backoff and drains a
  fired waker.
- sys::adopt_launch and sys::host_signals: the Unix modules
  (unix/adopt_launch.rs, unix/host_signals.rs, now pub(crate)).
Windows stubs (HostListener, publication lock, adopt_launch, host_signals)
fail closed until sys/windows.rs. Public re-export unchanged (cfg(unix)).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* test(cmux-tui-core): a place across a group publishes the group's change (red)

GPUI (cmux2-85) found that workspace.place renumbers a group's place in the
same transaction (personal-mixed-order-v1), so the group's top_index changes,
but the commit's session.events batch has no workspace_group change: mirrors
keep a stale top_index. The new test moves a workspace across a group and
expects a workspace_group state_upsert with the new top_index, and no group
change for a place that does not cross it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(cmux-tui-core): every personal commit publishes the groups whose place it moved

A workspace.place keeps each group at its place among the other rows, so a
workspace that crosses a group changes the group's top_index in the same
transaction. apply_personal now compares the group snapshots before and
after any personal state change and adds a workspace_group state_upsert for
each changed group the change did not already publish. The home creation
(place_home_first moves home to the front) queues the same group changes.
GPUI (cmux2-85) can drop its re-read of the group list after each change.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* test(home): the subagent link test uses its own message id (the measure cache keys by id)

* test(home): the subagent link sits among the rows the https link test uses

* test(cmux-tui): a journal writer commit holds no registry or state lock (red)

Terminal output batches take the workspace registry lock for their whole
commit, including the fsync, so every create and every registry user waits
behind them. Two tests pin the target lock order (registry -> connection ->
state; the writer takes only the connection lock).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* home: HomeMarkdown's link check is MarkdownLinkPolicy, so a Chief subagent link is a link in Home's renderer too

* fix(cmux-tui): the journal writer takes only the registry connection lock

The session journal writer held the workspace registry lock for every
terminal output batch, including its fsync, so every registry user waited
behind terminal output.

The registry's SQLite connection now sits behind its own reentrant lock
(workspace_registry/registry_connection.rs). Lock order: workspace registry
-> registry connection -> state. The writer takes only the connection lock
(never registry, never state; debug-asserted on the registry mutex). Request
threads that hold the registry and then lock state take the connection
first (Mux::lock_state_pinned), so state is never held across a writer
fsync. Transactions use unchecked_transaction on the shared guard; the
writer batch append moved to journal_extensions/writer_batch.rs on the
connection. Writer-blocking tests now hold the connection lock; a new test
proves a flow waiting behind a writer commit holds no state lock.

parking_lot (arc_lock) instead of std::sync::Mutex: std has no stable
reentrant mutex, registry methods nest connection access, and the pin must
be an owned guard so its holder can still call &mut WorkspaceRegistry
methods; the writer uses its try_lock_until deadline.

Moves for the godfile limit only: presentation snapshot, resource event
page, creation lookup, plugin generation, terminal snapshot reader, the
public projection store tests and three terminal journal tests.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(optchat): the size loop retries fragments and keeps the try that keeps the most

Kept memory of the 9f851bd1dfb9 import: merge 288+32 of two 500-byte
lines became "Tests fail on pool.rs" (21 bytes): a size retry answered
with a fragment, it fit, and 32 messages' facts were lost. And when no
try fit, the loop kept the SHORTEST try, the one that dropped the most.

size_check_for (the host's size loop gives it the step's <input>):
- a reply under an eighth of the limit, for an input over the limit, is
  a fragment: it is retried ("Too short: ...") while tries are left, and
  never kept over a real try;
- of the real tries that fit, the longest is kept;
- when the tries run out with none fitting, the real try that keeps the
  most of the input's words (4+ characters) is kept, the shortest at a
  tie.
size_check_in (no input) keeps the old rules but the fragment one. Test
fakes that answered a 1,300-byte message with a 25-byte line now answer
with a fair-size line (DEPLOY_LINE).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* ci: style ratchets warn under a hard ceiling and file their debt (#18894)

* test(ci): style ratchets warn under a hard ceiling (red)

A god type, an l10n style error or a custom scrollbar under its ceiling
(GODFILES_WARN_SLACK, L10N_STYLE_WARN_MAX, SCROLLBARS_WARN_MAX) is a
ratchet debt warning; over it, it fails. Red: every hit still fails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* ci: style ratchets warn under a hard ceiling and file their debt

God files and types, l10n style errors and custom scrollbars now have a
warning tier: under its ceiling a hit is a ratchet debt annotation and
passes; over it, it fails as before. The checks job sets the ceilings
(GODFILES_WARN_SLACK=20: allowance + 20% of the budget, so a new type
fails past 1200 lines; L10N_STYLE_WARN_MAX=100; SCROLLBARS_WARN_MAX=10).
Placeholder and table errors, concurrency, notification centers and
crash safety keep no warning tier.

A new ratchet-debt job (issues: write, base code only, feat-cmux-next
pushes) keeps one open issue listing the debt at the tip and closes it
when it is paid.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(ci): ratchet ceilings never reach the checks job's script tests (red)

Job-wide, GODFILES_WARN_SLACK and SCROLLBARS_WARN_MAX reached the script
tests in the same job, which expect every hit to fail: #18894's run had
check-scrollbars.test.sh and the god-file scope tests red. Red: the
ceilings are job env.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* ci: set each ratchet ceiling on its own step

The ceilings were job env, so the checks job's script tests (which
expect every hit to fail) saw them too. Each now lives on the step of
the check it relaxes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* style(webviews): format the agent pane automation and direct tests (vp fmt; react-apps-check red)

0d93d66e8593 and 91f9f45ff124 landed lines that vp check refuses. The run on
1f33d128993c skipped react-apps-check, so the first push that ran it went red.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* cmux-tui: split-client-keys-v1 proven over the daemon socket (terminal_host_recovery/split_client_keys.rs) instead of an in-process unit test (NO UNIT TESTS rule); poison-safe registry lock in the prelaunch skip

* test(cmux-next): palette learning cases for hidden rows and Reset Ranking (red)

Behavior at the palette eval boundary (replay harness): a hidden row
leaves the results, a whole-title query still finds it, showing it again
brings it back, Reset Ranking drops a learned lift. Bead cx-9ce.27.

* backend: red behavior test, a Stack member with no permission entries answers 503 forever (cx-6wc8)

Drives the real POST /v1/hooks/stack route and the real Stack client; only
Stack's HTTP API is fake. A member with zero permissions and an owner the
team-wide list leaves out must not make the team sync answer 503.

* feat(cmux-next): hide a palette row or reset its ranking (palette-usage-v1 row controls)

Raycast bar row controls, owned by the daemon's usage history:
palette_usage.hide {key, hidden} and palette_usage.forget {key} (Reset
Ranking: forgets the row's uses and learned picks); the snapshot carries
the hidden keys (catalog 210 -> 212 ops, SDK descriptors, app-host
scopes, docs; MCP and the CLI exclude them). Both return only {revision}
and hash the key in the replay fingerprint.

App: a row's Actions offer Hide from Palette (Show in Palette when
hidden) and Reset Ranking; the store sends them in order with the
recorded runs. Ranker: a hidden row is gone from Recent and every match
except a query that is its whole title. Strings in all palette locales.

Learning cases 13/13. Bead cx-9ce.27.

* backend: red behavior test reads team.members.list by its real answer shape (cx-6wc8)

* backend: a Stack member missing from the team-wide permission list is read alone (cx-6wc8)

The team sync threw (503, Svix retried forever) when a listed member had no
entry in GET /team-permissions?team_id&recursive=true, because a truncated list
and a member with zero permissions look the same there. A project whose default
member permission set is empty made every team sync fail.

Now each such member's own permissions are read once (user_id, recursive). A
successful answer with zero permissions gives role member; an owner a truncated
list left out keeps $delete_team and stays owner; a failed direct read still
throws, so the delivery answers 503 and changes nothing. getTeamMember treats
an empty direct answer the same way. Two unit cases that asserted the old
throw are deleted; the behavior test team-stack-empty-perms covers both paths.

* cx-m0p7 adapters: red tests for the VS Code family and Zed sources and the chat index relay

* cx-m0p7 adapters: VS Code family and Zed project sources on project.sync; app relays the chat index as project.observe

* refactor(cmux-tui): image paste tests keep their #[path] files (no test moves)

NO UNIT TESTS rule (2026-10-10): a move-only crate move keeps the tests where they are. image_paste_tests.rs and image_paste_file_tests.rs stay next to their modules with the same #[path] attributes as in cmux-tui-core.

* docs(cmux-next): crate-split lane 2 step 1 line names the unchanged test files

* test(cmux-next): CEFLifecycleRaceTests runs the turn that applies a conceal

Since f688bed646c7 a conceal hides the CEF host view in a block queued for
the end of the run-loop turn (same-pane switch keeps the page window on
screen). enteringAWindowDoesNotShowAPageTheLifecycleHid still read isHidden
in the same turn and failed in 8 of 8 full fleet runs at 2b4a85dfcd52. It
now runs one zero-wait run-loop pass after the conceal and after re-entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(cmux-next): the local remote browser host's start deadline never stops a host that listened

After its sleep the deadline task returns when it was cancelled. start and
the task both run on the main actor, so a cancelled task means the host
listened or exited first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(cmux-next): DiffSidecarProcessTests deadlines run on a ManualClock

The startup, request and reply-deadline tests used the real clock with 300 ms
to 1 s limits; on a host running 32 suite processes the marker-to-reply path
took longer than 1 s (theRequestDeadlineStartsAtTheReadyMarker) and the
suite hit its time limit. They now step a ManualClock after the deadlines
are armed (sleepers(atLeast:)), and the child's go signal is a file the test
creates. The cancel test's pid file appears whole (write, then rename): the
test could read the empty file that '>' creates before echo writes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(cmux-next): LaunchMarkStaggerTests waits for the fired deadline's effect

A fired DemandTimer wakes on the global pool and hops to the main actor; 20
Task.yield calls did not cover that on a loaded host (isTitleShown false,
aws-m4pro-6 with 4 shards on one host). The test now waits (bounded) for
the mark and the status line with waitForCondition.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(cmux-next): the cloud progress view reads its typing-note color inside performWithTheme

check-theme-scope.sh was red on the tip (CloudMachineProgressView.swift:107,
rule 1) since 85007b45672e.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(cmux-next): the live-daemon harness uses 30 s test deadlines

BranchDaemonHarness connected with the product's 2 s request and 5 s
terminal start deadlines. With 4 shards on one host, create-terminal missed
5 s in BranchDaemonTests, BranchTerminalLifetimeTests and QuitSessionsTests
of one shard at once. These suites do not test deadlines (DeadlineTests
does); per the TEST GLOBAL STATE rule they now use 30 s.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(cmux-next): HomeSourceRouterRecoveryTests waits for both owners online, not 300 ms

The test slept 300 ms for both owners' first events, then dropped and
restored the local owner. On a loaded host the drop landed before the
cloud's online, so the return was a merged connection change instead of
ownerRecovered and the test hit its 60 s limit. HomeSourceRouter exposes
ownerConnections (each owner's applied connection); the test waits until
both are online.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(cmux-next): TabModel.resourceID is an observed stored property

Set from each snapshot in init and update, so an id that arrives with a
later snapshot of the same surface notifies observers (the store's waits,
views that find a tab by its public id).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(cmux-next): an agent action's page wait wakes at the page install

AppBrowserPage.awaitPage waited in Observations on
TabContentCache.existingBrowser, but the page table is not observable: a
page installed after the first look was never seen and the action waited
out its whole bound (AgentActionWaitTests.anActionWaitsForThePageThatIsStill
Starting, 1 of 8 full fleet runs at 2b4a85dfcd52, 30 s). The wait now also
reads pageInstalls.revision, which every install bumps. Verified red-then-
green on the fleet with a throwaway test (steps bc8ef772b57b1516c6d0b1c5
red, 0f4e22e36764bd848eef2fcb green against the earlier registrar fix); no
unit test lands (NO UNIT TESTS rule).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(cmux-next): RecoveryNoticeTests and the DiffSidecar exit tests stop racing the clock

RecoveryNoticeTests waited 500 Task.yield calls for the debounced draft
write, which runs off the main actor; on a loaded host (aws-m4pro-7, shared
with PR runners) it ran out first. It now waits, bounded at 30 s, for the
file. aNonZeroExitIsAFailure timed out at the real 10 s request deadline on
the same shard; the exit and empty-reply tests now use a ManualClock like
the other non-deadline tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-tui test: scoped capture reassert tests use a quiet surface

Race: scoped_focus_gained_reasserts_host_mouse_capture_after_invisible_host_reset
and scoped_resize_reasserts_host_mouse_capture_after_invisible_host_reset ran a
live shell. The tests assumed the terminal was idle between their own
vt_write and the reassert. The shell's startup output is parsed on the PTY
reader thread while it holds the terminal lock, so try_pointer_semantics
reported Contended. reassert_scoped_host_terminal_state then kept the applied
state (left: Some(true), right: None), or desired_host_mouse_capture fell
back to false. Both are the designed contention behavior. The tests raced
the shell.

Fix: the surface writes nothing until it reads a line
(crate::test_wait::quiet_surface, the same fix as the sibling
desired_host_mouse_capture_follows_scoped_inner_terminal). No product change.

Red under load (Linux Testbox, cargo nextest run --workspace in parallel):
pty_mouse::scoped_* 3 of 60 runs failed at pty_mouse.rs:509, :543, :552.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* cmux-tui test: initial route fallback tests stop racing a 20 ms handshake budget

Race: initial_provider_timeout_falls_back_to_next_route and
initial_link_timeout_closes_group_and_falls_back_to_next_route gave every
route one 20 ms attempt budget and the whole selection 500 ms. The tests
assumed the budget expires only for the stalled route. The same budget also
covers the real Unix route's connect plus the client/daemon handshake. That
handshake runs CPU-bound crypto on the test's current-thread runtime. Under
full-suite load it can take more than 20 ms (the Unix route then fails) or
the whole run more than 500 ms (remote_runtime.rs:5574 and :5620).

Fix: the tests drive select_initial_route with the runtime attempt
(RuntimeInitialRouteAttempt) and one budget per route index. The stalled
route keeps 20 ms, so only the runtime's attempt timer moves selection on.
The Unix route gets a 30 s hang guard, so the handshake never races the short
timer. Same product code path, same assertions (one stalled call, one group
close, the Unix route selected). No product change. The tests move to
remote_runtime/tests/initial_route_budget.rs (test count unchanged, 2072);
remote_runtime.rs goes from 6677 to 6590 lines.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* cmux-tui tests: diagnostics for two unreproduced full-suite timeouts

deferred_input.rs:894 (right_button_capture_cannot_cross_a_new_pairing_dialog)
and remote_entry_tests.rs:88 (READ_HANG_GUARD) did not fail in 120 runs each
on a Linux Testbox: 60 under a parallel cargo nextest run --workspace, 60
under 64 CPU hogs, plus 6 concurrent full in-process runs of both test
binaries. Code reading found no lost-event or shared-state race: every
pending-mutation decrement without an event (supersede) happens on the
enqueuing thread before the wait, and each entry test has its own temp dir
and Mux.

So the waits now say why they failed:
- settle_pending_mutations (app/tests.rs) replaces the bare
  recv_timeout(...).unwrap() loops in deferred_input.rs. A timeout prints
  pending and pointer counts, elapsed time, and the settlements already
  handled, so a lost settlement can be told apart from a slow one.
- connect_as_link and response in remote_entry_tests.rs print the error
  kind, elapsed time, active connections, registered remote clients and
  whether the socket still exists.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* cmux-tui-core: move start_host_runtime to shared over a HostChild seam (cx-ko2e B17)

Move-only. start_host_runtime (the PTY reader, parser, child watcher and
exit publisher of a host) leaves unix/host_start.rs for
terminal_host_runtime/shared/host_start.rs. Its OS edges are sys seams:
- sys::HostChild: the Unix enum (Spawned | Adopted) stays in
  unix/host_start.rs with wait_for_child_exit_without_reaping (table C);
  its methods are pub(crate).
- sys::pty_poll_handle(&dyn MasterPty) -> PtyPollHandle: Unix is
  master.as_raw_fd() with the same context message.
- The drain waker pair is HostStream::pair() (std UnixStream::pair on
  Unix).
Windows stubs fail closed until sys/windows.rs. The re-export of the
host_refusal types in shared/unadoptable.rs gets
cfg_attr(not(unix), allow(unused_imports)): it warned on the Windows check
since d5137c4a1619.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* cmux-tui: split client keys socket test matches the wire error; poison-safe lock without a path

* refactor(cmux-next): palette row controls as their own type; RequestId out of resource.rs (god-file limits)

* cx-m0p7 adapters: review fixes (no partial complete lists from a busy vscdb, a failed Zed row or a scanning chat index; rank-only times; three.js folders)

* refactor(cmux-tui-core): lift the hosted reader loop out of spawn_hosted into HostedReader (move-only)

The 760-line reader-thread closure of Surface::spawn_hosted becomes the named
type surface/hosted_reader.rs: HostedReader holds the loop state, run() is the
connection loop, stream() the host stream loop, after_stream() the exit/loss
handling; hosted_reader/{output,resize,reconnect}.rs hold the transition
handlers and the reconnect retry loop. Labeled break/continue became Flow and
Option returns; the stream's frame demultiplexer, journal target and journal
reservation still live until the connection iteration ends. Clippy-only edits:
let-else to `?`, needless borrows.

surface.rs 1678 -> 942 lines (godfile baseline row removed); new files 279,
140, 144 and 280 lines. Production code only; no test moved or added.

* cmux-tui: client id lookup reads through the registry connection guard (tip changed the connection type)

* cmux-tui-core: move the standby/seeded launch to shared over a StandbyTerminalHost seam (cx-ko2e B18)

Move-only. launch_terminal_host_from, HostPresentation and
launch_terminal_host_seeded leave unix/standby.rs for
shared/attachment/launch.rs, so the daemon side launches a host the same
way on every platform. Edges are sys seams:
- sys::StandbyTerminalHost: the Unix struct stays in unix/standby.rs
  (spawn uses pre_exec setsid, table C); its fields are pub(crate) and the
  launch code uses stdin/stdout only through Write/Read (GPUI lane R2;
  the Windows stub carries io::PipeWriter / io::PipeReader).
- sys::endpoint_dir(file_owner(root)) replaces
  PathBuf::from("/tmp").join("cmux-th-<uid>") after fs::metadata(root).uid():
  the same path.
- sys::reserve_terminal_host_publication gains a Windows stub.
The sys::launch_terminal_host_from seam from B12 is removed (the function
is shared now). Re-exports keep names and cfg(unix) gating.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* S3: drop the unit tests and the reducer op (NO UNIT TESTS rule); verification moves to the tagged app via the debug socket

* cmux-tui: failing CLI behavior test for closed.delete (R102 S1b, red)

A real daemon, driven through the CLI: `closed <id> delete --members 0`
keeps the other member of a closed two-tab group, `closed <id> delete` removes a
group so `closed <id> reopen` answers resource.not_found, `closed clear
--since-ms <future>` deletes nothing, and `closed clear` empties the
history. A malformed --since-ms is a usage error.

The state CLI helpers move to tests/cli/state_helpers.rs so tests/cli.rs
stays under its size budget.

Re-applied from feat-cmux-next-reopen-closed (orphan audit lane A, bead
cx-gzh.7) as a behavior test in place of the in-process unit tests.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* cx-m0p7 store: socket-level behavior test on a real daemon replaces the in-process unit tests (NO UNIT TESTS)

* cx-m0p7 adapters: socket-level daemon test for the editor import replaces the unit tests (NO UNIT TESTS)

* Announce omnibox selection changes (#18904)

* fix(cmux-next): announce omnibox selection changes

* fix(cmux-next): expose selected omnibox rows on the AX list

* fix(cmux-next): keep the AX list type visible to tests

* fix: satisfy package namespace lint

* fix(history): needsOrigin stays on the main actor

The app build (Xcode 27) refused the nonisolated helper: it calls the
main-actor HistoryLocation.isSameSidebarItem. Fix-forward for 588d7fb6a7d5.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* cmux-tui-core: terminal_host_runtime launch.rs doc names the standby seam (B18 review nit)

Comment only.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* cmux-tui: closed.delete: delete a group or members, clear all or since (R102 S1b)

Delete Permanently and Clear Recently Closed. `closed.delete` takes
exactly one of `closed` (with optional member indexes) or `all` (with
optional `since_ms`). Deleted groups cannot be reopened and their terminal
archives go with them. A zero-member group of a deleted space keeps its
restore record. The request's idempotency key records the result, so a
retry replays it; reopen and delete serialize on one lock.

CLI: `cmux closed <id> delete [--members 0,2]` and `cmux closed clear
[--since-ms T]`. Page access needs closed:write. Spec, bindings
descriptors, app-host generated files and the CLI contract are updated.

RequestId moves to resource/request_id.rs so resource.rs stays under its
size budget.

Re-applied from feat-cmux-next-reopen-closed d2cde3b51efc (orphan audit
lane A, bead cx-gzh.7).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* cx-m0p7 store: the socket test picks a non-temporary folder

* cx-m0p7 adapters: the socket test picks a non-temporary folder

* cloud: declare the G8 approval codes on the gated Cloud ops, type them in cmux-cloud, allow the agent test team on development (cx-t2rz)

cloud.machine.create/resize/delete and cloud.snapshot.create/delete/restore
(CLOUD_APPROVAL_OPS, cx-wb5.65) declare approval.pending, approval.denied,
approval.expired and approval.too_many_pending, with the docs of the
install path. cmux-cloud declares the same codes (ops/declared.rs, checked
against the catalog by tests/manifest.rs) and maps them to
cmux.cloud.approval_pending/_denied/_expired/_too_many_pending with
details (the request id) kept, instead of protocol_error.

development only: CLOUD_ALLOWED_TEAMS adds team_6774f37212cb1152d2a9, the
personal team of the agent test account (lead decision); staging and
production are unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* cmux-next: the Mac app serves the credential relay with its install token; Cloud create and list through cmux/cloud behind CMUX_CLOUD_LINK=app (cx-wb5.63, cx-t2rz)

Credential relay (cx-wb5.63, slice B of cx-wb5.57): after each handshake
of the verified app connection (client-hello user_origin_allowed) the app
registers the provider family credential on its local daemon and answers
credential.relay with POST /v1/read (no key) or /v1/ops (key, origin)
using the install token only; a 401 mints once more; the Worker's answer
goes back unchanged. credential.session answers signed_in and team. A
non-verified connection never registers.

Cloud create and list through the Cloud app server (cx-t2rz), for builds
with CMUX_CLOUD_LINK=app; the default stays /api/vm until the first-party
app packages ship in the bundle. CloudMachineCreateFlow: a create no
person started shows the native confirmation first and sends nothing until
a click; one key, origin user; on approval.pending it shows the
confirmation if no click happened in this flow, answers that exact apr_
request as the person's session (feed.answer allow/once), then retries the
same key at most 8 times with a 1-8 s backoff through an injected clock.
The confirmation is user-only in debug.dialog (automation may only
dismiss it). Strings in the 21 Cloud languages.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* chore(cmux-next): sync the AppPlatform scopes copy after the merge

* Fix composer option selection semantics (#18910)

* test(webviews): delete the webviews unit tests (Lawrence 2026-10-10, "Delete all now")

Unit-test deletion lane, slice 1. Pure deletion: no product code change.
webviews tests before: 352 files, about 2,693 cases; after: 51 files, about
172 cases (23 of the files are the frozen settings page).

Deleted: 301 test files, about 2,521 cases (function, hook, component,
store and CSS-string tests under jsdom/happy-dom), and the fixtures and
helpers only they used (test/fixtures/markdown-roundtrip,
acpmux/conversation/fixtures, test/viewer-empty-dom.ts,
test/markdown-viewer-shell.harness.ts, popoverTriggerTesting.ts,
checkpoints/testFixture.ts). Mixed files keep only their boundary case.

Kept (not unit tests):
- real-browser lanes (requireBrowserLane): agent-pane-locale,
  composer-menu-width, desktop-feel, diff-virtualization, editor-csp,
  editor-page, files-panel-coverage, gallery-browse-layout,
  latency-harness, ui-a11y;
- palette eval replay (palette-eval);
- guards and ratchets over the tree: ui-rules, check-layers,
  check-type-scale, gallery-coverage, pane-english (scan only),
  diff-catalog-owner, desktopLayer (first-import scan only),
  cloud strings catalog check;
- generated-file and bundle checks: icons/iconPack (build_pack --check),
  protocol codegen (generated files current), app-theme (Swift vectors
  current), diff-worker-bundle, diff-label-bundle;
- shared contract vectors: protocol vectors, acpmux handoff schema pin,
  question fixtures (shared with the Swift port), gallery env vectors.
- webviews/src/pages/settings/ (FREEZE, settings slot): 23 files wait
  for a later slice.

Stale comments and docs that named deleted tests are updated; the
oxfmt ignore list drops the two deleted corpus folders.

* cloud: regenerate the catalog, the TS client and the Rust wire client for the approval codes (cx-t2rz)

Generated by bun run catalog:export, catalog:client and catalog:rust on a
Testbox; the app-host global is unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* cmux-next: one thin Base UI scrollbar (ui/ScrollArea) for the model picker's rail and list

Lawrence 2026-10-09: "i dont like the scrollbar can we use thinner one from baseui that looks very clean?"
(the rail showed the system scrollbar again; the earlier hide-classes were lost in a later landing).
ui/ScrollArea wraps Base UI ScrollArea: the viewport scrolls natively (wheel, trackpad, keys, scrollIntoView)
and hides the system scrollbar; a 6px rounded thumb with no track fades in on hover or while scrolling (no
fade with Reduce Motion), colored from the surrounding text (--ui-scroll-thumb overrides). Tailwind only, so the
pane and the pages compile the same rules. The rail and the model list use it.

test/preload.ts: jsdom lacks Element.getAnimations, which Base UI ScrollArea calls; the preload gives every
jsdom window the method (no running animations) and sets Base UI's BASE_UI_ANIMATIONS_DISABLED so popups
still close at once in jsdom. Full bun test: no new failures against the tip (784 pre-existing).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* S3: tagged-app proof harness (Cmd+D through debug.key, read-screen) and its results

* docs(cmux-next): crate-split lane 2 landing line (fdc55cb2fb50)

* cmux-next: the Cloud create retry loop is bounded by retries (check-concurrency) (cx-t2rz)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* feat(cmux-tui-core): debug-build lock-rank checker for the mux and surface locks

crate::lock_rank gives each lock of the documented order a rank: mux.state
(StateMutex) > pty.geometry > pty.term > pty.runtime > pty.taps > leaf
(pty.render, pty.title, pty.mouse_encoders, pty.last_attach_colors,
mux.default_colors). The surface locks become RankedMutex (same API as
std::sync::Mutex); a thread-local stack records held ranks, and a blocking
acquisition at a rank equal to or before a held one panics in debug and test
builds with both lock names and the acquisition site. try_lock is recorded,
not checked (it cannot deadlock). Release builds compile the rank, name and
stack out, so RankedMutex is a plain Mutex there.

The first run found one nested order the docs did not name: taps then
last_attach_colors, so attach taps get their own rank before leaf. It found
one wrong-order acquisition, in a test double: the clear-history probe locked
the terminal from inside the PTY write (runtime held); it now probes with
try_lock, which keeps its assertion.

Also: spawn_hosted moves `mux` into HostedReader and the debug-only disconnect
seam reads the surface's own mux (release clippy flagged a redundant clone).

* docs(cmux-tui-core): explain why a broken host stream keeps its journal reservation until the reconnect (cx-fr5l)

An active reservation keeps the capture epoch odd, so checkpoint capture
refuses the diverged mirror until the reconnect replaces it and records the
host_reconnect gap; releasing it at the break would publish an even epoch over
unjournaled output. An inactive reservation delays nothing (shutdown waits for
the reader first, then revokes it at once). No behavior change, so no test.

* S3: the provisional split maps columns and rows instead of indexing them (crash ratchet)

* cx-m0p7 store: keep LocalOperation in resource.rs (boundary check reads it); move encode_hex out for the god-file limit

* test(cmux-next): delete the CmuxNext unit test suites, pass 2 (Lawrence 2026-10-10, "Delete all now")

Unit-test deletion lane, slice 2. Pass 1 (dcc8cad9829b) kept 1,306 suites
under the old rule. This pass applies the chief's ruling: delete every
in-process suite (calls on internal types, in-process AppKit/WebKit/CEF,
fake daemons, red/crash/property suites); keep real-boundary suites,
guards/generators and benches.

CmuxNext tests before: 1,375 Swift files, 6,535 @Test; after: 63 files,
179 @Test. 1,312 files and 1,305 suites deleted, plus fixtures only they
read; 39 of 50 test targets are empty and leave Package.swift and
ci-target-graph.json (PKG slot f31e0a2ac07f).

Kept:
- real daemon or binary over the wire: the .liveDaemon suites
  (Branch*, BatchClose, DaemonVersionHandoff, Integration, QuitSessions,
  BranchStateResources), Mobile LiveDaemon + PhoneClientOverDaemonLane,
  FirstLaunch (same-tree cmux-tui), RemoteLocalhostLive, ChildSignalDefaults
  (posix_spawn children, named by check-crash-safety.sh);
- the control socket (chief ruling): all of CmuxNextControlTests;
- guards and generators run by scripts: ActionSurfaceParity,
  ActionContract, ActionCatalog, CLISurfaceParity, DaemonCapabilityExport,
  LinkExport, ManagedPreferencesManifest, SettingsSchemaExport, CEFShimABI;
- benches: AgentPaneTransportBench(+Parse), CmuxDialogLatencyBench,
  PageHostPoolClaimReadyBenchmark, PaletteBenchmark, OmniboxPhaseABenchmark.
Their helpers that lived in deleted suites move to helper files
(OmniboxFixtures.swift, RecordingPageProvider.swift); FirstLaunchTests
binds AppServices itself instead of calling ActionBindingCoverageTests.

The sidebar-layout-cases.json fixture stays: cmux-tui-core reads it.

CI: cmux-next.yml swift-test drops the WebKit driver and attach stress
steps (their suites are gone) and the deleted suites from the skip and
filter lists. scripts/measure/hotkey-agent-feed.sh, hotkey-resize.sh and
modifier-hints.sh only ran deleted unit suites and go too.
tests/test_cmux_next_route.py follows the smaller target graph; a test
folder with no Swift sources (a fixture another language reads) is not a
target.

* S3: SpawnOptions drops unserved fields itself (DaemonConnection stays within its size limit)

* ci(cmux-tui artifacts): cmux_next_ daemon tests build only the members that hold them

The macOS daemon test job spent 11m31s of its 14 min compiling: `cargo test
--workspace cmux_next_` builds every member's test binaries (acpmux alone has
about 45) to run tests from four members. The filter matches the test path, so
a cmux_next_ test can only live in a member whose sources name cmux_next_;
scripts/ci/cmux-next-daemon-test-packages.sh selects those (today cmux-motion,
cmux-tui, cmux-tui-core, cmux-tui-platform) and falls back to --workspace.
publish-tree and nightly-next promotion wait on this job.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(cmux-next): WebKit right and middle buttons: right presses go in as AppKit right-button events, a middle press carries button number 2 on its own Quartz form (DOM button 1), wheel events take the Quartz location AppKit gives the window point, and the web view empties the context menu of a right-click the app did not dispatch, so an agent's right-click opens no native menu (cx-ollo)

* cmux-next: harden the credential relay and the Cloud create approval (cx-t2rz security review)

P2-1: the relay serves only the app cmux/cloud and only cloud.* ops
(credential.app_refused / credential.op_refused otherwise): an install
token with origin user counts as the person on the Worker, so no other app
or op family may borrow it.
P2-2: before feed.answer the app reads integration.approval.get as the
person and requires op cloud.machine.create, state pending and exactly the
params the person confirmed; the feed item must carry the same request
and digest, tool cloud.machine.create, and poster system:cloud:<team>.
Any mismatch answers nothing and shows why. The confirmation shows the
machine size and name.
P3-1: the relay registers only on the app server link path (never with
the legacy default or in release).
P3-2: a create from the progress view's Retry (no gesture origin) asks
the person first; only an action with origin user skips the sheet.
P3-3: the session call for the approval refuses redirects and keeps no
cookies or cache.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(cmux-next): short update titles; the test feed and announcements are notice cards

Chief 2026-10-09 (round 2 of Lawrence's update card ask).

- Update titles are short and the version moves to the detail line:
  "Update Available" / "Version 1.0.0-nightly.…", "Update Check Failed",
  "Needs macOS 27.0 for …". Before, "cmux 1.0.0-nightly.376…" and
  "Couldn't Check for Up…" truncated in a 260 pt sidebar. UpdateNote's
  needsNewerMacOS carries the found version. 4 new strings (en + ja,
  19 languages needs_review).
- The test-feed notice (Use Real Feed, no x) and the announcements (their
  allow-listed Try It, an x that dismisses for good, the next one shows)
  are the shared notice card. Order: update status, test feed,
  announcement, tip; the "cmux Updated!" card waits while an update
  status or a test feed shows.
- The plain R114 card stack is gone (SidebarCard, SidebarCardView,
  SidebarCardStackView, SidebarCardStackLayout, SidebarModel.cards and
  onCardAction); its hover test goes with it. The footerCards slot stays.
  Announcements now show whether or not the sidebar chrome is revealed.
- NO UNIT TESTS (Lawrence 2026-10-10): the unit tests this change breaks are
  deleted in this diff (UpdateNoticeTests and its ManualClock copy, from
  31d888016eed / d344241b7a88; the stack hover test; two stack-card
  asserts). Proof is the tagged app on cmux-lawrence-2 through the debug
  socket with screenshots.

Bead: cx-t34p

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(cmux-next): a check while an update waits shows the waiting update

Lawrence's NIGHTLY (2026-10-09/10): 3797671859401 was downloaded and
waited for a restart; Sparkle skips every check while an update waits
("update check ignored while install is progressing"), so his manual
Check for Updates showed nothing and never saw 3800566557701.

- Check for Updates while an update is staged does not ask Sparkle (it
  would ignore the check); it logs and answers with the staged update.
- The staged update card ("cmux X is ready", Restart to Update) shows
  after such a check even under updates.notify silent, which otherwise
  hides it. Before, a silent install had no answer to a check at all.

Proof: tagged app on cmux-lawrence-2 through the debug socket (staged
update + notify silent: no card; Check for Updates: the card), with
screenshots. No unit tests (Lawrence 2026-10-10).

Bead: cx-t34p

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* ci(nightly-next): keep the acpmux and OptChat Chief dependency builds between nightlies (cx-g34i)

build-nightly-app compiled every Rust dependency of acpmux (53 s + 46 s, two
arches) and the OptChat Chief (24 s + 24 s) from scratch on each nightly-next
run, on the serial path before the Release build. Swatinem rust-cache on their
target dirs (cmux-tui/target/acpmux-app, optchat-chief/target/app), nightly-next
track only; cargo rebuilds what changed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* test(cmux-tui): drop the hand-written operation and command counts (cx-034r)

Unit-test deletion lane, slice 3 (chief order): every lane that added an
op had to edit 4+ files with a hand-written count (palette hit 211, 212,
213). The generated-from-spec checks (spec inventory, SDK sync, the set
equality of generated commands and the IR) already prove coverage.

Removed, no other assertion changed:
- cmux-tui-core resource_router tests: operations.len() == 213 (2x);
- cmux-tui transport path coverage: cases.len() == 188, catalog 213;
- check-resource-api-boundary test: operations 213;
- conformance runner: TRANSPORTED_OPERATION_COUNT (now: a non-empty
  catalog), and its test;
- SDK raw coverage tests: command count 236 and event count 69 in C++,
  Go, Java, Python, TypeScript, Zig (src and the watch example);
- spec/resource-operations-v2.md names the JSON catalog, not a number.

Pure assertion deletion: no red test first. 0 tests deleted, 0 added
(test count before == after).

* cmux-next: S3 optimistic Cmd+D off by default (cx-wb5.72)

Keys typed right after Cmd+D lost the tail of the line in 1 of 8 proof
runs. Every daemon now serves split-client-keys-v1, so the capability gate
protects no one. PaneSplitCommand.optimisticSplitEnabled = false sends every
split through the old path; the landing with the ordering fix sets it back.
split-optimistic-proof.py records the path each Cmd+D took and takes an
expected path (optimistic|old) as a gate argument.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* test(scheduler): failing tests for automations, runs and bodies in rows (g1)

Head under 100 KB with 100 automations of 20,000-character instructions
and 450 kept runs; bodies stored once with reference counts; keyset
paging for automation.list and run.list stable under inserts; run.get
joins the body; migration of an old JSON head is idempotent on a live
DO and after a rollback refill; disable, delete and a policy deny
cancel every queued run. Deterministic: reads answer without an ok
field, the cancelled-run check uses a sleeping first run, the migration
check binds the object before it reads the head.

Re-applied from orphaned fe31fa1f0ed8 + 467e890d7cb1 (cx-3bi.38).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* feat(scheduler): automations, runs and bodies in rows, keyset paging, live migration (g1)

SchedulerDO moves to row mode (plans/cmux-next/backend.md (g), DO audit
F-1). automation/<id> and run/<id> rows hold the records without their
bodies; body/<sha256> rows hold each distinct body once with a reference
count (automations plus kept runs) adjusted in the same commit; a
finished/<run> index lets prune drop the oldest finished runs past 200
without reading the open ones. A started run keeps its body hash. The
head keeps owner, settings, counters, chains and the counts
automation_count, open_runs, finished_count, row_seq. All four tables
are private, so events and snapshots carry the head only.

One lookup module (domains/scheduler-rows.ts) and one per-op store
(domains/scheduler-store.ts) replace every read of state.automations and
state.runs. New reads run.list and run.get; automation.list pages with
cursor and limit and its first page is unchanged. System op
scheduler.rows_migrate (key carries the head seq) rebuilds the rows from
an old head's maps on bind and drops the maps in one commit; the alarm
path migrates first too.

Re-applied from orphaned 7ab757b458e5 onto feat-cmux-next (cx-3bi.38).
run-policy.test.ts keeps the tip's createWithTeamDown helper (cx-nfts)
and reads next_at from the automation row. Catalog, TS client, Rust
cloud-wire client, app-host ops/scopes/d.ts and the bundled AppPlatform
scopes.json are regenerated with the repo generators, not hand-merged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* test(scheduler): failing tests for the g1 review findings

A run pruned while the dispatch loop awaits a create is skipped and
onWake goes on; a refilled head migrates by merge (maps win per id, rows
the maps lack stay with their bodies); reads on a legacy head answer the
retryable owner.migrating; run.list with a sparse filter over more than
200 kept runs scans every step and ends without an empty last page.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(scheduler): g1 review: stale dispatch rows, merge migration, legacy reads, paging

- onWake re-reads each run before it builds the dispatch params: a
  disable, delete or deny during an earlier create may have cancelled the
  run and prune may have dropped it with its body, which made requireBody
  throw and stop the wake. A missing, started or terminal run is skipped.
- scheduler.rows_migrate merges a refilled head instead of replacing the
  rows: the maps win per id, rows the maps lack stay with their stored
  bodies (dropped only when that body row is gone), and every body
  reference count is recounted.
- Reads on a legacy head answer the retryable owner.migrating, the same
  as the reducer.
- run.list looks one match ahead, so a page that ends the list has no
  cursor to an empty page.
- automation.list keeps automation_count and next_cursor optional while
  an old Worker can answer (deploy skew); catalog, TS client, Rust
  cloud-wire client and app-host outputs regenerated.
- plans/cmux-next/backend.md (g): roll-forward only, merge semantics,
  and the operator recovery when a pre-g1 build meets a migrated head.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* test(scheduler): drop the engine-level g1 unit tests (NO UNIT TESTS rule); the SchedulerDO op tests stay as the behavior proof

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* scheduler: regenerate the cloud-wire Rust client and the bundled AppPlatform scopes.json after the rebase

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* cmux-next: a Cloud create approval must be for the create's own team; the mismatch text stays localized (cx-t2rz review)

The approve step takes the backend team the relay's create bills (the
install token's team claim, captured when the create starts) and requires
the feed item's approval.team to equal it, so an identical create pending
in another team the person belongs to is never approved. The mismatch
reason goes to the log only; the person sees the localized text.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* scheduler (cx-3bi.38): regenerate the cloud-wire Rust client on the tip

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* test(rd-host): bench sends synthetic upstream frames; upstream loopback check (red: no host sink) (cx-9zn)

cmux-rd bench --upstream mic|camera|screen opens an upstream stream, sends
synthetic frames through cmux-rd-core's UpstreamSender with the host's
feedback, and reports frames sent/acked and an FNV-1a hash.
scripts/upstream-loopback.sh expects a host with --upstream-record DIR to
record every sent frame; red until the host has the recording sink.

* feat(rd-host): --upstream-record DIR development sink for viewer upstream media (cx-9zn)

The desktop host offers up_media only with a sink; it had none (NoSink),
so no viewer could send its microphone, camera or screen anywhere. The new
RecordSink (development only, off by default) writes each upstream stream
to an owner-only file (Annex-B .h264, length-prefixed .opus-packets) and
logs frames, bytes and an FNV-1a hash on close. The sink is chosen per
session; without the flag the host still offers no upstream caps.

* fix(cmux-next): the diff sidecar keeps its pipe readers until end of file

DiffSidecarProcess released the Process at the child's exit, and with it
the Pipes and their read FileHandles. When the exit was handled before the
stdout end-of-file event, the FileHandle closed its descriptor and the EOF
never came: the reply never finished and the caller waited for the request
deadline (130 s; a 60 s hang of writesTheRequestAfterTheReadyMarker... on
the ManualClock, 7 times on 2026-10-09). The invocation now holds both read
ends until each reads EOF, and releases them when the launch fails.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Bead: cx-kuxw

* fix(cmux-next): a cloud edit marks the source degraded before it subscribes

requireEditable started the conversation's subscribe and only then set
degraded. A subscribe that went live in between called recover() on a
source that was not degraded yet, so no .ownerRecovered went out and the
edit waited for a recovery nothing would send (CloudHomeSourceTests
withoutAnEchoTheEditSubscriptionEndsAtTheDeadline, 60 s, once under load).
The flag is now set in the same lock as the target read.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Bead: cx-kuxw

* test(cmux-next): live-daemon suites wait for the daemon's events, not deadlines

The harness connection (and QuitSessionsTests' relaunch connections) send
no client deadline: each request waits for the daemon's reply, so
create-terminal returns when its host is up. The 30 s bound of 57a5a8129382
only moved the line a loaded host crossed at 5 s. TerminalHosts.awaitExit
waits for kernel exit events (one process source per pid) instead of a
50 ms kill(0) poll; its 30 s bound and the suites' .timeLimit are only hang
guards.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Bead: cx-kuxw

* test(cmux-next): the palette benchmark measures the thread's CPU time

The budget read wall time, which counts time the thread waited for a core:
with 32 suite processes on one host the ranking median read 123 ms (54.8 ms
in another run) against 50 ms. The ranking runs synchronously on the calling
thread, so its CPU time is the search cost.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Bead: cx-kuxw

* fix(rd-host): review fixes for the upstream record sink (cx-9zn)

Review subagent should-fix items: the record dir must be a real 0700 dir
owned by this user; a per-session byte limit (--upstream-record-max-mb,
default 1024) and 64-file limit; a sink that cannot start falls back to
NoSink instead of dropping every viewer; unique file names; Drop logs
open streams. Bench uses the welcome's max_datagram. upstream-loopback.sh
counts host lines per run (a failed run no longer shifts later checks),
accepts paced drops, and takes PORT/PORT_NOSINK.

* test(cmux-tui): delete the unit tests of 18 leaf crates (cx-034r)

Unit-test deletion lane, slice 4a (Lawrence 2026-10-10, "Delete all now").
In-src #[cfg(test)] tests and the tests/ files that call library code are
deleted in cmux-motion, cmux-history, cmux-conversation, cmux-chat-index,
cmux-terminal-iface, cmux-terminal-sizing, cmux-tui-cloud-conversations,
cmux-theme-tokens, cmux-agent-pane-policy, cmux-tasks-core, cmux-tasks,
cmux-app-manifest, cmux-chief, cmux-terminal-client, cmux-mobile-core,
cmux-mobile-ffi, cmux-unix-socket and ghostty-vt.

Kept (boundary or cross-language contract):
- ghostty-vt tests/ (bytes in, terminal state out; chief ruling);
- cmux-terminal-client tests/route_e2e.rs and cmux-tasks tests/server.rs
  (real sockets end to end);
- shared corpora and fixtures another language replays:
  cmux-conversation conformance, cmux-chief corpus, cmux-agent-pane-policy
  parity + typescript, cmux-history fixtures, cmux-theme-tokens load,
  cmux-mobile-core terminal sizing fixtures, cmux-app-manifest schema
  fixtures + samples, cmux-tasks-core catalog (catalog <-> Op guard).
cfg(any(test, feature = "test-support")) narrows to the feature.
cmux-tui.yml: the Windows step drops cmux-chat-index, which has no tests.

Deferred to slice 4b (security and malformed-input cases need a boundary
test first): cmux-local-auth, cmux-relay, cmux-tui-machine-protocol,
cmux-tui-machine-agent-protocol, cmux-transport, cmux-encode, cmux-wg,
cmux-link.

Counts: 327 in-src tests and 189 tests/ tests deleted (516); 55 files deleted.

* cmux-tui: two terminal_host_recovery load flakes made deterministic (cx-2dc4)

pipelined_new_tabs_start_hosts_in_parallel_in_request_order asserted a
wall-clock bound (8 hosts x 400 ms ready delay < 3.2 s), which IO and CPU
load broke (40/40 red on a loaded Testbox). A debug-only host seam,
CMUX_TUI_TEST_HOST_READY_GATE, now holds every host before Ready at a FIFO
the test opens later; all records on disk while the gate is closed prove
the launches overlap, and identify must reply while the creates wait.

host_crash_is_named_with_its_panic_message: the abort seam fired 300 ms
after install, racing the owner's PTY custody (no custody, no replacement,
no host_replaced line), and systemd-coredump of the aborted 1 GB debug host
kept it alive 3-6 s under load. The seam now crashes when the test opens
<marker>.trigger, after wait_for_daemon_custody saw the daemon hold the
master and its custody thread (renamed pty-custody, visible in /proc) end,
and it disables core dumps before abort.

Loaded Testbox runs (32 vCPU; P=8+32 hogs, P=24+96 hogs+8 fsync dd,
P=16+32 hogs+workspace build): before 40/40 and 3/40 red; after 0/120 each.

* cx-m0p7 adapters: daemon-capabilities.json lists project-list-v1

* test(cmux-tui): server-stats write_path counters and the effect intent socket check (red)

Add the optional server-stats section write_path (include-gated, like
resource_projection, so older SDK decoders stay safe): effect receipt
commits carried by journal writer batches, their savepoint failures,
the batches that carried them, effect receipt commits that ran their own
transaction on a request thread, and registry lock acquisitions on a
journal writer thread (counted in release builds, process-wide).

The new socket test drives a headless daemon: terminal creates under
terminal output must commit their effect receipts as writer intents,
with no request-side effect commit and no writer registry lock. Red
today: effect_store still commits each receipt in its own request-side
transaction and fsync (effect_intents stays 0).

Spec: commands.md and sdk-schema.json gain the section; bindings
regenerated.

Bead: cx-g1fa.1

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(cmux-tui): effect receipt commits ride the journal writer batch

effect_store commit_resource_effect and commit_resource_effect_patch
become typed writer intents (journal ingress Effect events on the
durable lane). The request thread prepares the intent under the
workspace registry (validation, stored fingerprint, public-fold pruning),
the journal writer applies it inside its batch transaction under a
SAVEPOINT per intent (one shared fsync per batch), and the request
thread finishes it (projection stats, public fold) under the same
registry hold. An intent that fails its checks rolls back to its
savepoint and only its request gets the error; a SQLite failure still
fails the batch, which the writer retries or splits as before.

Lock order is unchanged: workspace registry -> registry connection ->
state. commit_resource_effect_projection drops state and the connection
pin before the receipt wait, keeps the registry (topology writers stay
serialized), and re-locks state to install the revision. The receipt
wait uses the producer deadline and commit fence: a writer error, a
dropped receipt or an expired deadline returns an error and releases
the registry. Prepare and executing transitions stay request-side.

The writer marks its whole loop iteration (batch, commit, retries,
receipt delivery) as the journal writer, so a registry lock there is
asserted in debug and counted in release (server-stats
write_path.writer_registry_locks, checked over the socket). Local commit
fallback (counted as request_effect_commits): writer disabled, stopped,
failed, not running yet, or the calling thread still holds the
connection.

The registry lock, the journal writer iteration and the registry
connection take lock ranks in the lock-rank checker (crate::lock_rank:
workspace.registry > journal.writer > registry.connection > mux.state),
so debug and test builds panic on a registry lock on the writer thread
on every pa…
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