Skip to content

fix(chief memory): the launch-time memory offer never lists ~/Downloads or ~/Desktop (restores the #28811 test) - #28820

Merged
lawrencecchen merged 2 commits into
feat-cmux-nextfrom
fix/chief-memory-unasked-detect-privacy
Oct 31, 2026
Merged

lawrencecchen merged 2 commits into
feat-cmux-nextfrom
fix/chief-memory-unasked-detect-privacy

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Privacy bug, live on feat-cmux-next: Home's first-run memory offer (HomeHostView -> ChiefMemoryImportHandlers.detectForOffer) runs the bundled optchat-chief memory import-from detect at every launch until the person clicks the offer. The person did not ask for it. detect listed ~/Downloads and ~/Desktop to look for claude.ai and ChatGPT export zips. macOS charges that read to cmux and shows the prompt "cmux would like to access files in your Downloads folder".

This PR:

  1. Puts back the red test unasked_detect_never_lists_downloads_or_desktop (Native/OptChat/optchat-chief/src/memory_sync_state.rs, first added in aec7054) by reverting base red: revert the unasked-detect red test (aec7054d44b) until its fix lands #28811.
  2. Fixes it (Lawrence's commit bc8b414 from a-lazy-permissions, cherry-picked with no conflicts and no changes):
    • detect now reads only the agents' own folders (Claude Code, Claude memory, Codex).
    • The new detect_with_exports (CLI detect --exports) also lists the export zips.
    • In Swift, only the Import Memories action that the person runs passes --exports (MemoryImportCommand.detect(exports: true)). The launch-time offer calls detect() and does not pass it.
    • The offer still finds Claude Code and Codex memory.

Takes back #28811: it removed the red test only until this fix lands.

Risk: risk:high, because it changes which user data the app reads.

Testing

Focused command (fleet, macOS):

cmux-ci run --class isolated --script scripts/ci/cmux-tui-rust-check.sh --ref SHA --arg optchat-chief --arg unasked_detect_never_lists_downloads_or_desktop
  • Red at commit 1, ee4ed73 (test only, no fix): FAIL on its own assertion, step 6ff896f6fa46488400b42f66 (aws-m4pro-8): panicked at src/memory_sync_state.rs:461:9: detect listed a protected folder: [... zip:.../Downloads/data-2026.zip ..., zip:.../Desktop/claude-export.zip ...].
  • Green at the head, 3c94857 (--arg optchat-chief with no filter: all optchat-chief tests, clippy -D warnings and fmt): step 52751e3eef0eaaade0c87d33 (aws-m4pro-4): lib tests 154 passed, 0 failed, including unasked_detect_never_lists_downloads_or_desktop ... ok and detect_with_exports_finds_claude_code_notes_codex_and_export_zips ... ok; clippy and fmt pass. The step is red on 3 integration tests in files this PR does not touch: tests/acpmux_peer_moved.rs:187 (CMS signature status 4), tests/eval_upsell.rs:37 (skill lacks the billing checkout line), tests/toolpack_mcp.rs:219 (unwrap on None). A base run of the same mode at dff3196 (step 791a2c4095b9f21b801a1d9b) is queued to confirm them as base reds.
  • Swift ChiefMemoryImportTests at the head (scripts/ci/package-test-lane.sh --arg=suite --arg=Packages/macOS/CmuxNext --arg=ChiefMemoryImportTests): step 004f0362b68ecae05f93dc15 at the head stopped before any test ran: generated file is stale ... Native/OptChat/optchat-chief/inspector/index.html (a base red; this PR does not touch that file; open fix cmux NEXT chief inspector: regenerate the committed page after the cursor-default change (base red) #28817). Rerun on proof-only branch proof/cmdp-swift-with-28817 (d064fc0: this head plus only the cmux NEXT chief inspector: regenerate the committed page after the cursor-default change (base red) #28817 page) is queued as step 3eb65e3eb41152fcc129469f; base check step b9079fb1b1db3bf15738764d queued.
  • Compile at the head (cmux-ci compile-check --package CmuxNext --app --recipe-version 2): passed, job 3be590d91044f7623578aa4c: CmuxNext 291 s, app 75 s, cmux-tui 37 s.

Observed / Not run

Observed:

  • git revert 74b40b70e02 applied cleanly. The restored test is the same as the one in aec7054, line for line.
  • git cherry-pick bc8b414147a applied cleanly. Its patch-id is the same as the original commit's, so nothing changed in the patch.
  • I read the Swift changes. detect(exports: Bool = false) is a nonisolated method on a Sendable struct. Runner.detect() is @MainActor and already awaits the off-main run helper, so isolation does not change.
  • I read the Rust flag parsing. --exports is changed to --exports=1 before Flags::parse, the same as --stop-host. No other optchat-chief command uses --exports.
  • scripts/cmux-next/prepush-gate.py: no new violations.
  • Fleet jobs: see Testing above.
  • Independent review: SHIP at the head (fix(chief memory): the launch-time memory offer never lists ~/Downloads or ~/Desktop (restores the #28811 test) #28820 (comment)).

Not run:

  • No live app check of the macOS TCC prompt at launch (that needs a fresh TCC state on a GUI host).
  • I did not check if chief-evals.yml or other suites call detect and expect zips. A search found no other Rust caller of detect.

Proof

No capture: the offer looks the same; this change only stops a folder read at launch, which no still can show.

Changelog

  • Fixed: the Home memory offer no longer lists files in Downloads or Desktop.

…(red)

This reverts commit 74b40b7, which
removed aec7054 only until its fix lands. The test is red here:
detect still lists ~/Downloads and ~/Desktop. The next commit fixes it.
…loads or ~/Desktop

Root cause of the launch prompt '"cmux DEV …" would like to access files in your Downloads
folder' (Lawrence, 2026-10-30): HomeHostView's first-run memory offer runs the bundled
`optchat-chief memory import-from detect` on every launch until the offer is clicked, and detect
listed ~/Downloads and ~/Desktop for claude.ai / ChatGPT export zips. macOS attributes the child's
read to cmux and prompts.

`detect` now reads only the agents' own folders; `detect --exports` (detect_with_exports) also
lists the export zips, and only the Import Memories action the person runs passes it. The offer
still finds Claude Code and Codex memory (LAUNCH-NO-TCC-PROMPTS).
@lawrencecchen lawrencecchen added the risk:high Risk tier (docs/ci/risk-tiers.md) label Oct 31, 2026
@lawrencecchen

Copy link
Copy Markdown
Contributor Author

Review (claude-opus independent reviewer): SHIP at 3c94857

Read-only review of the two commits (ee4ed73 red test, 3c94857 fix). I did not build or run anything.

Correctness

  1. Rust flag handling is correct. Native/OptChat/optchat-chief/src/main.rs:151-153 rewrites a bare --exports to --exports=1 before Flags::parse, the same as --stop-host. Without the rewrite, the app's argument order detect --json --exports --mux-home H would break: after --json is taken out, Flags::parse (cli.rs:21-23) would make --mux-home the value of exports and H a bare word, so run would see [detect, H] and return the usage text. With --exports=1, the --mux-home H pair stays as it is. The first parse (with --json still present) makes --exports=1 the value of json, but memory_json (main.rs:158-162) parses again without --json, so nothing shifts. git grep finds no other exports key in the crate. pack, github and peer-chiefs return before the rewrite, so they do not see it. If someone sends --exports false, the extra word fails safe: [detect, false] goes to the usage error and nothing is listed.
  2. memory_sync.rs:469-473 / memory_sync_state.rs:31-69. detect now reads only ~/.claude/projects (or CLAUDE_CONFIG_DIR), the memory/ notes under it, and ~/.codex/memories (agent_memory/sources.rs:115-137). detect_zips (the only read_dir of Downloads/Desktop) is now called only from detect_with_exports.
  3. The restored test is a real test. unasked_detect_never_lists_downloads_or_desktop (memory_sync_state.rs:455-480) calls detect only, so it also compiles at ee4ed73. At that commit detect called detect_zips, which would find both zips (each holds conversations.json, has a new mtime, and export_kind returns export). Both assertions would then fail. At the head they pass. One caveat: the red result depends on unzip being on the host (export_kind returns None without it). The sibling test detect_with_exports_finds_claude_code_notes_codex_and_export_zips would go red in that case, so a missing tool cannot go unseen.
  4. Swift. MemoryImportCommand.detect(exports: Bool = false) (MemorySource.swift:127) is a pure method on a Sendable struct, and the default is the safe value. detectForOffer (ChiefMemoryImportHandlers.swift:69-73, the launch path from HomeHostView.swift:255-262) calls detect() and gets no --exports. Only Runner.detect() (:111-115) passes exports: true. It is @MainActor and awaits the off-main run, the same as before. The isolation does not change.

Findings (none block)

  • F1 (PR description, must fix before landing): the Testing section still shows the placeholders RED_RESULT, GREEN_RESULT, SWIFT_RESULT and COMPILE_RESULT. This PR is risk:high, so it needs the red result at ee4ed73, the green result at the head, and a green compile in the body.
  • F2 (low, Chief memory scope): chief.importMemories with no source can also run from a script or agent through the socket action.run (ControlRouter+Builtins.swift:31, ControlRouter+ActionRun.swift:33-39; the catalog's cli: .exempt(.guiOnly) is a surface plan, not a runtime refusal). That run calls Runner.detect() with exports: true (ChiefMemoryImportHandlers.swift:42-43) and lists ~/Downloads and ~/Desktop before the person sees the dialog. It only runs when someone asks for it (it is never a launch path) and it shows the dialog, so I do not block on it. A follow-up could pass exports: invocation.origin == .user, or have a script name a source.
  • F3 (low, test gap): no Swift test pins that detectForOffer sends no --exports. ChiefMemoryImportTests.swift:66-68 tests the command builder only. If someone later changes the offer to detect(exports: true), no test would fail. One fix is a small static var offerArguments that the test can check.
  • F4 (note): the unasked detect still reads the full text of Claude and Codex memory notes at launch (memory_sync.rs:242-258, for the count and the byte size). These are the agents' own folders, not TCC-guarded, and this PR does not change it. I record it only because the goal says "user data the person did not ask about".

Other unasked paths: what I searched and what I found

Searched (all inside this checkout):

  • Native/OptChat: downloads|desktop|Documents|ZIP_FOLDERS|detect_zips|detect_with_exports|memory_sync_state::detect. The only read of the protected folders is detect_zips, which only detect_with_exports calls. read_source (memory_sync.rs:358-379) reads a zip:FILE path only when the caller names it. It does no globbing and does not pick a newest file. agent_memory::Sources (sources.rs:42-137) reads only ~/.claude, ~/.codex and the folders the person set in settings. subagents.rs:1160-1230 refuses Desktop/Documents/Downloads as a subagent cwd, so it does not read them. acpmux_daemon.rs:540 runs in its own home.
  • Callers of import-from/MemoryImportCommand/detectForOffer in the whole checkout (scripts, webviews, cmux-tui, .github, Packages): only HomeHostView (offer, now without exports), TopHomePageView/HomeFirstRunView (the person's click on Import Memories) and ChiefMemoryImportHandlers. No script, workflow or cmux-tui code calls memory import-from. BundledAgentMemoryIndex uses agent_memory (see above), not detect.
  • Packages/macOS/CmuxNext/Sources: downloadsDirectory|desktopDirectory|"Downloads"|"Desktop"|/Downloads|/Desktop.

Found outside Chief memory. These are separate follow-ups if anything, not this PR's scope:

  • CmuxNextScreenshots/ScreenshotSourceDetector.swift:79-85 uses ~/Desktop as the macOS screenshot folder when there is no location pref. ScreenshotsPageService (AppServices.swift:277, lazy) lists it only when a Screenshots page is made, and TerminalHandlers+Input.swift:129-133 reads it for the latest-screenshot action. Both start from the person. One possible launch case: a Screenshots tab that session restore opens again at launch would list ~/Desktop with no new action. I did not check whether restore makes the page before the tab shows. This is a possible follow-up, not confirmed.
  • CmuxNextBrowser/WebKit/DownloadDestination.swift:86-87 only resolves the Downloads URL for a download the person starts. ProfileMenuHandlers.swift:20-21 opens Downloads in Finder and cmux reads nothing. PickerPath.swift:62 and GoPlace.swift:26-28 only build URLs. ImportStepModel.swift:195-206 (browser import) starts detection only when the person opens Import from Browser. I found no launch-time read in these.

@github-actions github-actions Bot added risk:normal Risk tier (docs/ci/risk-tiers.md) and removed risk:high Risk tier (docs/ci/risk-tiers.md) labels Oct 31, 2026
@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 31, 2026
@lawrencecchen

Copy link
Copy Markdown
Contributor Author

Non-green checks at head 3c94857, classified (none come from this PR's 6 files):

@lawrencecchen
lawrencecchen merged commit 038bf00 into feat-cmux-next Oct 31, 2026
196 of 229 checks passed
@lawrencecchen
lawrencecchen deleted the fix/chief-memory-unasked-detect-privacy branch October 31, 2026 03:51
@lawrencecchen

Copy link
Copy Markdown
Contributor Author

Swift proof for this PR (merged head 3c94857): ChiefMemoryImportTests passed, 7 of 7, in fleet step 3eb65e3eb41152fcc129469f (aws-m4pro-2). One of them is localImportStopsTheHostAndKeepsTheMuxHome, which has the new --exports assertions.

The step ran at proof-only commit d064fc0, which is this head plus only the regenerated Native/OptChat/optchat-chief/inspector/index.html of #28817. Without that page, the run at the head stopped on the base stale-page check (004f0362b68ecae05f93dc15). That page is now fixed on feat (e20297d). I deleted the proof branch.

Follow-up from the review: #28882 (a scripted Import Memories run never lists ~/Downloads or ~/Desktop).

@cmux-bot

cmux-bot Bot commented Oct 31, 2026 •

Copy link
Copy Markdown

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

Not verified: macOS jobs did not run: compile and app tests were skipped on 3c94857e85; ci-status is green without them.

CI passes on 3c94857e85 (run 41165770450 attempt 1).
macOS jobs did not run: compile and app tests were skipped: Fast static checks skipped (the macos job needs both to succeed). Re-run CI once they pass, before merging.

Written by scripts/ci/classify_failures.py (ci-failure-attribution.yml); signatures are its SIGNATURES table. A machine verdict is the runner's fault, not this PR's; yours means the failing file is one this PR changes, also red on main that main's latest full suite fails the same way, seen on other PRs that it failed on another pull request's run lately.

Shipped in:

lawrencecchen added a commit that referenced this pull request Nov 1, 2026
…ds ~/Desktop until the person shows it (#28907)

* test(chief memory): a scripted Import Memories run never lists ~/Downloads or ~/Desktop (red)

chief.importMemories with no source runs detect --exports for any caller:
a script, an agent or the CLI through the control socket makes cmux list
~/Downloads and ~/Desktop (and raise the macOS folder prompt) before the
dialog shows. Only the person's own run in the app may ask for the export
zips.

This commit moves the choice of the detect arguments into
ChiefMemoryImportHandlers.detectArguments(muxHome:invocation:) and
offerDetectArguments(muxHome:) with no change in behavior, and adds
ChiefMemoryImportExportsTests. onlyThePersonsOwnRunListsTheExportZips
fails here: every origin still gets --exports. The next commit fixes it.

* fix(chief memory): only the person's own Import Memories run lists ~/Downloads or ~/Desktop

A chief.importMemories run that names no source asked optchat-chief for
detect --exports whatever its origin, so a script, an agent or the CLI
through the control socket made cmux list ~/Downloads and ~/Desktop, the
folders macOS guards, with no action of the person.

detectArguments now passes --exports only for
ActionInvocation.isPersonsOwnRun: origin user, not carried by the control
socket, and naming no caller app, agent chat or terminal. Any other run
lists only the agents' own folders; a script still imports a zip by naming
it (source zip:/path). The predicate moves from ScreenshotHandlers.isPersonsRun
into CmuxNextActions, so the raw-pixel rule and this rule are one rule.

Reported by the independent review of #28820.

* test(screenshots): a Screenshots model not started reads and watches no folder (red)

~/Desktop is macOS's default screenshot folder, which macOS guards. The
Screenshots page reads and watches its folders as soon as the page is made,
and an agent or a script makes it with no action of the person (cmux
browser on its tab, the DevTools relay, screenshots.open with focus: true).

This commit adds ScreenshotsModel(listsAtOnce:), isListing and
startListing() with no change in behavior (every model still lists at
once), and ScreenshotsModelListsOnlyWhenStartedTests, which fails here: a
model made with listsAtOnce: false lists and watches ~/Desktop. The next
commit fixes it and makes the app's page start listing only when it is on
screen.

* fix(screenshots): a Screenshots page reads ~/Desktop only once it is on screen, and only the person's run puts it there

The Screenshots page read and watched its folders, ~/Desktop (macOS's
default screenshot folder, which macOS guards) among them, as soon as the
page was made. An agent or a script makes it with no action of the person:
cmux browser on its tab and the DevTools relay make the page off screen,
and screenshots.open with focus: true put the tab in front.

ScreenshotsModel(listsAtOnce: false) now reads and watches nothing until
startListing(). The app's page model is made that way and starts listing
when the page is first on screen (setContentVisible(true), which only the
pane that shows the tab calls). screenshots.open selects the tab only for
ActionInvocation.isPersonsOwnRun (the rule of #28882); any other run opens
it in the background.

A tab the person left on screen is restored on screen and lists at once:
that is the person's own view, as Chrome reloads a restored tab.

* test(screenshots): a restored or agent-shown Screenshots page lists nothing until the person clicks (red)

The review of #28907 found that showing the page is not the person's
choice: an agent can show a background Screenshots tab (cmux tab focus,
a browser mirror, the Home mirror), and a tab restored on screen reads
~/Desktop at launch, which raises the macOS folder prompt after a privacy
reset or on a new Mac (LAUNCH-NO-TCC-PROMPTS).

This commit adds the seams ScreenshotsView.showsPrompt and
showScreenshots(), and ScreenshotsOpen (the decision of a screenshots.open
run) with the old behavior, and the tests:
- ScreenshotsPageListsOnlyAfterTheClickTests: a page made by the app and
  shown (restored on screen, or shown by an agent) lists nothing and shows
  the prompt until the person clicks; it fails here (showing it lists).
- screenshotsOpenFromAScriptWithFocusSaysItOpenedInTheBackground (the
  handler, on a real window): a scripted run with focus says that it
  opened the tab in the background; it fails here (it says nothing).
- ScreenshotsOpenDecisionTests: only the person's run selects and lists at
  once, and every other run gets a note; it fails here.

* fix(screenshots): a Screenshots page lists nothing until the person clicks Show Screenshots in it

The review of #28907 (FIX) found that showing the page is not the person's
choice. An agent can show a background Screenshots tab (cmux tab focus, a
browser mirror, the Home mirror), and a tab restored on screen read
~/Desktop at launch, which raises the macOS folder prompt after a privacy
reset or on a new Mac (LAUNCH-NO-TCC-PROMPTS). Chrome's reload of a
restored tab is no precedent here: Chrome passes no OS privacy gate.

- ScreenshotsView shows a Show Screenshots prompt (title, one line on why,
  one button) until the person clicks it; only that click starts the model
  listing. Shown, focused or restored, the page reads and watches nothing.
  The prompt goes in one frame, with no animation; its fonts come from the
  app's Typography.
- The app's page model is made with listsAtOnce: false, and
  setContentVisible no longer starts listing.
- screenshots.open: only the person's own run (isPersonsOwnRun) brings the
  tab to the front; any other run, with --focus too, opens it in the
  background and says so (result opened: background, and a note), never a
  silent background open. The decision is ScreenshotsOpen.
- ScreenshotsModel.apply does nothing before listing starts.
- Four new strings in all 21 locales of the Screenshots catalog.

* test(screenshots): the click-gate test checks plain values, so a red run never reflects the view

In the red run (step c6a93fd37237b33f8c12418c) the test failed on its own
assertion, then the test process crashed while Swift Testing described the
next failed expectation: it reflected ScreenshotsView and hit a null
NSPasteboard. The handler test after it never ran. Each expectation now
checks a Bool that is read first.

* test(screenshots): no detection before the click, the click remembered per tab, and screenshots.open says what it did (red)

The review of 791f3d7 (FIX) found:
- folder detection ran before the click (makePage); it reads other apps'
  containers and checks folders under ~/Desktop, which can raise a macOS
  prompt at launch for a restored tab;
- the keyboard could not reach Show Screenshots (the hidden grid took focus);
- the click was kept on the page, so a page made again (hibernation wake,
  engine switch) asked again;
- ScreenshotsModel listed at once by default;
- screenshots.open told a script "opened in the background" for a tab
  already open and listing; the unselected tab and the no-window refusal
  had no test.

This commit adds the seams ScreenshotsGate (unlocked tabs by key),
ScreenshotsView.onShowScreenshots and focusTarget, and an injectable
ScreenshotEnvironment for ScreenshotsPageService, all with the old
behavior, and the tests ScreenshotsGateTests and ScreenshotsGateWiringTests,
which fail here.

* fix(screenshots): nothing is detected before the click, the click is kept per tab, and screenshots.open says what it did

The fixes for the review of 791f3d7 (FIX):
- Folder detection (which reads other apps' containers and checks folders
  under ~/Desktop) runs only after the person's first Show Screenshots click
  (ScreenshotsGate.mayDetect); before it, a restored or agent-made page
  reads nothing at all.
- The click is kept per tab key (ScreenshotsGate), so a page made again
  after a hibernation wake or an engine switch lists with no second click.
- While the prompt is up, focusing the tab focuses the Show Screenshots
  button (Space presses it; no Return key equivalent), not the hidden grid.
- ScreenshotsModel lists only when its caller says so (listsAtOnce
  defaults to false).
- The prompt's secondary text uses the palette (Palette.textSecondary).
- screenshots.open from a script on a tab already open answers
  opened: existing with listing true or false (and a note while it waits
  for the click), not "opened in the background".
- SystemScreenshotsWorkspace (the open and reveal adapter) moves from
  CmuxNextApp into CmuxNextScreenshots.
- A What's New highlight, and one new string in all 21 locales.

* test(screenshots): the answer for a Screenshots tab already open is tested on ScreenshotsOpen

The red run at d5198fd (step bfd0f9bd9fc2d72fa22f0be0) showed that
screenshotsOpenSaysWhatItDid passed there: its existing-tab part ran only
when the new tab was in the pane, and in a test with no daemon the tab
never arrives, so that part was skipped. That part moves to
ScreenshotsOpenDecisionTests, on ScreenshotsOpen.result: a new tab opened
behind, the tab already open and listing, and the tab already open and
waiting for the click. The handler test keeps the background answer.

* test(screenshots): Space shows the screenshots without Full Keyboard Access, and the wiring test never reads the test Mac's Desktop (red)

The third review (FIX at bf28c6a) found that the Show Screenshots
button takes the keyboard only with Full Keyboard Access on, so with the
default setting Space did nothing; and that one wiring test ran the real
detector after its click, which lists the test Mac's own Desktop.

spacePressesShowScreenshotsWhileThePromptIsUp: while the prompt is up the
pane itself takes the keyboard and Space shows the screenshots; it fails
here (the focus target is the button, and Space does nothing). The click
test now uses a fake detection environment. The unused
ScreenshotsGate.forget leaves the gate test.

* fix(screenshots): Space shows the screenshots with the default keyboard setting; the title is themed; a script with no pane is refused; the highlight validates

The fixes for the third review (FIX at bf28c6a):
- B: while the prompt is up the pane itself takes the keyboard
  (acceptsFirstResponder) and Space shows the screenshots: a plain button
  takes the keyboard only with Full Keyboard Access on. After the click the
  keyboard goes on to the grid.
- A: the What's New highlight has its action line (screenshots.open), so
  the highlight validator passes.
- E: the prompt title takes Palette.textPrimary.
- F: screenshots.open with no focused pane is refused (No pane is focused),
  never a silent success.
- D: the unused ScreenshotsGate.forget is gone; a click stays for the life
  of the process (tab keys are durable).
- G: the click test uses a fake detection environment.

* test(screenshots): the Space test checks plain values, so a red run never reflects the view

Its red run (step 7dd4a41d0e08065a142e97fc) crashed while Swift Testing
described a failed expectation on two views (a null NSPasteboard in the
reflection), as the click test did before. It now compares Bools.

* chore(next): PR autofix

The regenerated files from autofix run 41198445289 at e04a9b9 (its
pr-autofix-patch artifact): ci-target-graph.json (CmuxNextScreenshots now
depends on CmuxNextCompat), action-surfaces.json and actions.md
(screenshots.open is an effect binding), the CLI and palette reference
docs, and schemas/defaults/cmux-next.json. The run's push job did not run:
it needs a non-pull_request event, and pull_request is the only trigger.

* chore(next): this PR carries no generated output (take the base's copies)

The security of the merge (review FIX at cef95c1): the autofix patch
was applied with git apply, not with pr-autofix-apply.sh, so it pushed
outputs this pull request does not own (bot-only schemas/defaults, the CLI
and palette references, and export rows regenerated against an older base;
merged with a newer base, action-surfaces.json and actions.md removed 155 of
the base's rows, the #21946 problem). generated_outputs.py drift also says
ci-target-graph.json is written by the base autofix only.

All six now equal origin/feat-cmux-next; the base autofix regenerates them
after this pull request merges. The commit message of cef95c1 was also
wrong: the export change there was the base's automations.show, not
screenshots.open.

---------

Co-authored-by: cmux-bot[bot] <348531757+cmux-bot[bot]@users.noreply.github.com>
lawrencecchen added a commit that referenced this pull request Nov 1, 2026
…oads or ~/Desktop (#28882)

* test(chief memory): a scripted Import Memories run never lists ~/Downloads or ~/Desktop (red)

chief.importMemories with no source runs detect --exports for any caller:
a script, an agent or the CLI through the control socket makes cmux list
~/Downloads and ~/Desktop (and raise the macOS folder prompt) before the
dialog shows. Only the person's own run in the app may ask for the export
zips.

This commit moves the choice of the detect arguments into
ChiefMemoryImportHandlers.detectArguments(muxHome:invocation:) and
offerDetectArguments(muxHome:) with no change in behavior, and adds
ChiefMemoryImportExportsTests. onlyThePersonsOwnRunListsTheExportZips
fails here: every origin still gets --exports. The next commit fixes it.

* fix(chief memory): only the person's own Import Memories run lists ~/Downloads or ~/Desktop

A chief.importMemories run that names no source asked optchat-chief for
detect --exports whatever its origin, so a script, an agent or the CLI
through the control socket made cmux list ~/Downloads and ~/Desktop, the
folders macOS guards, with no action of the person.

detectArguments now passes --exports only for
ActionInvocation.isPersonsOwnRun: origin user, not carried by the control
socket, and naming no caller app, agent chat or terminal. Any other run
lists only the agents' own folders; a script still imports a zip by naming
it (source zip:/path). The predicate moves from ScreenshotHandlers.isPersonsRun
into CmuxNextActions, so the raw-pixel rule and this rule are one rule.

Reported by the independent review of #28820.

---------

Co-authored-by: cmux-bot[bot] <348531757+cmux-bot[bot]@users.noreply.github.com>

This branch was previously deployed

1 inactive deployment
ci-routing-agent — 3c94857e Deployed Oct 31, 2026 by lawrencecchen via namespace autofix #8909
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-to-land Reviewed at this head; next-autoland merges it into feat-cmux-next when its only reds are base reds risk:normal Risk tier (docs/ci/risk-tiers.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant