[PR #4903] [MERGED] fix(setup): tell MSIX Claude Desktop's process view from any private roaming file #6666

Closed
opened 2026-11-01 21:42:10 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/4903
Author: @pablodiazjorge
Created: 10/17/2026
Status: ✅ Merged
Merged: 10/22/2026
Merged by: @morluto

Base: main ← Head: fix/msix-claude-desktop-roaming-probe


📝 Commits (8)

  • 3f253d0 fix(setup): tell MSIX Claude Desktop's process view from any private roaming file
  • ccdba74 fix(setup): address review findings on the MSIX roaming probe
  • eb07605 fix(setup): bound the roaming walk's directory listings by its entry budget
  • a55c805 Merge upstream main into fix/msix-claude-desktop-roaming-probe
  • 6e674aa fix(setup): recheck the private probe before accepting a mismatched target
  • e67abba fix(setup): repeat the AppData lookup before trusting a rechecked probe
  • c882e56 fix: bound Claude roaming folder probes
  • ef48460 fix(setup): charge enumerated Claude folder names

📊 Changes

6 files changed (+622 additions, -56 deletions)

View changed files

📝 docs/client-configuration.md (+11 -6)
📝 src/application/ClaudeDesktopConfigLocation.test.ts (+339 -1)
📝 src/application/ClaudeDesktopConfigLocation.ts (+200 -40)
📝 src/application/Setup.configuration.test.ts (+15 -1)
📝 src/application/SupportedClients.ts (+8 -3)
📝 tests/boundary/filesystem/claudeDesktopClientConfiguration.test.ts (+49 -5)

📄 Description

Summary

Fixes #4278. On some Windows profiles, rea setup --client claude_desktop and rea uninstall refuse Claude Desktop permanently. The MSIX app there reads and writes the real %APPDATA%\Claude, so the package never gets a private LocalCache\Roaming\Claude folder. Setup only looked for private files in that folder, so it could never decide whether this process was started by the package. It reported unknown and refused, and the suggested remedy (open the app once, quit, rerun) cannot change that.

Setup now decides from any private file under the package's LocalCache\Roaming. It still compares the Claude folder first. When that folder has nothing, it takes a few files from the package's other private roaming folders, which the package redirects in the same way. A package with no private roaming file at all still refuses, but the message now says how to proceed.

Thanks to @myso-kr for the report, @CoreyKatzburg for the 2.31226.1.0 data point, and @mrcustsilva for the source trace, the manifest exclusions and the listing of the private roaming root.

Problem and expected behavior

processView in ClaudeDesktopConfigLocation.ts decides whether this process sees the package's redirected AppData. A process the package started (a terminal in the Code tab, for example) sees private files at their %APPDATA% paths. Any other process sees the real files or nothing. The comparison only listed LocalCache\Roaming\Claude. When that folder is missing, readdir throws and the view is unknown. registrationWriteBlocker lets only direct through.

That folder can be missing for good. Microsoft's description of AppData virtualization (Understanding how packaged desktop apps run on Windows) covers Windows 10 1903 and later. New files and folders under Roaming go to the per-package location, but when a file is opened from the real AppData location "no virtualization for that file occurs". A %APPDATA%\Claude that existed before the MSIX install, from an earlier unpackaged Claude Desktop, keeps being written in place. @mrcustsilva's profile fits: %APPDATA%\Claude dates from 2025-09-22 and its files change while the app runs. Meanwhile the private roaming root holds Claude-3p, Claude-NativeHost, Microsoft and a turborepo folder created the day of the listing.

Expected: setup should still tell whether this process was started by the package, and on such a profile write the %APPDATA% file that REA already resolves as configPath.

Change and scope

  • processView compares paths relative to the package's private roaming root. LocalCache\Roaming\Claude is tried first, unchanged: the private config when it exists, else up to eight of that folder's files. When that folder yields nothing, roamingProbes walks the private roaming root breadth first:
    • names are sorted;
    • at most 2 files per directory, 8 files in all, 64 directories listed or queued, depth 4 and 1024 entries inspected;
    • an entry is inspected only while its directory still needs a file or the queue has room for a folder;
    • a listing reads no more names than the budget has left (opendirSync with a limit), so a crowded folder is never enumerated whole;
    • unreadable directories are skipped and links are not followed (lstat).
  • The comparison is the same as before: if realpath at the %APPDATA% path equals the private target, the view is redirected; a different target or ENOENT gives direct; any other error gives unknown. One guard is new: a different target or ENOENT counts as direct only while the private file is still in place and a second AppData lookup agrees with the first. A log or telemetry file deleted between the lookups is skipped instead of passing for a direct view, whether the AppData lookup then finds nothing or an ordinary copy; one recreated meanwhile is caught by the second lookup, and a probe whose lookups disagree decides nothing.
  • Why the whole roaming root counts: the manifest's FileSystemWriteVirtualization excludes directories only under LocalAppData (Microsoft\Office\16.0\WEF, Claude-3p, Claude-Data and Claude\logs in 2.42304.0.0, as @mrcustsilva also reports), so every private roaming file is redirected for a process the package started. LocalCache\Local is not compared, because files under its excluded directories are not redirected and could not tell the views apart.
  • The unknown refusal message now covers both remaining causes (no comparable private file, or an unreadable AppData path). It names the roaming roots it compared and offers the manual edit that @myso-kr and @mrcustsilva confirmed works, instead of "open Claude Desktop once". For setup it names the configuration REA resolved, which is the private copy whenever one exists; for uninstall it names both files, which uninstall itself cleans.
  • docs/client-configuration.md describes the new order and the remaining refusal.
  • Not included:
    • An override flag that skips the check, as the issue suggests. That would let setup write while a running app can overwrite the change, so it needs a maintainer decision.
    • Detection by package identity. A process the Code tab starts reports no package identity, yet its AppData is redirected (see Evidence), so identity cannot replace the file comparison.
  • Scope: an ordinary fix within the existing setup boundary; a maintainer scope decision is not applicable. #4525 changes a different function and does not overlap.

Contract and boundary impact

  • Semantic owner and earliest changed stage: resolveWindowsClaudeDesktopLocation's processView in src/application/ClaudeDesktopConfigLocation.ts; the refusal text in registrationWriteBlocker (src/application/SupportedClients.ts).
  • Contribution scope fit and linked maintainer decision: existing boundary; not applicable.
  • CLI and MCP/tool-catalog contract: on a profile without a private Claude folder but with other private roaming files, setup and uninstall for claude_desktop now plan or perform the change instead of returning needs_human. The unknown remediation text changes. There is no catalog change.
  • Provider, bridge, target-format, or platform compatibility: Windows with the MSIX Claude Desktop only; other platforms and clients do not reach this code.
  • Evidence, artifact, provenance, or reconstruction contract: none.
  • Target selection, authority, upstream behavior, and cleanup details: setup reads at most 64 directory listings, 1024 entry inspections and 16 realpath results under the package's private roaming root and %APPDATA%; it writes nothing new.
  • Generated metadata, package, or installation impact: none.

Expected effects:

  • Read existing files or captured data.
  • Launch or attach to a process.
  • Inject instrumentation or run scripts/code.
  • Change process memory, registers, files, or configuration.
  • Contact network services.
  • Recover protected code or work around protections.
  • Documentation or usability only; no target interaction.
  • Not sure yet; investigation or maintainer guidance needed.

(The configuration change is setup's existing write of Claude Desktop's configuration, now reached on these profiles.)

Evidence and regression coverage

  • Tests added or updated:

    • src/application/ClaudeDesktopConfigLocation.test.ts, new describes "Claude Desktop MSIX process view from other roaming folders" and "... when a probe changes during the comparison", 12 tests:
      • direct and redirected views decided from other roaming folders;
      • ENOENT at the AppData path counts as direct, but not when the private file was deleted meanwhile, nor when the lookup then reaches an ordinary AppData copy;
      • a private file recreated during the comparison still shows a redirected view, and a probe whose two AppData lookups disagree decides nothing;
      • an unreadable folder is skipped;
      • probes spread at 2 per folder and stop at 8;
      • the walk stops after 1024 inspected entries, asks the listing for no more names than that, and stays unknown when that finds no file;
      • an empty roaming root stays unknown.
    • src/application/Setup.configuration.test.ts: with a private copy and an unknown view, the setup advice names the private copy and the uninstall advice names both files.
    • tests/boundary/filesystem/claudeDesktopClientConfiguration.test.ts:
      • a real temporary profile whose package keeps only turborepo\telemetry.json: the view is direct, setup configures mcpServers.rea in the AppData file;
      • the same folder linked into %APPDATA% with a junction: the view is redirected and the file is unchanged;
      • the former "no private copy" test now asserts the refusal with an empty private roaming root and the manual-edit remediation;
      • "refuses uninstall when the package view is unknown" now asserts the uninstall form of that remediation.
  • Base reproduction:

    • the reporters' layout through the source CLI, node scripts/rea.mjs setup --client claude_desktop --dry-run --json, in a temporary profile (APPDATA and LOCALAPPDATA pointed into a temp directory; the package keeps Claude-NativeHost, turborepo and Microsoft\Windows files and no Claude folder):
      • on main 01cdbbdc5: "status":"needs_human", no planned actions, exit 1;
      • with the change: "status":"planned" with configure_client:claude_desktop targeting <temp>\AppData\Roaming\Claude\claude_desktop_config.json, exit 0;
      • in both runs the configuration file is unchanged (dry run).
    • With an empty private roaming root the change still refuses (exit 1), with the new message.
    • With the two test files copied onto main, 10 of the 11 new or updated tests fail (unknown instead of direct or redirected, or the old remediation). The eleventh, an empty roaming root staying unknown, holds on both.
    • The tests added for the review findings fail against the first push 3f253d092: the advice test and the uninstall test (the old advice named only the AppData file) and the entry-budget test. The deleted-file test fails without the new guard (direct instead of unknown).
  • Live redirection check on Windows 11 Pro 10.0.26300 with Claude Desktop MSIX 2.42304.0.0. The same Node script compared one private file from each of three roaming folders with its %APPDATA% path:

    • from a terminal started by Claude Desktop's Code tab: ghidra\…\application.log, turborepo\telemetry.json and Claude-NativeHost\com.anthropic.claude_browser_extension.json all resolve to the private copies;
    • from a terminal started from Explorer: two are ENOENT and turborepo\telemetry.json resolves to a different, real file;
    • in the same Code-tab terminal, Windows.ApplicationModel.Package.Current reports no package identity (HRESULT 0x80073D54) for a PowerShell child, and tasklist /apps does not list the Node process;
    • the script resolves paths with fs.realpathSync.native, the same call REA's production file system uses for this comparison (ClaudeDesktopConfigLocation.ts);
    • this host has a private Claude folder, so the new walk is not what decides here; the check shows that the rest of the roaming root is redirected in the same way.
  • User-visible CLI output, empty private roaming root, setup (paths shortened):

    Claude Desktop: REA cannot tell whether Claude Desktop's MSIX package Claude_pzs8sxrjxfjjc started this process, because no private file under <LOCALAPPDATA>\Packages\Claude_pzs8sxrjxfjjc\LocalCache\Roaming could be compared with <APPDATA>, and a Claude Desktop that started it would overwrite the change from memory. Add REA's entry to <APPDATA>\Claude\claude_desktop_config.json by hand while Claude Desktop is fully quit, or rerun setup from a terminal that Claude Desktop did not start once REA can compare a private file there.
    
  • Remaining proof gaps:

    • No run on a reporter's real profile. @CoreyKatzburg, your private roaming root holds only Microsoft\. Could you list the files under %LOCALAPPDATA%\Packages\Claude_pzs8sxrjxfjjc\LocalCache\Roaming\Microsoft? If it holds any file, this change decides your view. If it holds only empty folders, setup still refuses there, with the manual-edit message.
    • Windows 10 was not run; the walk relies on the 1903+ behavior described above.
    • A redirected profile whose only private files sit deeper than depth 4 or past the first 64 directories would stay unknown; that errs on the side of refusing.

For evidence-bearing changes:

  • Observed, derived, and inferred claims remain distinguishable.
  • Artifact identity, source provenance, and failed attempts remain preserved.
  • Unsupported, incomplete, unavailable, or uncertain outcomes remain visible.

Validation performed

Windows 11 Pro 10.0.26300, Node 24.18.0, on main 01cdbbdc5:

  • npx vitest run src/application/ClaudeDesktopConfigLocation.test.ts src/application/Setup.configuration.test.ts tests/boundary/filesystem/claudeDesktopClientConfiguration.test.ts: 70 passed, 1 failed, 1 skipped. The failure is "blocks managed skill removal through redirected AppData before any removal", which cannot create its symlink on Windows without developer mode and fails the same way on main.
  • npx vitest run src/application tests/boundary/filesystem on main and on the change, compared test by test:
    • main: 3222 passed, 626 failed, 253 skipped;
    • change: 3231 passed, the same 626 failed, 253 skipped;
    • the only differences are the new tests (all pass), the renamed test, and a property test whose name carries a random seed;
    • this comparison ran on the first push; after the review fixes, npx vitest run src/application tests/boundary/filesystem/claudeDesktopClientConfiguration.test.ts gave 1200 passed, 11 failed and 1 skipped, the same failures as main, and the only differences from main are the new tests;
    • the 626 failures are host-dependent (POSIX-only paths, symlink privilege, suites that need the built runtime) and identical on both.
  • npm run check:fast: typecheck and lint pass. Lint reports 0 errors and 1663 warnings on both main and the change.
  • After merging main b83b9caf4 into the branch: npm run check:fast passes, and npx vitest run src/application tests/boundary/filesystem/claudeDesktopClientConfiguration.test.ts tests/boundary/filesystem/setupClientConfigCleanupOwnership.test.ts gives 1267 passed, 11 failed and 1 skipped; the 11 are the same host-dependent failures as main (Hopper installers on Linux and macOS, skill planning paths, and the symlink case above).
  • npx knip: clean.
  • npx oxfmt --check on the changed files: pass.
  • npm run docs:check was not run; the change to docs/client-configuration.md is prose only.
  • npm run compile, then the dry runs above with and without the change.

Compatibility, safety, and release

  • Breaking changes or migration steps: none. Profiles with a private Claude folder take the same path as before.
  • Real Hopper/Ghidra, browser, or OS coverage: Windows 11 with the MSIX Claude Desktop 2.42304.0.0; not applicable to providers.
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: setup still refuses whenever it sees a redirected view or cannot decide; the change only lets it decide in more profiles. It reads directory names and resolved paths under the package's own private roaming root and does not follow links there.

Author readiness

Human owner: @pablodiazjorge

  • I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work.
  • I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps.
  • I declare this PR ready for initial maintainer review.

Before merge

  • This PR addresses a concrete problem or an agreed enhancement.
  • Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff.
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above.
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

Maintainer blocker verification

The preferred LocalCache/Roaming/Claude probe and fallback roaming probes now share a 1,024-name enumeration/inspection budget. Every returned name is charged before sorting or early leaf selection, so crowded folders cannot hide work from later probes. Production opendirSync enumeration reads only the remaining permitted count.

The focused Claude Desktop location suite passed 41 tests and npm run check:fast passed on Node 24.18.0/npm 11.16.0. The many-early-folders regression verifies remaining-budget propagation. No current Windows profile was exercised. This budget targets roaming probes; it does not claim to bound the separate pre-existing MSIX Packages-root scan.

Windows CI follow-up

The initial Windows job timed out after 30 seconds in the unchanged Cursor override acceptance test. The same-revision failed-job rerun passed the curated Windows tests, package verification, Node 24 native directory identity and Node 22 native path boundaries. The previous timeout did not reproduce; no deterministic defect attributable to this PR was established. No timeout increase, skip or source change was made. The aggregate CI required summary subsequently passed on this same head, as did all four required contexts.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/4903 **Author:** [@pablodiazjorge](https://github.com/pablodiazjorge) **Created:** 10/17/2026 **Status:** ✅ Merged **Merged:** 10/22/2026 **Merged by:** [@morluto](https://github.com/morluto) **Base:** `main` ← **Head:** `fix/msix-claude-desktop-roaming-probe` --- ### 📝 Commits (8) - [`3f253d0`](https://github.com/morluto/rea/commit/3f253d092ed506be6475ef9a9dd9bdef25dd3d09) fix(setup): tell MSIX Claude Desktop's process view from any private roaming file - [`ccdba74`](https://github.com/morluto/rea/commit/ccdba74df9edf7ba466106360d3b5c540627d993) fix(setup): address review findings on the MSIX roaming probe - [`eb07605`](https://github.com/morluto/rea/commit/eb0760567d9503a157f641cac32516211a65a40f) fix(setup): bound the roaming walk's directory listings by its entry budget - [`a55c805`](https://github.com/morluto/rea/commit/a55c8059cfd3981bb1bdc3c494bd7cdd01a8eea1) Merge upstream main into fix/msix-claude-desktop-roaming-probe - [`6e674aa`](https://github.com/morluto/rea/commit/6e674aa1eb1e738440a9d1911a96c6c9b8a7c8fd) fix(setup): recheck the private probe before accepting a mismatched target - [`e67abba`](https://github.com/morluto/rea/commit/e67abba99c265c9c4d19b87aeadf138c158a6053) fix(setup): repeat the AppData lookup before trusting a rechecked probe - [`c882e56`](https://github.com/morluto/rea/commit/c882e566f64f72b12f6b9de9190fd5e1cc4a1d6b) fix: bound Claude roaming folder probes - [`ef48460`](https://github.com/morluto/rea/commit/ef48460aa7c3bbaf4e9a31b73f989a7135434f7d) fix(setup): charge enumerated Claude folder names ### 📊 Changes **6 files changed** (+622 additions, -56 deletions) <details> <summary>View changed files</summary> 📝 `docs/client-configuration.md` (+11 -6) 📝 `src/application/ClaudeDesktopConfigLocation.test.ts` (+339 -1) 📝 `src/application/ClaudeDesktopConfigLocation.ts` (+200 -40) 📝 `src/application/Setup.configuration.test.ts` (+15 -1) 📝 `src/application/SupportedClients.ts` (+8 -3) 📝 `tests/boundary/filesystem/claudeDesktopClientConfiguration.test.ts` (+49 -5) </details> ### 📄 Description ## Summary Fixes #4278. On some Windows profiles, `rea setup --client claude_desktop` and `rea uninstall` refuse Claude Desktop permanently. The MSIX app there reads and writes the real `%APPDATA%\Claude`, so the package never gets a private `LocalCache\Roaming\Claude` folder. Setup only looked for private files in that folder, so it could never decide whether this process was started by the package. It reported `unknown` and refused, and the suggested remedy (open the app once, quit, rerun) cannot change that. Setup now decides from any private file under the package's `LocalCache\Roaming`. It still compares the `Claude` folder first. When that folder has nothing, it takes a few files from the package's other private roaming folders, which the package redirects in the same way. A package with no private roaming file at all still refuses, but the message now says how to proceed. Thanks to @myso-kr for the report, @CoreyKatzburg for the 2.31226.1.0 data point, and @mrcustsilva for the source trace, the manifest exclusions and the listing of the private roaming root. ## Problem and expected behavior `processView` in `ClaudeDesktopConfigLocation.ts` decides whether this process sees the package's redirected AppData. A process the package started (a terminal in the Code tab, for example) sees private files at their `%APPDATA%` paths. Any other process sees the real files or nothing. The comparison only listed `LocalCache\Roaming\Claude`. When that folder is missing, `readdir` throws and the view is `unknown`. `registrationWriteBlocker` lets only `direct` through. That folder can be missing for good. Microsoft's description of AppData virtualization ([Understanding how packaged desktop apps run on Windows](https://learn.microsoft.com/en-us/windows/msix/desktop/desktop-to-uwp-behind-the-scenes#common-file-system-operations)) covers Windows 10 1903 and later. New files and folders under `Roaming` go to the per-package location, but when a file is opened from the real AppData location "no virtualization for that file occurs". A `%APPDATA%\Claude` that existed before the MSIX install, from an earlier unpackaged Claude Desktop, keeps being written in place. @mrcustsilva's profile fits: `%APPDATA%\Claude` dates from 2025-09-22 and its files change while the app runs. Meanwhile the private roaming root holds `Claude-3p`, `Claude-NativeHost`, `Microsoft` and a `turborepo` folder created the day of the listing. Expected: setup should still tell whether this process was started by the package, and on such a profile write the `%APPDATA%` file that REA already resolves as `configPath`. ## Change and scope - `processView` compares paths relative to the package's private roaming root. `LocalCache\Roaming\Claude` is tried first, unchanged: the private config when it exists, else up to eight of that folder's files. When that folder yields nothing, `roamingProbes` walks the private roaming root breadth first: - names are sorted; - at most 2 files per directory, 8 files in all, 64 directories listed or queued, depth 4 and 1024 entries inspected; - an entry is inspected only while its directory still needs a file or the queue has room for a folder; - a listing reads no more names than the budget has left (`opendirSync` with a limit), so a crowded folder is never enumerated whole; - unreadable directories are skipped and links are not followed (`lstat`). - The comparison is the same as before: if `realpath` at the `%APPDATA%` path equals the private target, the view is `redirected`; a different target or `ENOENT` gives `direct`; any other error gives `unknown`. One guard is new: a different target or `ENOENT` counts as `direct` only while the private file is still in place and a second AppData lookup agrees with the first. A log or telemetry file deleted between the lookups is skipped instead of passing for a direct view, whether the AppData lookup then finds nothing or an ordinary copy; one recreated meanwhile is caught by the second lookup, and a probe whose lookups disagree decides nothing. - Why the whole roaming root counts: the manifest's `FileSystemWriteVirtualization` excludes directories only under LocalAppData (`Microsoft\Office\16.0\WEF`, `Claude-3p`, `Claude-Data` and `Claude\logs` in 2.42304.0.0, as @mrcustsilva also reports), so every private roaming file is redirected for a process the package started. `LocalCache\Local` is not compared, because files under its excluded directories are not redirected and could not tell the views apart. - The `unknown` refusal message now covers both remaining causes (no comparable private file, or an unreadable AppData path). It names the roaming roots it compared and offers the manual edit that @myso-kr and @mrcustsilva confirmed works, instead of "open Claude Desktop once". For setup it names the configuration REA resolved, which is the private copy whenever one exists; for uninstall it names both files, which uninstall itself cleans. - `docs/client-configuration.md` describes the new order and the remaining refusal. - Not included: - An override flag that skips the check, as the issue suggests. That would let setup write while a running app can overwrite the change, so it needs a maintainer decision. - Detection by package identity. A process the Code tab starts reports no package identity, yet its AppData is redirected (see Evidence), so identity cannot replace the file comparison. - Scope: an ordinary fix within the existing setup boundary; a maintainer scope decision is not applicable. #4525 changes a different function and does not overlap. ## Contract and boundary impact - Semantic owner and earliest changed stage: `resolveWindowsClaudeDesktopLocation`'s `processView` in `src/application/ClaudeDesktopConfigLocation.ts`; the refusal text in `registrationWriteBlocker` (`src/application/SupportedClients.ts`). - Contribution scope fit and linked maintainer decision: existing boundary; not applicable. - CLI and MCP/tool-catalog contract: on a profile without a private `Claude` folder but with other private roaming files, `setup` and `uninstall` for `claude_desktop` now plan or perform the change instead of returning `needs_human`. The `unknown` remediation text changes. There is no catalog change. - Provider, bridge, target-format, or platform compatibility: Windows with the MSIX Claude Desktop only; other platforms and clients do not reach this code. - Evidence, artifact, provenance, or reconstruction contract: none. - Target selection, authority, upstream behavior, and cleanup details: setup reads at most 64 directory listings, 1024 entry inspections and 16 `realpath` results under the package's private roaming root and `%APPDATA%`; it writes nothing new. - Generated metadata, package, or installation impact: none. Expected effects: - [x] Read existing files or captured data. - [ ] Launch or attach to a process. - [ ] Inject instrumentation or run scripts/code. - [x] Change process memory, registers, files, or configuration. - [ ] Contact network services. - [ ] Recover protected code or work around protections. - [ ] Documentation or usability only; no target interaction. - [ ] Not sure yet; investigation or maintainer guidance needed. (The configuration change is setup's existing write of Claude Desktop's configuration, now reached on these profiles.) ## Evidence and regression coverage - Tests added or updated: - `src/application/ClaudeDesktopConfigLocation.test.ts`, new describes "Claude Desktop MSIX process view from other roaming folders" and "... when a probe changes during the comparison", 12 tests: - direct and redirected views decided from other roaming folders; - `ENOENT` at the AppData path counts as direct, but not when the private file was deleted meanwhile, nor when the lookup then reaches an ordinary AppData copy; - a private file recreated during the comparison still shows a redirected view, and a probe whose two AppData lookups disagree decides nothing; - an unreadable folder is skipped; - probes spread at 2 per folder and stop at 8; - the walk stops after 1024 inspected entries, asks the listing for no more names than that, and stays `unknown` when that finds no file; - an empty roaming root stays `unknown`. - `src/application/Setup.configuration.test.ts`: with a private copy and an unknown view, the setup advice names the private copy and the uninstall advice names both files. - `tests/boundary/filesystem/claudeDesktopClientConfiguration.test.ts`: - a real temporary profile whose package keeps only `turborepo\telemetry.json`: the view is `direct`, setup configures `mcpServers.rea` in the AppData file; - the same folder linked into `%APPDATA%` with a junction: the view is `redirected` and the file is unchanged; - the former "no private copy" test now asserts the refusal with an empty private roaming root and the manual-edit remediation; - "refuses uninstall when the package view is unknown" now asserts the uninstall form of that remediation. - Base reproduction: - the reporters' layout through the source CLI, `node scripts/rea.mjs setup --client claude_desktop --dry-run --json`, in a temporary profile (`APPDATA` and `LOCALAPPDATA` pointed into a temp directory; the package keeps `Claude-NativeHost`, `turborepo` and `Microsoft\Windows` files and no `Claude` folder): - on main `01cdbbdc5`: `"status":"needs_human"`, no planned actions, exit 1; - with the change: `"status":"planned"` with `configure_client:claude_desktop` targeting `<temp>\AppData\Roaming\Claude\claude_desktop_config.json`, exit 0; - in both runs the configuration file is unchanged (dry run). - With an empty private roaming root the change still refuses (exit 1), with the new message. - With the two test files copied onto main, 10 of the 11 new or updated tests fail (`unknown` instead of `direct` or `redirected`, or the old remediation). The eleventh, an empty roaming root staying `unknown`, holds on both. - The tests added for the review findings fail against the first push `3f253d092`: the advice test and the uninstall test (the old advice named only the AppData file) and the entry-budget test. The deleted-file test fails without the new guard (`direct` instead of `unknown`). - Live redirection check on Windows 11 Pro 10.0.26300 with Claude Desktop MSIX 2.42304.0.0. The same Node script compared one private file from each of three roaming folders with its `%APPDATA%` path: - from a terminal started by Claude Desktop's Code tab: `ghidra\…\application.log`, `turborepo\telemetry.json` and `Claude-NativeHost\com.anthropic.claude_browser_extension.json` all resolve to the private copies; - from a terminal started from Explorer: two are `ENOENT` and `turborepo\telemetry.json` resolves to a different, real file; - in the same Code-tab terminal, `Windows.ApplicationModel.Package.Current` reports no package identity (HRESULT `0x80073D54`) for a PowerShell child, and `tasklist /apps` does not list the Node process; - the script resolves paths with `fs.realpathSync.native`, the same call REA's production file system uses for this comparison (`ClaudeDesktopConfigLocation.ts`); - this host has a private `Claude` folder, so the new walk is not what decides here; the check shows that the rest of the roaming root is redirected in the same way. - User-visible CLI output, empty private roaming root, `setup` (paths shortened): ``` Claude Desktop: REA cannot tell whether Claude Desktop's MSIX package Claude_pzs8sxrjxfjjc started this process, because no private file under <LOCALAPPDATA>\Packages\Claude_pzs8sxrjxfjjc\LocalCache\Roaming could be compared with <APPDATA>, and a Claude Desktop that started it would overwrite the change from memory. Add REA's entry to <APPDATA>\Claude\claude_desktop_config.json by hand while Claude Desktop is fully quit, or rerun setup from a terminal that Claude Desktop did not start once REA can compare a private file there. ``` - Remaining proof gaps: - No run on a reporter's real profile. @CoreyKatzburg, your private roaming root holds only `Microsoft\`. Could you list the files under `%LOCALAPPDATA%\Packages\Claude_pzs8sxrjxfjjc\LocalCache\Roaming\Microsoft`? If it holds any file, this change decides your view. If it holds only empty folders, setup still refuses there, with the manual-edit message. - Windows 10 was not run; the walk relies on the 1903+ behavior described above. - A redirected profile whose only private files sit deeper than depth 4 or past the first 64 directories would stay `unknown`; that errs on the side of refusing. For evidence-bearing changes: - [x] Observed, derived, and inferred claims remain distinguishable. - [x] Artifact identity, source provenance, and failed attempts remain preserved. - [x] Unsupported, incomplete, unavailable, or uncertain outcomes remain visible. ## Validation performed Windows 11 Pro 10.0.26300, Node 24.18.0, on main `01cdbbdc5`: - `npx vitest run src/application/ClaudeDesktopConfigLocation.test.ts src/application/Setup.configuration.test.ts tests/boundary/filesystem/claudeDesktopClientConfiguration.test.ts`: 70 passed, 1 failed, 1 skipped. The failure is "blocks managed skill removal through redirected AppData before any removal", which cannot create its symlink on Windows without developer mode and fails the same way on main. - `npx vitest run src/application tests/boundary/filesystem` on main and on the change, compared test by test: - main: 3222 passed, 626 failed, 253 skipped; - change: 3231 passed, the same 626 failed, 253 skipped; - the only differences are the new tests (all pass), the renamed test, and a property test whose name carries a random seed; - this comparison ran on the first push; after the review fixes, `npx vitest run src/application tests/boundary/filesystem/claudeDesktopClientConfiguration.test.ts` gave 1200 passed, 11 failed and 1 skipped, the same failures as main, and the only differences from main are the new tests; - the 626 failures are host-dependent (POSIX-only paths, symlink privilege, suites that need the built runtime) and identical on both. - `npm run check:fast`: typecheck and lint pass. Lint reports 0 errors and 1663 warnings on both main and the change. - After merging main `b83b9caf4` into the branch: `npm run check:fast` passes, and `npx vitest run src/application tests/boundary/filesystem/claudeDesktopClientConfiguration.test.ts tests/boundary/filesystem/setupClientConfigCleanupOwnership.test.ts` gives 1267 passed, 11 failed and 1 skipped; the 11 are the same host-dependent failures as main (Hopper installers on Linux and macOS, skill planning paths, and the symlink case above). - `npx knip`: clean. - `npx oxfmt --check` on the changed files: pass. - `npm run docs:check` was not run; the change to `docs/client-configuration.md` is prose only. - `npm run compile`, then the dry runs above with and without the change. ## Compatibility, safety, and release - Breaking changes or migration steps: none. Profiles with a private `Claude` folder take the same path as before. - Real Hopper/Ghidra, browser, or OS coverage: Windows 11 with the MSIX Claude Desktop 2.42304.0.0; not applicable to providers. - Package or release metadata impact: none. - Security, privacy, process, or containment review: setup still refuses whenever it sees a redirected view or cannot decide; the change only lets it decide in more profiles. It reads directory names and resolved paths under the package's own private roaming root and does not follow links there. ## Author readiness Human owner: @pablodiazjorge - [x] I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work. - [x] I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps. - [x] I declare this PR ready for initial maintainer review. ## Before merge - [x] This PR addresses a concrete problem or an agreed enhancement. - [x] Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff. - [x] The PR has one focused outcome and the title follows `type(scope): outcome`. - [x] Related issue is linked, or the reason for not linking one is stated above. - [x] Tests cover changed observable behavior and meaningful failure paths. - [x] Owning docs, contracts, and generated metadata are updated where needed. - [x] User-visible CLI/MCP changes include representative output. - [x] I checked the final diff for secrets, unrelated cleanup, and unsupported claims. <!-- maintainer-blocker-review --> ### Maintainer blocker verification The preferred `LocalCache/Roaming/Claude` probe and fallback roaming probes now share a 1,024-name enumeration/inspection budget. Every returned name is charged before sorting or early leaf selection, so crowded folders cannot hide work from later probes. Production `opendirSync` enumeration reads only the remaining permitted count. The focused Claude Desktop location suite passed 41 tests and `npm run check:fast` passed on Node 24.18.0/npm 11.16.0. The many-early-folders regression verifies remaining-budget propagation. No current Windows profile was exercised. This budget targets roaming probes; it does not claim to bound the separate pre-existing MSIX Packages-root scan. <!-- windows-ci-followup --> ### Windows CI follow-up The initial Windows job timed out after 30 seconds in the unchanged Cursor override acceptance test. The same-revision [failed-job rerun](https://github.com/morluto/rea/actions/runs/39678758895/job/119525597674) passed the curated Windows tests, package verification, Node 24 native directory identity and Node 22 native path boundaries. The previous timeout did not reproduce; no deterministic defect attributable to this PR was established. No timeout increase, skip or source change was made. The aggregate `CI required` summary subsequently passed on this same head, as did all four required contexts. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-11-01 21:42:10 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#6666
No description provided.