Skip to content

fix(cmux-next checks): clear the package-deps and crash-ratchet base reds - #28036

Merged
teamleaderleo merged 4 commits into
feat-cmux-nextfrom
fix/feat-checks-red
Oct 30, 2026
Merged

teamleaderleo merged 4 commits into
feat-cmux-nextfrom
fix/feat-checks-red

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Summary

The cmux-next checks job is red on every feat-cmux-next push since 8ac1d73, so every PR that merges the base inherits it (83 PRs wait with ready-to-land). This PR fixes two of the three red checks. The third (Check the app FFI pin) needs a published artifact and is not in this PR (see the end).

Package dependency lists sorted. CmuxNextAccounts imports CmuxNextWakeups (DemandTimer in TeamInviteConsentWindow, from a0ee135) but did not declare it. The target now declares it. ui -> core is an allowed layer edge.

Crash safety (crash ratchet). Six rows rose. Each new hit is fixed in the code:

row first bad commit fix
rust cmux-tui-core unwrap 1149 -> 1150 915bab1 agent_records.lock() recovers a poisoned lock (unwrap_or_else(PoisonError::into_inner)), like the other 340 sites in the crate
swift CmuxNextSidebar dynamic_dispatch 3 -> 5 f97775b the disclosure chevron sets its turn as a typed CATransform3DMakeRotation, not with setValue(_:forKeyPath:). The turn is the glyph's only transform, so the value is the same.
swift MessagesLabHome index_subscript (banned) 0 -> 1 6ac0d64 the row bitmap write gets its slot from out.checkedIndex(i) on the same line again
swift MessagesLabHome unchecked 14 -> 16, unchecked.waived 1 -> 4 6ac0d64 PaletteScope's main-thread slot, process palette and box table, and Fixture.baseGeneration, move from nonisolated(unsafe) statics into one OSAllocatedUnfairLock. Render workers read the process palette and the base generation while the main thread writes them, so this was a real data race. PaletteBox keeps @unchecked Sendable with a reviewed crash-allow (two lets set before the box is published). This is recorded with --record-waivers: waived 1 -> 2.
swift CmuxNextDesign unchecked.waived 3 -> 4 17d64ea (#27385) the Dock policy observation keeps its object in an OSAllocatedUnfairLock(uncheckedState:), not in a hand-made @unchecked Sendable holder. The lifetime does not change: the object stays alive until termination and is then released.

The Patches/ files for Fixture, Transcript and WindowView are made again with sync-messageslab.sh --write-patches. The WindowView patch was already stale on the tip. Now --check passes. The arch ratchet's global_mutable_statics row goes down by three. Its one new lock site has a reviewed exception in arch-ratchet.json.

Owners: the sidebar owner (a590) for DisclosureChevronView (no visual change) and PL-daemon for the one agent_roster_fold.rs line.

Testing

  • python3 scripts/cmux-next/crash_ratchet.py: red on base b3587f5 (6 rows, the same as the CI log of 49d2105). On the head it prints crash-ratchet: ok.
  • python3 scripts/cmux-next/check-package-dependency-lists.py: red in CI on base (CmuxNextAccounts: imports CmuxNextWakeups but does not declare it). It passes on the head. Its self-test passes too.
  • scripts/cmux-next/check-crash-safety.sh: ok. sync-messageslab.sh --check: ok. python3 scripts/cmux-next/prepush-gate.py: no new violations.
  • Fleet compile-check of head 98a7c2c: job b9abe4f397655cc8ce7f0615 (cmux-ci compile-check --app --package CmuxNext --recipe-version 2): CmuxNext (with test targets), app and cmux-tui all ok. The first head 9d40107 also compiled (job 77d862cc17adfe165114e0c0).
  • PaletteBox keeps its reviewed @unchecked Sendable waiver (MessagesLabHome unchecked.waived 1 -> 2): a checked Sendable needs a Sendable FixtureTheme, and FixtureTheme.swift:28 holds a CGGradient, which the SDK does not mark Sendable (Swift 6 probe job e72b1f95374d27da0c9f8e94). The crash-allow names its owner and expires 2026-11-13.

Observed / Not run

Observed: the static check scripts above, red on the base and green on the head, run on cmux-orchestrator-1. Not run: Swift or Rust tests. The changes keep behavior the same (lock-guarded reads of the same values, the same transform value, the same lifetime). DockBadge's lifetime tests and the MessagesLab palette tests run in the next fleet test round.

Not in this PR: Check the app FFI pin. The FFI sources changed after pin 619b1a3. The tip's FFI sources are the same as at b02c564. The app FFI release run for that commit (40992288584) has been queued for blacksmith-6vcpu-macos-26 since 02:32Z, and 21 jobs wait for that label. When the run builds, its publish job releases cmux-app-ffi-b02c5645478... and its last job opens the re-pin PR. That PR moves the pin. The bot PR #26434 (ff720d8) is out of date.

Changelog

none

Proof

No capture: there is no visual change. The chevron gets the same transform through a typed setter, and the other changes are lock and ratchet changes.


Summary by cubic

Fixes the cmux-next checks job, which has been red on every feat-cmux-next push, so every PR that merges the base inherits it (83 PRs wait with ready-to-land). The package-deps and crash-ratchet checks now pass; the third check (app FFI pin) needs a published artifact and is not in this PR.

Package dependencies

CmuxNextAccounts now declares CmuxNextWakeups, which it imports (DemandTimer in TeamInviteConsentWindow) but never declared.

Crash ratchet

  • cmux-tui-core: the roster's agent_records read recovers a poisoned lock with unwrap_or_else(PoisonError::into_inner), matching the other 340 sites in the crate.
  • CmuxNextSidebar: the disclosure chevron sets its turn as a typed CATransform3DMakeRotation instead of setValue(_:forKeyPath:), with no visual change (same transform value).
  • MessagesLabHome: the row bitmap write gets its slot from out.checkedIndex(i) on the same line again.
  • MessagesLabHome: PaletteScope's main-thread slot, process palette, box table, and Fixture.baseGeneration move from nonisolated(unsafe) statics into one OSAllocatedUnfairLock, fixing a real data race where render workers read the process palette and base generation while the main thread writes them.
  • PaletteBox keeps @unchecked Sendable with a reviewed crash-allow (two lets set before the box is published).
  • CmuxNextDesign: the Dock policy observation keeps its object in an OSAllocatedUnfairLock(uncheckedState:) instead of a hand-made @unchecked Sendable holder, with the same lifetime.

The Fixture, Transcript, and WindowView patches were regenerated with sync-messageslab.sh --write-patches (WindowView's was already stale), the arch ratchet's global_mutable_statics row falls by three with one reviewed exception, and the static check scripts are red on the base and green on this head. Swift and Rust tests were not run.

Written for commit 9d40107. Summary will update on new commits.

View guided diff Turn on auto-fix

…cks job (package deps, crash ratchet)

Package dependency lists sorted: CmuxNextAccounts' TeamInviteConsentWindow
uses DemandTimer from CmuxNextWakeups (a0ee135); the target now declares it.

Crash safety: six crash-ratchet rows rose on the tip. Each new hit is fixed in
the code, not waived away:
- cmux-tui-core unwrap 1149 -> 1150 (915bab1): the roster's agent_records
  read recovers a poisoned lock (PoisonError::into_inner), as its 340 siblings do.
- CmuxNextSidebar dynamic_dispatch 3 -> 5 (f97775b): the disclosure chevron
  sets its turn as a typed CATransform3D, not by key-value coding.
- MessagesLabHome index_subscript (banned) 0 -> 1 (6ac0d64): the row
  bitmap write takes its slot from out.checkedIndex on the same line again.
- MessagesLabHome unchecked 14 -> 16 and unchecked.waived 1 -> 4 (6ac0d64):
  PaletteScope's main-thread slot, process palette and box table, and
  Fixture.baseGeneration, move from nonisolated(unsafe) statics into one
  OSAllocatedUnfairLock. Render workers read the process palette and the base
  generation while the main thread writes them, so this was a real data race.
  PaletteBox keeps @unchecked Sendable with a reviewed crash-allow (two lets set
  before publication): recorded with --record-waivers, waived 1 -> 2.
- CmuxNextDesign unchecked.waived 3 -> 4 (17d64ea, #27385): the Dock
  policy observation keeps its object in an OSAllocatedUnfairLock
  (uncheckedState) instead of a hand-rolled @unchecked Sendable holder.

Patches/ for Fixture, Transcript and WindowView regenerated with
sync-messageslab.sh --write-patches (WindowView's patch was already stale on
the tip); --check passes. The arch ratchet's global_mutable_statics falls by
three; the one lock is a reviewed exception.

Red on base b3587f5, green here: python3 scripts/cmux-next/crash_ratchet.py
(6 rows), python3 scripts/cmux-next/check-package-dependency-lists.py (CI run
on 49d2105).

Sidebar-Look: no visual change
@lawrencecchen lawrencecchen added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Oct 30, 2026
@lawrencecchen

Copy link
Copy Markdown
Contributor Author

Owner notice (the pre-push gate names both areas). This PR touches them only to clear the crash ratchet base red:

  • Sidebar owner (a590, plans/cmux-next/sidebar-owner.md): Packages/macOS/CmuxNext/Sources/CmuxNextSidebar/Views/DisclosureChevronView.swift. Two places that set the chevron glyph's turn by key-value coding (glyph.setValue(target, forKeyPath: "transform.rotation.z") in setOpen(_:animated:) and in layout()) now call a new private setTurn(_:), which sets glyph.transform = CATransform3DMakeRotation(angle, 0, 0, 1). The turn is the glyph's only transform, so the value is the same. The animated path (Motion.sidebar.set(glyph, Self.rotation, ...)) is unchanged. There is no visual change (Sidebar-Look: no visual change). Crash ratchet row: CmuxNextSidebar dynamic_dispatch 5 -> 3 (added by f97775b).
  • PL-daemon (session cmux-b6, plans/cmux-next/perf-lead.md): cmux-tui/crates/cmux-tui-core/src/mux/agent_roster_fold.rs, apply_agent_hook_record. One line, self.agent_records.lock().unwrap(), is now .lock().unwrap_or_else(PoisonError::into_inner). A poisoned lock is no longer a panic, and the crate's other 340 lock sites already do this. No other change on the request path. Crash ratchet row: cmux-tui-core unwrap 1150 -> 1149 (added by 915bab1).

…ble, its owner and its expiry (2026-11-13)

A checked Sendable PaletteBox needs a Sendable FixtureTheme, and
FixtureTheme.swift:28 holds a CGGradient, which the SDK does not mark
Sendable: the fleet probe e72b1f95374d27da0c9f8e94 (scratch 78d5dd9)
failed with "stored property 'gradient' ... contains non-Sendable type
'CGGradient'" (NSColor passed). The waiver stays, recorded as before.
@github-actions

github-actions Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

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

Review 5f14c23: 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=pr28036 -f ref=fix/feat-checks-red -f platform=macos -f backend_mode=local

@lawrencecchen

Copy link
Copy Markdown
Contributor Author

Review (independent review subagent, Opus 5.5): SHIP at 98a7c2c

Compile: fleet job b9abe4f397655cc8ce7f0615 at this head: CmuxNext (with test targets), app, cmux-tui all ok.
Static checks run by the reviewer: crash_ratchet.py, check-package-dependency-lists.py and check-crash-safety.sh are red on base b3587f5 (the same 6 rows, and the undeclared CmuxNextWakeups import) and pass at this head. crash_ratchet.py --baseline-against base: one recorded waiver only (MessagesLabHome unchecked.waived 1 -> 2); no counted row rises; cmux-remote expect 44 -> 39 is a true lower count. arch-ratchet.py: global_mutable_statics -3. sync-messageslab.sh --check passes.
Behavior: the chevron's typed transform gives the same model value as the KVC write (rotation is the glyph's only transform; the action is null; same transaction). DockBadge keeps the same lifetime (token invalidated, then the reference dropped). The PaletteScope lock has no re-entry (no lock body calls back), and with restores on throw. The Transcript write and the poison recovery in agent_roster_fold.rs are equivalent.

publish-before-share proof: confirmed. PaletteBox's init is fileprivate, and box(for:) makes boxes only on the main thread, inside the lock. Both stored values are lets set in init. A box reaches other threads only through the lock, a task local or a dispatched closure, and each of these orders the init before the read. FixtureTheme and its Colors hold only lets: a Sendable HomePalette, NSColors made from components, an immutable CGGradient and Bools.
exception reviewed: ok. The 2026-10-30 global_mutable_statics exception (allow 1, owner and expiry 2026-11-13) states a true reason: four unsynchronized statics become one lock, a net change of -3. It is needed because the swap rule counts the new site, and it is minimal. Note: no tool enforces the expiry.
R9: pass. The PR adds no cap, queue bound, retry or deadline.
perf claim: none. The PR moves no work onto the main thread; the main-thread palette reads take a short unfair lock.

Non-blocking follow-ups: (1) arch-ratchet.py --update to lower the recorded global_mutable_statics total; (2) make PaletteBox checked Sendable by building the CGGradient from gradientStops at draw time, before the 2026-11-13 expiry; (3) the crash ratchet does not count OSAllocatedUnfairLock(uncheckedState:) or withLockUnchecked (a gap in the rule, not a defect here).
Not run: Swift and Rust tests (static review only).

@lawrencecchen lawrencecchen added the ready-to-land Reviewed at this head; next-autoland merges it into feat-cmux-next when its only reds are base reds label Oct 30, 2026
…ecks-red

# Conflicts:
#	plans/cmux-next/arch-ratchet.json
@blacksmith-sh

blacksmith-sh Bot commented Oct 30, 2026

Copy link
Copy Markdown

Found 22 test failures on Blacksmith runners:

Failures

Test View Logs
test_every_linux_nightly_job_takes_a_runner_set_member (main.NightlyTrustedRouting.
test_every_linux_nightly_job_takes_a_runner_set_member) (job='attest-nightly-remote-dae
mon', runner='', primary='failure')/
test_every_linux_nightly_job_takes_a_runner_set_member (main.NightlyTrustedRouting.
test_every_linux_nightly_job_takes_a_runner_set_member) (job='attest-nightly-remote-dae
mon', runner='', primary='failure')
View Logs
test_every_linux_nightly_job_takes_a_runner_set_member (main.NightlyTrustedRouting.
test_every_linux_nightly_job_takes_a_runner_set_member) (job='attest-nightly-remote-dae
mon', runner='', primary='success')/
test_every_linux_nightly_job_takes_a_runner_set_member (main.NightlyTrustedRouting.
test_every_linux_nightly_job_takes_a_runner_set_member) (job='attest-nightly-remote-dae
mon', runner='', primary='success')
View Logs
test_every_linux_nightly_job_takes_a_runner_set_member (main.NightlyTrustedRouting.
test_every_linux_nightly_job_takes_a_runner_set_member) (job='attest-nightly-remote-dae
mon', runner='blacksmith-16vcpu-ubuntu-2404', primary='failure')/
test_every_linux_nightly_job_takes_a_runner_set_member (main.NightlyTrustedRouting.
test_every_linux_nightly_job_takes_a_runner_set_member) (job='attest-nightly-remote-dae
mon', runner='blacksmith-16vcpu-ubuntu-2404', primary='failure')
View Logs
test_every_linux_nightly_job_takes_a_runner_set_member (main.NightlyTrustedRouting.
test_every_linux_nightly_job_takes_a_runner_set_member) (job='attest-nightly-remote-dae
mon', runner='blacksmith-16vcpu-ubuntu-2404', primary='success')/
test_every_linux_nightly_job_takes_a_runner_set_member (main.NightlyTrustedRouting.
test_every_linux_nightly_job_takes_a_runner_set_member) (job='attest-nightly-remote-dae
mon', runner='blacksmith-16vcpu-ubuntu-2404', primary='success')
View Logs
test_every_linux_nightly_job_takes_a_runner_set_member (main.NightlyTrustedRouting.
test_every_linux_nightly_job_takes_a_runner_set_member) (job='attest-nightly-remote-dae
mon', runner='blacksmith-32vcpu-ubuntu-2404', primary='failure')/
test_every_linux_nightly_job_takes_a_runner_set_member (main.NightlyTrustedRouting.
test_every_linux_nightly_job_takes_a_runner_set_member) (job='attest-nightly-remote-dae
mon', runner='blacksmith-32vcpu-ubuntu-2404', primary='failure')
View Logs
test_every_linux_nightly_job_takes_a_runner_set_member (main.NightlyTrustedRouting.
test_every_linux_nightly_job_takes_a_runner_set_member) (job='attest-nightly-remote-dae
mon', runner='blacksmith-32vcpu-ubuntu-2404', primary='success')/
test_every_linux_nightly_job_takes_a_runner_set_member (main.NightlyTrustedRouting.
test_every_linux_nightly_job_takes_a_runner_set_member) (job='attest-nightly-remote-dae
mon', runner='blacksmith-32vcpu-ubuntu-2404', primary='success')
View Logs
test_every_linux_nightly_job_takes_a_runner_set_member (main.NightlyTrustedRouting.
test_every_linux_nightly_job_takes_a_runner_set_member) (job='attest-nightly-remote-dae
mon', runner='blacksmith-4vcpu-ubuntu-2404', primary='failure')/
test_every_linux_nightly_job_takes_a_runner_set_member (main.NightlyTrustedRouting.
test_every_linux_nightly_job_takes_a_runner_set_member) (job='attest-nightly-remote-dae
mon', runner='blacksmith-4vcpu-ubuntu-2404', primary='failure')
View Logs
test_every_linux_nightly_job_takes_a_runner_set_member (main.NightlyTrustedRouting.
test_every_linux_nightly_job_takes_a_runner_set_member) (job='attest-nightly-remote-dae
mon', runner='blacksmith-4vcpu-ubuntu-2404', primary='success')/
test_every_linux_nightly_job_takes_a_runner_set_member (main.NightlyTrustedRouting.
test_every_linux_nightly_job_takes_a_runner_set_member) (job='attest-nightly-remote-dae
mon', runner='blacksmith-4vcpu-ubuntu-2404', primary='success')
View Logs
test_every_linux_nightly_job_takes_a_runner_set_member (main.NightlyTrustedRouting.
test_every_linux_nightly_job_takes_a_runner_set_member) (job='attest-nightly-remote-dae
mon', runner='ubuntu-24.04', primary='failure')/
test_every_linux_nightly_job_takes_a_runner_set_member (main.NightlyTrustedRouting.
test_every_linux_nightly_job_takes_a_runner_set_member) (job='attest-nightly-remote-dae
mon', runner='ubuntu-24.04', primary='failure')
View Logs
test_every_linux_nightly_job_takes_a_runner_set_member (main.NightlyTrustedRouting.
test_every_linux_nightly_job_takes_a_runner_set_member) (job='attest-nightly-remote-dae
mon', runner='ubuntu-24.04', primary='success')/
test_every_linux_nightly_job_takes_a_runner_set_member (main.NightlyTrustedRouting.
test_every_linux_nightly_job_takes_a_runner_set_member) (job='attest-nightly-remote-dae
mon', runner='ubuntu-24.04', primary='success')
View Logs

...and 12 more test failures. View all on Blacksmith

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

@lawrencecchen

Copy link
Copy Markdown
Contributor Author

Review (independent review subagent, Opus 5.5): SHIP at 5f14c23

merge-only delta of feat tip 70b40c7, conflict in arch-ratchet.json resolved by keeping both 2026-10-30 entries.

  • Each entry is byte-identical to its own side (diff against 98a7c2c adds only feat's observer_sites entry; diff against 70b40c7 adds only the PR's global_mutable_statics entry). The JSON parses. Nothing else in the file changed.
  • On the 12 files of the PR, git diff 98a7c2c8e8b 5f14c2327b1 changes only arch-ratchet.json (feat's entry). The PR's change lines against the new base 70b40c7 are the same as against the old base b3587f5; only the position of one }, moved.

exception reviewed: ok (global_mutable_statics, owner and expiry 2026-11-13, unchanged from 98a7c2c)
R9: pass
perf claim: none

Static checks at this head (reviewer ran them):

  • The CmuxNextAccounts -> CmuxNextWakeups dependency is in Package.swift; CmuxNextWakeups does not depend back on CmuxNextAccounts.
  • arch-ratchet.py: no architecture count rises (global_mutable_statics -3).
  • crash-class-gate.sh --pr: ok.
  • crash_ratchet_compare.py, head against an export of 70b40c7 (as CI does): rc 0, "already red on the base; this change does not make it worse". Recorded waiver: MessagesLabHome unchecked.waived 1 -> 2 (the PaletteBox crash-allow, reviewed at 98a7c2c).
  • Base finding (does not block): the plain crash_ratchet.py is red on feat tip 70b40c7 with 6 rows. At this head 5 of those rows are clear. One row stays: MessagesLabHome: index_subscript 0 -> 1 in Packages/Shared/CmuxMessagesLab/Sources/MessagesLabHome/Cmux/HomeMapping.swift. Feat commit 71076ff added it after the old base, and it is not in a file of this PR. A follow-up must fix it.

compile 03f0c961: running; the lead adds ready-to-land only after it passes.
Not run: Swift and Rust builds and tests (static review only).

@lawrencecchen lawrencecchen added ready-to-land Reviewed at this head; next-autoland merges it into feat-cmux-next when its only reds are base reds and removed ready-to-land Reviewed at this head; next-autoland merges it into feat-cmux-next when its only reds are base reds labels Oct 30, 2026
@lawrencecchen

Copy link
Copy Markdown
Contributor Author

Compile of the exact head 5f14c23 passed: fleet job 03f0c961a98a3e0b45c9317b, CmuxNext ok, app ok, cmux-tui ok. ready-to-land is removed and added again after the merge of the feat tip 70b40c7. The remaining crash-ratchet row (MessagesLabHome index_subscript in HomeMapping.swift, from feat commit 71076ff, not a file of this PR) goes to a follow-up PR.

@lawrencecchen

Copy link
Copy Markdown
Contributor Author

State at 07:5xZ: the only red checks are base reds, not this PR's own.

@teamleaderleo

Copy link
Copy Markdown
Collaborator

Independent review (codex gpt-6.1-sol high; Astra via SR2 hung): SHIP at 5f14c23

No concrete correctness defects found. PaletteScope’s lock is not held across drawing/AppKit calls, generation arithmetic is preserved, chevron and DockBadge behavior remain correct, and all three Catalyst patches reverse-apply cleanly to the vendored sources.

No builds or tests were run, per instruction.


Coordinator check (merging under the rulebook's red-fix exception for a teammate PR that fixes a base red): check-package-dependency-lists.py passes at this head and fails on the base tip with the same finding. The crash ratchet goes from 6 rows on the base tip to 1 here. The remaining row (MessagesLabHome index_subscript in HomeMapping.swift) is byte-identical to base and comes from 71076ff, so it's not this PR's own. The only failing check here, release-notary, is a known base red; this PR touches no release-notes or workflow files.

@github-actions

github-actions Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

lawrencecchen added a commit that referenced this pull request Oct 31, 2026
… capture backdrop is DEBUG/bench only (#28263)

* fix(design): the capture backdrop panel is DEBUG and bench only, as its only callers are

An independent exception review (reward-hacking audit, arch-ratchet tip rises) found that
CaptureBackdropPattern.swift (8fb4805, #27514) ships its CaptureBackdropPanel, its willClose
observer and NSWindow.setCaptureBackdrop in Release, although its only callers
(DebugWindowSnapshot.captureBackdrop, the debug.capture_backdrop route in AppControl+Input) are
`#if DEBUG || CMUX_BENCH_HOOKS`, and its exception said "DEBUG-only". The panel, its view and the
two NSWindow members that make or read it now sit in the same `#if`; Release keeps the two read
helpers that NSWindow+Snapshot uses, as constants (no backdrop: none to skip, nothing behind).
The pattern enum stays (a value type, no observer). Release behavior: unchanged, since nothing
could make a backdrop there.

* arch-ratchet.json: 12 reviewed exceptions for the tip rises an independent reviewer judged needed; 9 unreviewed pending entries removed

Reward-hacking audit (handoff/followups.md "arch-ratchet tip rises (no exceptions)"). An independent
exception reviewer (it wrote none of the code and none of the old reasons) judged 14 candidates at
2391c5e: 12 EXCEPTION-OK, 2 FIX (AppDelegate.swift:514's quit flush, on fix/arch-rise-quit-flush;
the capture backdrop, gated in 2391c5e and then accepted). The reviewer wrote each entry's
receipt itself; each has author, owner and expiry 2026-11-30, one site per entry (allow 1):
global_mutable_statics PaletteScope lock (#28036), CrashWatcher descriptor (#22844);
stateful_singletons KillSwitches.shared (#24212), BlockingViolationTable.shared (f668c89);
observer_sites DesktopWallpaperWatch :144 :147 :148 :149 (#25308), IPhoneHostService wake (#27297),
DebugWindowRecord willClose (#23680), CaptureBackdropPattern willClose (#27514);
blocking_allow_escapes SingleInstanceGuard (#25918), plus its escape_reviews entry (read by the rule
of #28195).

Removed: the 9 PENDING entries with no review that these replace or that cover no site any more
(CrashWatcher, BlockingViolationTable, DesktopWallpaperWatch +7, SingleInstanceGuard, HomeReduceMotion
(a FIX), kill switches +2, PaletteScope +4 (statics gone) and +1, CaptureBackdropPanel). No applied
entry, total or measured_sha changes; no ratchet number is raised. --update still refuses until the
fix PRs land; it is run after them.
lawrencecchen added a commit that referenced this pull request Oct 31, 2026
…ring PaletteBox waiver goes away (#28259)

* fix(home): HomeMapping caches a thread root with updateValue (crash ratchet base red)

71076ff added `mapped.entries[root] = .some(entry)` in HomeMapping.roots,
a subscript write that the crash ratchet counts as a banned index_subscript
in MessagesLabHome (a dictionary write cannot trap, but the module also
declares an `entries` that is not a dictionary). The cache now writes with
`updateValue(entry, forKey:)`, as the same function does at line 45. The
value is the same: a found root, or nil for a miss (cached too).

Red on base 84bca8e, green here: python3 scripts/cmux-next/crash_ratchet.py
("banned index_subscript in .../Cmux/HomeMapping.swift: 1 hit(s)").

* fix(home): PaletteBox and FixtureTheme are checked Sendable; the PaletteBox waiver goes away

#28036 kept PaletteBox `@unchecked Sendable` with a crash-allow that expires
2026-11-13, because FixtureTheme stored a CGGradient, which the SDK does not
mark Sendable (fleet probe e72b1f95374d27da0c9f8e94). FixtureTheme.Colors now
keeps the sent bubble's gradient stops ([HomePalette.GradientStop], Sendable)
and makes the CGGradient from them when a bubble draws. FixtureTheme, Colors
and PaletteBox are checked Sendable, so the crash-allow is gone. PaletteScope's
lock state is Sendable too, so it uses OSAllocatedUnfairLock(initialState:)
and withLock, not uncheckedState / withLockUnchecked.

crash_ratchet.py --update-baseline: MessagesLabHome unchecked.waived 2 -> 1.

* test(home): OutgoingGradientCostBench measures one sent-bubble gradient creation (R10 number)

Prints nanoseconds per creation (p50/p95/p99/max over 20,000) for the
draw-time path (Colors.outgoingGradient: stops -> CGColor -> CGGradient) and
for a gradient from ready CGColors. A measurement, not a gate.

* fix(home): the theme keeps its gradient's ready CGColors; a draw only allocates the CGGradient

The Swift 6 probe (job cc4b222b913bea77571982d7) compiled a Sendable struct of
[CGColor]: CGColor is Sendable, CGGradient is not. So FixtureTheme.Colors
makes its CGColors and locations once, at build time, and outgoingGradient
makes only the CGGradient from them. OutgoingGradientCostBench measures that
per-draw path, and the HomeColor-stops path for comparison.

* test(home): OutgoingGradientCostBench type-checks in reasonable time

Bench be852e69acae5d2126be56ed failed to compile its own file
(OutgoingGradientCostBench.swift:20: the compiler is unable to type-check this
expression in reasonable time). The stop list is now a plain loop over typed
locals, and the report lines are built from named values.

* test(home): a palette's draws must share one gradient object (red); the draw bench replaces the creation bench

The draw bench (fleet step a8c873017d9189e8692656f7) showed a new CGGradient
per draw costs 10.8 µs more per bubble at p99 than a reused one: CoreGraphics
does about 10 µs of work on a gradient object's first draw. Creation alone is
0.5 µs, so OutgoingGradientCostBench measured the wrong thing; it is removed.

PaletteGradientReuseTests.twoDrawsInOnePaletteUseOneGradient fails at this
commit: Fixture.outgoingGradient makes a new gradient for every draw.
OutgoingGradientDrawCostBench times lookup or creation plus the draw, for
the app's path and for a new gradient per draw, in a warm and a fresh context.

* fix(home): a palette makes its sent-bubble gradient once; every draw in it reuses that object

The draw bench (fleet step a8c873017d9189e8692656f7) showed a new CGGradient
per draw costs 10.8 µs more per bubble at p99 than a reused one. PaletteBox
now makes the themed gradient once (outgoingGradient), and
Fixture.outgoingGradient returns the effective palette's gradient. The
measured (non-themed) gradients were already static lets.

CGGradient is not Sendable in the SDK, so PaletteBox keeps it in a one-let
SharedGradient wrapper (@unchecked Sendable, with a crash-allow that names
CGGradient's immutability, an owner and a review date): recorded with
--record-waivers, MessagesLabHome unchecked.waived 1 -> 2, the same count as
on the base (this PR still removes the broad PaletteBox waiver).

PaletteGradientReuseTests.twoDrawsInOnePaletteUseOneGradient goes green.

This branch was previously deployed

3 inactive deployments
artifacts — 5f14c232 Deployed Oct 30, 2026 by lawrencecchen via add the Windows daemon to the tree #81425
sentry-dsyms — 5f14c232 Deployed Oct 30, 2026 by lawrencecchen via upload cmux-tui dSYMs to Sentry #81425
ci-routing-agent — 5f14c232 Deployed Oct 30, 2026 by lawrencecchen via namespace autofix #7394
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.

2 participants