Repository navigation
Agent pane: no system call on the key path to judge who posted an event (P-MAIN S5) - #28065
Conversation
…cted EventSourceProcesses No behavior change: AgentPanePersonInput.isPerson and PersonInputCheck.isPerson read the posting process's path through one seam, EventSourceProcesses.path(of:), which still calls proc_pidpath (AgentPanePersonInput.executablePath, now with the start time around it) on the caller's thread for each event. The app builds one table in AppServices (eventSources) and passes it to PersonInputLedger, the join consent sheet's check and each AgentPaneView, so tests can inject the resolver; the next commit's regression test uses it.
…nt (red) P-MAIN S5: AgentPanePersonInput.isPerson and PersonInputCheck.isPerson call proc_pidpath on the main thread for each key and mouse event a process posts. EventSourceProcessesTests injects a resolver that records each read and its thread, and fails here: every event reads the process, on the main thread. It also covers a pid reused by a new process (a new start time is read again) and a pid with no process (not kept).
…osting process P-MAIN S5 (found by the S2 stall agent): AgentPanePersonInput.isPerson (the agent pane's gesture monitor, every key- and mouse-down) and PersonInputCheck.isPerson (PersonInputLedger, every key and mouse event of the app) called proc_pidpath on the main thread for each event a process posted (source pid not 0: Screen Sharing, Universal Control, a computer-use agent, a script). EventSourceProcesses is now a table: the key path reads it under its lock and makes no system call. A pid not read yet takes the safe default at once (not the person's: no gesture or grant is backed on a guess; the person's HID input, pid 0, never reaches the table), and the process is read once on the table's serial queue (start time, path, start time again). An exit watch armed before the read, on the same queue, removes the entry when that process ends, so a pid the kernel gives to a new process is read again. No timer decides anything (R5); one entry per live posting process, no constant cap (R9). Regression: EventSourceProcessesTests (red at the previous commit, green here).
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Review App of this head (app.zip, kept 1 day)
red: cmux-next checks (god files, concurrency, crash safety, l10n); base red: the same failure on the base fd9dbf9; does not block Shipped in:
|
…t without an exit watch (red) Lead review of #28065: (1) the first posted click of a new source (a Screen Sharing session) must not be lost to the safe default when the session moved the mouse first: the ledger now sees mouse moves, scrolls and modifier changes (primingTypes) and EventSourceProcesses.prime(_:) must queue the read, with no system call on the event path. Red here: prime does nothing. (2) SECURITY: a verdict must never outlive its process. The exit watch now reports whether the kernel attached it (a kqueue EVFILT_PROC/NOTE_EXIT with EV_RECEIPT); with no watch nothing may be kept. Red here: the entry is kept without a watch. theKernelExitWatchAttachesAsThisUser prints, on a fleet Mac, whether the watch attaches as a normal user to launchd, the WindowServer and screensharingd (exit-watch-proof: lines), and checks a child's exit reaches the handler.
… no verdict without an exit watch - PersonInputLedger also watches mouse moves, scrolls, other drags and modifier changes (primingTypes); for a posted one it queues the read of an unknown pid (EventSourceProcesses.prime, a table lookup, no system call). A remote session moves the mouse before it clicks, so its first click already counts. - SECURITY: the exit watch is a kqueue EVFILT_PROC/NOTE_EXIT registered with EV_RECEIPT, so a refusal is seen. A pid whose watch the kernel refuses is never kept: its events are not the person's, and the next event reads it again off the main thread. No old verdict can pass to a process that reuses the pid. Regression: EventSourceProcessesTests (red at the previous commit, green here).
…ocking-allow exception Lead decision on #28065: the exception stays, with an owner and an expiry.
|
Found 13 test failures on Blacksmith runners: Failures
...and 3 more test failures. View all on Blacksmith
|
…s; per-event cost (red) Review of #28065 at 3f13e1d (claude-opus-review-subagent, FIX): - finding 3: a table entry survives an exec (same pid and start time, new image). Red here: theKernelWatchFiresOnExec, a child that execs /bin/sleep, gets no notice (NOTE_EXIT only). - finding 5: theKernelExitWatchAttachesAsThisUser could hang on a nil watch; it now requires the watch, and both kernel waits fail after 10 s instead of hanging (test deadline only). - finding 1 (R10): perEventMainThreadCost prints the main-thread ns per event of the new work on the person's mouse moves, of a posted event read from the table, and of the base's per-event proc_pidpath (person-input-cost: lines). A measurement, not a time assertion.
…losed on an odd kevent receipt Review of #28065 at 3f13e1d: - finding 3: the watch is EVFILT_PROC NOTE_EXIT | NOTE_EXEC, so an entry leaves when its process runs a new program (same pid and start time, new image) as when it ends; the next event reads the new image off the main thread. - finding 4: the watch counts as attached only for exactly one EV_RECEIPT receipt with EV_ERROR and data 0; anything else is a refusal (nothing kept). Regression: EventSourceProcessesTests.theKernelWatchFiresOnExec (red at the previous commit).
…atch's source The compiler reports it unnecessary (DispatchSourceRead is Sendable).
Bugbot needs on-demand usage enabledBugbot uses usage-based billing for this team and requires on-demand usage to be enabled. A team admin can enable on-demand usage in the Cursor dashboard. |
|
Review (claude-opus-5.5-review-subagent): SHIP at ff83b4e Findings (file:line at ff83b4e, EventSourceProcesses.swift unless named):
Non-blocking notes:
|
…erson-input-pidpath # Conflicts: # Packages/macOS/CmuxNext/Sources/CmuxNextApp/AgentTabs.swift # plans/cmux-next/arch-ratchet.json
|
Merge of
I removed The finding is in this PR's own code, not in the merge.
The reviewer suggests |
…sses; a refused read is not read again per event (red) Review of #28065 at 072f126 (BLOCK): proc_pidinfo(PROC_PIDTBSDINFO) has a same-user check, so the WindowServer, launchd and root screensharingd read as nil and never count as the person's; and each of their events started a new read and a new kernel watch. aPidWithNoProcessIsNotKept becomes aProcessThatEndedBeforeItsReadIsNotKept (its watch fires: the kernel refuses a watch on a pid with no process).
…ROC_PID), which has no same-user check; keep an unreadable live process until its exit proc_pidinfo(PROC_PIDTBSDINFO) refuses another user's process (EPERM), so the WindowServer, launchd and root screensharingd were never read and never counted as the person's (join consent, the Settings full-access switch, Allow over Screen Sharing). sysctl(KERN_PROC_PID) p_starttime is public on macOS 14+ and reads them as uid 501 (fleet probe: cmux17, aws-m4pro-1, macOS 27); PROC_PIDUNIQIDENTIFIERINFO also reads them but is not in the public SDK headers. A live process that is still unreadable is kept as unreadable (the safe default) until its exit watch fires, so its events no longer post a read and a new kqueue each.
There was a problem hiding this comment.
5 issues found across 17 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="plans/cmux-next/threading-model.md">
<violation number="1" location="plans/cmux-next/threading-model.md:152">
P2: This queue has no back-pressure despite the enforced threading model requiring every queue to be bounded. Add a finite admission bound and fail closed for unknown PIDs once it is reached, while releasing the entry and watch on process exit.</violation>
<violation number="2" location="plans/cmux-next/threading-model.md:152">
P3: The one-item-per-process claim is false when exit-watch registration fails: each later event re-enqueues a read after `read(_:)` removes the entry. Document this retry path instead of claiming no back-pressure.</violation>
</file>
<file name="Packages/macOS/CmuxNext/Tests/CmuxNextAgentPaneTests/EventSourceProcessesTests.swift">
<violation number="1" location="Packages/macOS/CmuxNext/Tests/CmuxNextAgentPaneTests/EventSourceProcessesTests.swift:141">
P2: This test does not exercise the documented watch/read race because `fake.end(4343)` runs only after the read has settled. Make the fake end the process during `resolve`, so the test catches an exit callback racing with a nil read and verifies the entry is not retained.</violation>
</file>
<file name="Packages/macOS/CmuxNext/Sources/CmuxNextAgentPane/EventSourceProcesses.swift">
<violation number="1" location="Packages/macOS/CmuxNext/Sources/CmuxNextAgentPane/EventSourceProcesses.swift:148">
P1: This publication can briefly let a reused PID or newly exec'd image inherit the previous process's verdict: the exit/exec callback shares the read queue, so it cannot invalidate the entry while `resolve` is running, and `path(of:)` can read the newly published stale entry before that callback runs. Revalidate the watched identity or invalidate it atomically before exposing the resolved path.</violation>
</file>
<file name="Packages/macOS/CmuxNext/Sources/CmuxNextAgentPane/AgentPaneShared.swift">
<violation number="1" location="Packages/macOS/CmuxNext/Sources/CmuxNextAgentPane/AgentPaneShared.swift:23">
P3: The shared initializer drops `testRoot`, `chromium`, and `terminations` from `AgentPaneView`'s designated initializer. Callers using shared pane objects therefore silently get the default page-host and termination behavior and cannot inject test dependencies; add and forward these parameters.</violation>
</file>
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
| state.withLock { $0.entries[pid] = .unreadable(watch: watch, cancel: cancel) } | ||
| return | ||
| } | ||
| state.withLock { $0.entries[pid] = .read(process, watch: watch, cancel: cancel) } |
There was a problem hiding this comment.
P1: This publication can briefly let a reused PID or newly exec'd image inherit the previous process's verdict: the exit/exec callback shares the read queue, so it cannot invalidate the entry while resolve is running, and path(of:) can read the newly published stale entry before that callback runs. Revalidate the watched identity or invalidate it atomically before exposing the resolved path.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At Packages/macOS/CmuxNext/Sources/CmuxNextAgentPane/EventSourceProcesses.swift, line 148:
<comment>This publication can briefly let a reused PID or newly exec'd image inherit the previous process's verdict: the exit/exec callback shares the read queue, so it cannot invalidate the entry while `resolve` is running, and `path(of:)` can read the newly published stale entry before that callback runs. Revalidate the watched identity or invalidate it atomically before exposing the resolved path.</comment>
<file context>
@@ -0,0 +1,221 @@
+ state.withLock { $0.entries[pid] = .unreadable(watch: watch, cancel: cancel) }
+ return
+ }
+ state.withLock { $0.entries[pid] = .read(process, watch: watch, cancel: cancel) }
+ }
+
</file context>
| - Input: `com.cmuxterm.next.event-source-processes` (``EventSourceProcesses``: the executable path and start time of | ||
| each process that posts key and mouse events, read once per process with `proc_pidinfo`/`proc_pidpath`, user-initiated, | ||
| serial; it also runs that process's exit watch, which removes the entry. The key path only reads the table, never | ||
| a system call; one item per new posting process, so no back-pressure) |
There was a problem hiding this comment.
P2: This queue has no back-pressure despite the enforced threading model requiring every queue to be bounded. Add a finite admission bound and fail closed for unknown PIDs once it is reached, while releasing the entry and watch on process exit.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At plans/cmux-next/threading-model.md, line 152:
<comment>This queue has no back-pressure despite the enforced threading model requiring every queue to be bounded. Add a finite admission bound and fail closed for unknown PIDs once it is reached, while releasing the entry and watch on process exit.</comment>
<file context>
@@ -146,6 +146,10 @@ matches the label's literal text; `<x>` matches an interpolation). Its owner row
+- Input: `com.cmuxterm.next.event-source-processes` (``EventSourceProcesses``: the executable path and start time of
+ each process that posts key and mouse events, read once per process with `proc_pidinfo`/`proc_pidpath`, user-initiated,
+ serial; it also runs that process's exit watch, which removes the entry. The key path only reads the table, never
+ a system call; one item per new posting process, so no back-pressure)
- Diagnostics: `cmux-next.hang-episodes`, `cmux-next.hang-episodes.symbols`, `com.cmuxterm.app.next.run-marker`,
`com.cmuxterm.app.next.crash-forwarder`, `com.cmuxterm.app.next.owner-panic-forwarder`,
</file context>
| #expect(!AgentPanePersonInput.isPerson(event, sources: table)) | ||
| await table.settled() | ||
| #expect(table.cached(4343) == nil) | ||
| fake.end(4343) // its exit watch fires |
There was a problem hiding this comment.
P2: This test does not exercise the documented watch/read race because fake.end(4343) runs only after the read has settled. Make the fake end the process during resolve, so the test catches an exit callback racing with a nil read and verifies the entry is not retained.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At Packages/macOS/CmuxNext/Tests/CmuxNextAgentPaneTests/EventSourceProcessesTests.swift, line 141:
<comment>This test does not exercise the documented watch/read race because `fake.end(4343)` runs only after the read has settled. Make the fake end the process during `resolve`, so the test catches an exit callback racing with a nil read and verifies the entry is not retained.</comment>
<file context>
@@ -0,0 +1,361 @@
+ #expect(!AgentPanePersonInput.isPerson(event, sources: table))
+ await table.settled()
+ #expect(table.cached(4343) == nil)
+ fake.end(4343) // its exit watch fires
+ await table.settled()
+ fake.set(4343, EventSourceProcess(start: 300, path: Self.screenSharing))
</file context>
|
|
||
| extension AgentPaneView { | ||
| /// A pane over the app's shared objects (``AgentPaneShared``); the other parameters are as in the designated init. | ||
| public convenience init?(model: AgentPaneModel, source: AgentPaneSource? = nil, renderRate: AgentPaneRenderRate = .capped, |
There was a problem hiding this comment.
P3: The shared initializer drops testRoot, chromium, and terminations from AgentPaneView's designated initializer. Callers using shared pane objects therefore silently get the default page-host and termination behavior and cannot inject test dependencies; add and forward these parameters.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At Packages/macOS/CmuxNext/Sources/CmuxNextAgentPane/AgentPaneShared.swift, line 23:
<comment>The shared initializer drops `testRoot`, `chromium`, and `terminations` from `AgentPaneView`'s designated initializer. Callers using shared pane objects therefore silently get the default page-host and termination behavior and cannot inject test dependencies; add and forward these parameters.</comment>
<file context>
@@ -0,0 +1,28 @@
+
+extension AgentPaneView {
+ /// A pane over the app's shared objects (``AgentPaneShared``); the other parameters are as in the designated init.
+ public convenience init?(model: AgentPaneModel, source: AgentPaneSource? = nil, renderRate: AgentPaneRenderRate = .capped,
+ pageHost: Bool = false, shared: AgentPaneShared) {
+ self.init(model: model, source: source, renderRate: renderRate, pageHost: pageHost, openPanel: shared.openPanel,
</file context>
| - Input: `com.cmuxterm.next.event-source-processes` (``EventSourceProcesses``: the executable path and start time of | ||
| each process that posts key and mouse events, read once per process with `proc_pidinfo`/`proc_pidpath`, user-initiated, | ||
| serial; it also runs that process's exit watch, which removes the entry. The key path only reads the table, never | ||
| a system call; one item per new posting process, so no back-pressure) |
There was a problem hiding this comment.
P3: The one-item-per-process claim is false when exit-watch registration fails: each later event re-enqueues a read after read(_:) removes the entry. Document this retry path instead of claiming no back-pressure.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At plans/cmux-next/threading-model.md, line 152:
<comment>The one-item-per-process claim is false when exit-watch registration fails: each later event re-enqueues a read after `read(_:)` removes the entry. Document this retry path instead of claiming no back-pressure.</comment>
<file context>
@@ -146,6 +146,10 @@ matches the label's literal text; `<x>` matches an interpolation). Its owner row
+- Input: `com.cmuxterm.next.event-source-processes` (``EventSourceProcesses``: the executable path and start time of
+ each process that posts key and mouse events, read once per process with `proc_pidinfo`/`proc_pidpath`, user-initiated,
+ serial; it also runs that process's exit watch, which removes the entry. The key path only reads the table, never
+ a system call; one item per new posting process, so no back-pressure)
- Diagnostics: `cmux-next.hang-episodes`, `cmux-next.hang-episodes.symbols`, `com.cmuxterm.app.next.run-marker`,
`com.cmuxterm.app.next.crash-forwarder`, `com.cmuxterm.app.next.owner-panic-forwarder`,
</file context>
| a system call; one item per new posting process, so no back-pressure) | |
| a system call; normally one item per new posting process, but watch-registration failures retry on later events) |
|
Fix for the BLOCK at 072f126 (#28065 (comment)). Head: 62fc9e9. Cause. Fix (62fc9e9).
Fleet probe (read-only Python ctypes over SSH, uid 501, macOS 27, cmux17 and aws-m4pro-1, before the change): The exit watch ( Tests (
Not run:
|
|
Review (claude-opus-review-subagent-b): FIX at 62fc9e9 I am an independent reviewer. I did not write this code. I read the full diff from the merge base 4f957ed to the head, the repair alone ( Finding to fix1. The head does not compile when it lands on the current feat-cmux-next. The branch is 713 commits behind The repair itself: pass
Notes (do not block)
Observed / Not runObserved: the diffs; |
…ource table (the merge of feat's #29626) Feat's 701a03f added AgentPanePersonInput.record(model.transport.gestures) in the chosen callback; this PR makes sources: required, so the merged code did not compile. The check is the same as feat's, read from the injected table. threading-model.md names the sysctl read.
Bugbot needs on-demand usage enabledBugbot uses usage-based billing for this team and requires on-demand usage to be enabled. A team admin can enable on-demand usage in the Cursor dashboard. |
|
Review (claude-opus-review-subagent-c): SHIP at dc20356 I am an independent reviewer. I did not write this code. I read the full diff from the merge base 9f5e2e1 to the head (17 files), the merge 8a24c2b ( FindingsNone that block. The FIX finding at 62fc9e9 is fixed. What I checked
Notes (do not block)
Not runNo Swift build or test (repo rule: nothing on this machine; the fleet jobs at this head are pending). I did not do the GUI test of the file chooser through Screen Sharing. |
|
Fleet evidence at dc20356 (merge of feat plus the
|
…merge2 # Conflicts: # Packages/macOS/CmuxNext/Sources/CmuxNextApp/AgentTabs+Wiring.swift
|
Review (claude-opus-review-subagent-d): SHIP at 4b705a7 I am an independent reviewer. I did not write this code. I read FindingsNone that block. What I checked
Notes (do not block)
Observed / Not runObserved: the diffs and |
|
Merge of feat-cmux-next fd9dbf9 (scripts/merge-main.sh), pushed as 4b705a7. One conflict, in AgentTabs+Wiring.swift: the result keeps this PR's
|
Conflicts: #28065 changed the AgentTabStore init next to #30357's cloudRepositoryLocal closure (kept #28065's init, dropped the closure); #30831 edited a #30357 line in EnvironmentSection.tsx and added a test of #30357's attached-repository rows to session-info-environment.test.tsx (both files keep their pre-#30357 bytes; the test covers removed code).
Conflict in AgentPaneView+ContextMenu.swift: base #28065 made the person check take the pane's EventSourceProcesses table; the Chromium drop keeps its own event choice (droppedEvent) and person gate, now through isPerson(event, sources:). Agent-Session: https://cmux.com/deeplink/agent?id=aid-f71664ebfb6cf5206479
![Fix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/fix-with-codesmith-light-3x.png)
Summary
Risk tier: normal, as
scripts/ci/risk_tier.py pr --number 28065grades it (lead decision 2026-11-01 18:03Z: the label follows the repo's risk tool). The change guards permission grants and join consent, so it still has what high would need: an independent SHIP at the exact head and a green fleet compile. risk_tier.py does not list these files as high; that classifier gap goes to cmux-ad.P-MAIN S5 (found by the S2 stall agent, 2026-10-30).
AgentPanePersonInput.isPerson(the agent pane's gesture monitor: every key-down and mouse-down) andPersonInputCheck.isPerson(PersonInputLedger: every key and mouse event of the app) calledproc_pidpathon the main thread for each event that another process posted (source pid not 0). That is a system call on the key path.Why the path is read: only the person may grant in an agent pane (permission Allow, cx-ocdz / cx-1l61) or back a join consent. The HID system's events (pid 0) are the person's. Of the posted events, only the Apple services a person drives the Mac through count (Screen Sharing, Universal Control, Accessibility Keyboard, named by their path under SIP-protected /System), and for the strict join consent check only the WindowServer. A computer-use agent or a script that posts a click must not count. The event itself carries only the pid, not the executable, so the decision cannot come from the event alone (option (a) does not apply).
Fix (option (b)):
EventSourceProcesses, one table per app (built inAppServices, injected; no singleton).// perf-hot:marks the three functions.com.cmuxterm.next.event-source-processes: start time (sysctl(KERN_PROC_PID)p_starttime), path (proc_pidpath), start time again (so both are one process). Neither read has a same-user check, so the WindowServer, launchd and the root Screen Sharing daemon are read as this user (62fc9e9; before it,proc_pidinfo(PROC_PIDTBSDINFO)refused them with EPERM). A live process that is still unreadable keeps the safe default until its exit watch fires; it is not read again for each event.EVFILT_PROCwithNOTE_EXIT | NOTE_EXEC, registered withEV_RECEIPTand read by a dispatch read source on the same serial queue) is armed before the read. It removes the entry when that process ends or runs a new program (exec: same pid and start time, new image). A pid the kernel gives to a new process, or a new image in the same process, is therefore read again. No timer decides anything (R5). One entry per live posting process, no constant cap (R9).Lead review, second round:
PersonInputLedgeralso watches posted mouse moves, scrolls, other drags and modifier changes (primingTypes). For an unknown pid, it queues the table read (EventSourceProcesses.prime: a table lookup, no system call). A Screen Sharing session moves the mouse before it clicks, so its first click already counts.EVFILT_PROC/NOTE_EXITregistered withEV_RECEIPT, so a refusal is seen. When the kernel refuses the watch, nothing is kept: no verdict can outlive its process or pass to a process that reuses the pid. What the person sees then: a click or key that Screen Sharing, Universal Control or the Accessibility Keyboard posts never counts as their gesture. A permission Allow through Screen Sharing stays unanswered and they must use the Mac's own keyboard or mouse. Their own local input (HID, pid 0) is never affected. Each new event reads the process again, off the main thread.select.kqueuewithEV_RECEIPT): on cmux17, cmux19, cmux21 and cmux23 (macOS 27),NOTE_EXITattached to launchd (pid 1, root) and to the WindowServer (_windowserver). screensharingd starts only on demand and ran on no host; it runs as root like launchd.theKernelExitWatchAttachesAsThisUserprints the same lines from the fleet test step.plans/cmux-next/arch-ratchet.json: unchanged (feat's file). The PR adds no ratchet exception and no// blocking-allow:escape: the start-timesysctlandproc_pidpathrun only on the table's serial queue.Commits: the seam (no behavior change), red test 1, fix 1, red test 2 (priming, no watch), fix 2; after the base merge, 4cc0a03 moves the wiring into CmuxNextAgentPane (
AgentPaneShared,PersonInputLedger.sources) so CmuxNextApp lines do not rise, and drops the exception.Performance
Main-thread time per event, measured in
EventSourceProcessesTests.perEventMainThreadCost(mean over 20,000 events, one fleet Mac, not an exclusive host).proc_pidpathper event)perEventMainThreadCost, fleet job 5e30898cbc1d8b0a1826e976 at ff83b4eperEventMainThreadCost, fleet job 5e30898cbc1d8b0a1826e976 at ff83b4eNo capture: no visible change; the person-input check gives feat's verdicts for every posting process, including the WindowServer and root Screen Sharing (see the BLOCK at 072f126 and its fix 62fc9e9), only off the main thread; an unread or unreadable posting process is refused until it is read.
Review
Review (claude-opus-5.5-merge-review-subagent): SHIP at 97fd679 (receipt: #28065 (comment); merge of origin/feat-cmux-next, compile c17fa3d30e8e96886973f04c). Before the merge: SHIP at ff83b4e (#28065 (comment)). Earlier round: FIX at 3f13e1d, all findings fixed. Follow-up (non-blocking): drop the default
eventSources:argument ofAgentPaneView.initandAgentTabStore.init, so that a new caller cannot get an unprimed private table.Changelog
none
Review round 1 (claude-opus-review-subagent, FIX at 3f13e1d): fixed the exec case (
NOTE_EXEC), fail closed unless exactly oneEV_RECEIPTreceipt has no error, a kernel-wait test that could hang now fails after 10 s, andperEventMainThreadCostprints the main-thread ns per event (R10). The reviewer's finding that the blocking-allow exception is not needed is wrong for the installed hook: itsraw-blocking-extrule (proc_pid*) refused the push without it.Testing
Regression:
EventSourceProcessesTests(CmuxNextAgentPaneTests), run byscripts/ci/package-test-lane.sh suite Packages/macOS/CmuxNext EventSourceProcessesTests.3f13e1d3becbfa3612a880006dfa9828091b89a7aaa10518c989cb925e7f1104c5446ed9c4ed99a67abffd290d5e174c34c5dcebe3f13e1d3bec98d9a0eafbca030eb8d0fa80exit-watch-proof: launchd pid 1 as uid 501: attached,WindowServer pid 164 as uid 501: attached, screensharingd not runningff83b4e1f5dddbe9951d28d3c8897f4a6b0ff83b4e1f5d5e30898cbc1d8b0a1826e97638ce555794aebac231666cc7f1a9e7489d638ce555794aa74dba670ac66b514e5e0355theKernelWatchFiresOnExec(Expectation failed: await Self.fires(fired.stream)after 10.029 s); the other 8 passedObserved / Not run
Observed: the jobs above; an SSH kqueue probe as uid 501 on cmux17, cmux19, cmux21, cmux23 (macOS 27) attached
NOTE_EXITto launchd (root) and WindowServer (_windowserver).Not run: the typing bench (perf claim: none); screensharingd ran on no host (it starts on demand; it runs as root like launchd); no local Mac build or test (repo rule).