[GH-ISSUE #4378] [Bug] MSIX setup reports a changed config target when its recheck cannot inspect private storage #1737

Closed
opened 2026-10-16 21:09:38 +00:00 by bsakaguchi · 1 comment
Owner

Originally created by @coygeek on GitHub (Oct 16, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/4378

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Setup, packaging, or upgrade

Observed behavior

SOURCE-ONLY: when setup selected the AppData configuration and the final private-path lstat recheck fails with EACCES or EPERM, setup returns failed / target_changed and says that the client began reading a different file. An inspection failure does not establish that a private file appeared. Writes remain correctly blocked; the defect is the lost inspection diagnosis and inaccurate recovery instruction.

Reproduction

In a source checkout at either revision named below, I ran this exact source-inspection command once per checkout. It requires Python 3 and Git; it does not execute REA or change permissions:

python3 - <<'PY'
from pathlib import Path
import subprocess
revision = subprocess.check_output(['git', 'rev-parse', 'HEAD'], text=True).strip()
assert revision in {'8ab5024397ddbd3b37ab68a13cabedc2eb7ce7dd', '3b79af28e80785afce9f5998d8f03289402a1847'}
print(revision)
checks = {
 'src/application/MsixPackageStorage.ts': ['if (configPath !== view.appDataConfigPath) return false;', 'await lstat(view.privateConfigPath);', 'return !(', '(cause.code === "ENOENT" || cause.code === "ENOTDIR")'],
 'src/application/SetupClientConfiguration.ts': ['(await packageReadTargetChanged(client.msixPackage, client.configPath))', 'return { status: "failed", reason: "target_changed" };'],
 'src/application/SetupClients.ts': ['The client began reading a different configuration file after the plan was shown'],
 'src/application/ClaudeDesktopConfigLocation.ts': ['privateCopy.value || !appDataCopy.value', 'view = "direct";', 'could not be inspected, so REA cannot establish which', 'Repair access to that path, then rerun.'],
 'src/application/ClaudeDesktopConfigLocation.test.ts': ['reports an uninspectable private copy instead of choosing a file', 'throw failure("EPERM");'],
 'src/application/SupportedClients.ts': ['if (msix === undefined || msix.processView === "direct") return undefined;'],
}
for name, fragments in checks.items():
 text = Path(name).read_text()
 assert all(fragment in text for fragment in fragments), name
 print(name + ': source assertions passed')
PY

Both runs exited 0, printed the checkout revision, then printed source assertions passed for all six files. This verifies source contents only. No setup operation, permission failure, Windows ACL transition, or regression test was executed.

The supported conditional path is: Windows Claude Desktop MSIX discovery finds its recognized package, a missing private configuration, and an existing AppData configuration. Another comparable private file can establish processView: "direct", so the registration gate permits the AppData selection. Setup plans an update, receives approval, and reaches Setup.ts → configureDetectedClients → configureClientConfiguration → persistClientConfiguration for an otherwise valid configuration needing an update. If the final private-path inspection fails with a non-absence access error, the recheck returns true and persistence returns target_changed before backup/write. These are source-derived conditions; their occurrence on a real Windows installation was not measured.

Expected behavior

Keep writes blocked when the read target cannot be established, but distinguish an inaccessible private path from an observed new private copy and retain actionable access-repair guidance. Initial Claude Desktop discovery already preserves that distinction; its source test includes EPERM coverage. A successful private-path inspection after AppData selection should continue to report the confirmed target change.

Impact and scope

Affected condition: AppData was selected, then private-path inspection becomes unavailable before persistence. Setup loses the access-error diagnosis and offers an inaccurate recovery instruction. No wrong-file write or data loss was established. Windows incidence and frequency are unknown. The identified caller is setup persistence; doctor and uninstall are not established as affected.

REA version

Main source 8ab5024397ddbd3b37ab68a13cabedc2eb7ce7dd; latest published rea-agents 6.5.0, release source 3b79af28e80785afce9f5998d8f03289402a1847. Both contain the reviewed branches.

Runtime and platform

Python source inspection on macOS. NOT_ENOUGH_INFO: no Windows/MSIX runtime or ACL failure was observed; Node was not used.

Target and artifact context

Claude Desktop MSIX configuration selection between AppData and package-private storage. No real configuration was accessed.

Interface

Package or setup

Earliest failure stage

Setup or provider admission

Relevant output or evidence

The recheck returns true for every caught error except ENOENT or ENOTDIR; persistence maps true to target_changed, discarding the inspection error. failedConfigurationMessage then says: The client began reading a different configuration file after the plan was shown, so no change was made. Rerun setup to review the new target. This is a static control-flow result, not observed runtime output.

The recheck, persistence gate, and failure message establish the misclassification. The discovery error branch preserves inspection uncertainty and asks for access repair; ClaudeDesktopConfigLocation.test.ts:173-186 supplies the contrasting test source. SupportedClients.ts:145-150,283-294, ClaudeDesktopConfigLocation.ts:95-124,132-174, Setup.ts:333-344, and SetupHost.ts:313-330 establish selection, admission, and the caller path.

Typed outcome or recovery expected

Proposed, unexecuted checks: for the same admitted AppData selection, a final inspection access error must leave writes blocked and report an unresolved target/path inspection with access-repair guidance. Controls: confirmed appearance of a private copy must still report target_changed; ENOENT/ENOTDIR must continue to follow the existing absent-copy path. These checks restore diagnostic accuracy without relaxing the write gate.

Analysis or suspected cause

The boolean result combines positive existence evidence with non-absence inspection failures, preventing persistence from distinguishing them. This is source-derived, not a runtime reproduction.

Additional context

Issue #4278 concerns initial discovery when no package-private configuration is available for process-view comparison, before a plan is admitted. This report instead concerns a final recheck after a direct-view AppData selection was admitted. The merged PR #2426 introduced the target recheck; it documents target changes but does not address distinguishing access errors at that recheck.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: The final recheck discards inspection errors and supplies an incorrect diagnosis and recovery instruction for a blocked setup update; no unsafe write is established.
  • Workaround status: unverified
  • Workaround basis: Repairing access before retry appears appropriate, but no affected Windows setup or recovery was exercised.
Originally created by @coygeek on GitHub (Oct 16, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/4378 ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area Setup, packaging, or upgrade ### Observed behavior SOURCE-ONLY: when setup selected the AppData configuration and the final private-path `lstat` recheck fails with `EACCES` or `EPERM`, setup returns `failed / target_changed` and says that the client began reading a different file. An inspection failure does not establish that a private file appeared. Writes remain correctly blocked; the defect is the lost inspection diagnosis and inaccurate recovery instruction. ### Reproduction In a source checkout at either revision named below, I ran this exact source-inspection command once per checkout. It requires Python 3 and Git; it does not execute REA or change permissions: ```sh python3 - <<'PY' from pathlib import Path import subprocess revision = subprocess.check_output(['git', 'rev-parse', 'HEAD'], text=True).strip() assert revision in {'8ab5024397ddbd3b37ab68a13cabedc2eb7ce7dd', '3b79af28e80785afce9f5998d8f03289402a1847'} print(revision) checks = { 'src/application/MsixPackageStorage.ts': ['if (configPath !== view.appDataConfigPath) return false;', 'await lstat(view.privateConfigPath);', 'return !(', '(cause.code === "ENOENT" || cause.code === "ENOTDIR")'], 'src/application/SetupClientConfiguration.ts': ['(await packageReadTargetChanged(client.msixPackage, client.configPath))', 'return { status: "failed", reason: "target_changed" };'], 'src/application/SetupClients.ts': ['The client began reading a different configuration file after the plan was shown'], 'src/application/ClaudeDesktopConfigLocation.ts': ['privateCopy.value || !appDataCopy.value', 'view = "direct";', 'could not be inspected, so REA cannot establish which', 'Repair access to that path, then rerun.'], 'src/application/ClaudeDesktopConfigLocation.test.ts': ['reports an uninspectable private copy instead of choosing a file', 'throw failure("EPERM");'], 'src/application/SupportedClients.ts': ['if (msix === undefined || msix.processView === "direct") return undefined;'], } for name, fragments in checks.items(): text = Path(name).read_text() assert all(fragment in text for fragment in fragments), name print(name + ': source assertions passed') PY ``` Both runs exited 0, printed the checkout revision, then printed `source assertions passed` for all six files. This verifies source contents only. No setup operation, permission failure, Windows ACL transition, or regression test was executed. The supported conditional path is: Windows Claude Desktop MSIX discovery finds its recognized package, a missing private configuration, and an existing AppData configuration. Another comparable private file can establish `processView: "direct"`, so the registration gate permits the AppData selection. Setup plans an update, receives approval, and reaches `Setup.ts` → `configureDetectedClients` → `configureClientConfiguration` → `persistClientConfiguration` for an otherwise valid configuration needing an update. If the final private-path inspection fails with a non-absence access error, the recheck returns true and persistence returns `target_changed` before backup/write. These are source-derived conditions; their occurrence on a real Windows installation was not measured. ### Expected behavior Keep writes blocked when the read target cannot be established, but distinguish an inaccessible private path from an observed new private copy and retain actionable access-repair guidance. Initial Claude Desktop discovery already preserves that distinction; its source test includes `EPERM` coverage. A successful private-path inspection after AppData selection should continue to report the confirmed target change. ### Impact and scope Affected condition: AppData was selected, then private-path inspection becomes unavailable before persistence. Setup loses the access-error diagnosis and offers an inaccurate recovery instruction. No wrong-file write or data loss was established. Windows incidence and frequency are unknown. The identified caller is setup persistence; doctor and uninstall are not established as affected. ### REA version Main source `8ab5024397ddbd3b37ab68a13cabedc2eb7ce7dd`; latest published `rea-agents 6.5.0`, release source `3b79af28e80785afce9f5998d8f03289402a1847`. Both contain the reviewed branches. ### Runtime and platform Python source inspection on macOS. NOT_ENOUGH_INFO: no Windows/MSIX runtime or ACL failure was observed; Node was not used. ### Target and artifact context Claude Desktop MSIX configuration selection between AppData and package-private storage. No real configuration was accessed. ### Interface Package or setup ### Earliest failure stage Setup or provider admission ### Relevant output or evidence The recheck returns true for every caught error except `ENOENT` or `ENOTDIR`; persistence maps true to `target_changed`, discarding the inspection error. `failedConfigurationMessage` then says: `The client began reading a different configuration file after the plan was shown, so no change was made. Rerun setup to review the new target.` This is a static control-flow result, not observed runtime output. The [recheck](https://github.com/morluto/rea/blob/8ab5024397ddbd3b37ab68a13cabedc2eb7ce7dd/src/application/MsixPackageStorage.ts#L168-L187), [persistence gate](https://github.com/morluto/rea/blob/8ab5024397ddbd3b37ab68a13cabedc2eb7ce7dd/src/application/SetupClientConfiguration.ts#L219-L237), and [failure message](https://github.com/morluto/rea/blob/8ab5024397ddbd3b37ab68a13cabedc2eb7ce7dd/src/application/SetupClients.ts#L9-L15) establish the misclassification. The [discovery error branch](https://github.com/morluto/rea/blob/8ab5024397ddbd3b37ab68a13cabedc2eb7ce7dd/src/application/ClaudeDesktopConfigLocation.ts#L177-L196) preserves inspection uncertainty and asks for access repair; `ClaudeDesktopConfigLocation.test.ts:173-186` supplies the contrasting test source. `SupportedClients.ts:145-150,283-294`, `ClaudeDesktopConfigLocation.ts:95-124,132-174`, `Setup.ts:333-344`, and `SetupHost.ts:313-330` establish selection, admission, and the caller path. ### Typed outcome or recovery expected Proposed, unexecuted checks: for the same admitted AppData selection, a final inspection access error must leave writes blocked and report an unresolved target/path inspection with access-repair guidance. Controls: confirmed appearance of a private copy must still report `target_changed`; `ENOENT`/`ENOTDIR` must continue to follow the existing absent-copy path. These checks restore diagnostic accuracy without relaxing the write gate. ### Analysis or suspected cause The boolean result combines positive existence evidence with non-absence inspection failures, preventing persistence from distinguishing them. This is source-derived, not a runtime reproduction. ### Additional context [Issue #4278](https://github.com/morluto/rea/issues/4278) concerns initial discovery when no package-private configuration is available for process-view comparison, before a plan is admitted. This report instead concerns a final recheck after a direct-view AppData selection was admitted. The merged [PR #2426](https://github.com/morluto/rea/pull/2426) introduced the target recheck; it documents target changes but does not address distinguishing access errors at that recheck. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: The final recheck discards inspection errors and supplies an incorrect diagnosis and recovery instruction for a blocked setup update; no unsafe write is established. - Workaround status: unverified - Workaround basis: Repairing access before retry appears appropriate, but no affected Windows setup or recovery was exercised.
Author
Owner

@morluto commented on GitHub (Oct 17, 2026):

Revalidated and closing as fixed on main abb54ce3743dd51a54304833a6e081b89243a5e8. The runtime fixes were already published in 9abdad335; this reconciliation adds missing focused regressions in abb54ce37.

The reported producing boundary is already fixed by 9abdad335: a private-path recheck returns a Result, and setup distinguishes target_unavailable/inspect from a positively observed target_changed. Added a production-boundary control with a real owned directory whose search permission is denied. It retains EACCES and the private path in the result/remediation, preserves original config bytes, creates no backup, and does not say the client began reading a different file. The same control fails on pre-fix6d5281407 with target_changed.

Validation:82 tests across four configuration/CLI files passed. Seven newly checked-in controls ran against pre-fix6d5281407: six failed for the expected original defects and one existing-behavior guard passed; all seven pass now. Typecheck/lint, formatting, test discovery (1563files exactly once) and pre-push checks passed. Related header/skill/doctor reconciliation added186passing checks across nine other files.

Scope: macOS arm64 / Node24.18.0; controlled native files, source/platform-selected Windows adapters and real REA CLI commands. No native Windows Claude app,3P deployment, Windows/Linux permission fault or provider/target execution was verified. No full-suite or performance improvement claim.

<!-- gh-comment-id:6242951837 --> @morluto commented on GitHub (Oct 17, 2026): Revalidated and closing as fixed on main `abb54ce3743dd51a54304833a6e081b89243a5e8`. The runtime fixes were already published in `9abdad335`; this reconciliation adds missing focused regressions in `abb54ce37`. The reported producing boundary is already fixed by 9abdad335: a private-path recheck returns a Result, and setup distinguishes target_unavailable/inspect from a positively observed target_changed. Added a production-boundary control with a real owned directory whose search permission is denied. It retains EACCES and the private path in the result/remediation, preserves original config bytes, creates no backup, and does not say the client began reading a different file. The same control fails on pre-fix6d5281407 with target_changed. Validation:82 tests across four configuration/CLI files passed. Seven newly checked-in controls ran against pre-fix6d5281407: six failed for the expected original defects and one existing-behavior guard passed; all seven pass now. Typecheck/lint, formatting, test discovery (1563files exactly once) and pre-push checks passed. Related header/skill/doctor reconciliation added186passing checks across nine other files. Scope: macOS arm64 / Node24.18.0; controlled native files, source/platform-selected Windows adapters and real REA CLI commands. No native Windows Claude app,3P deployment, Windows/Linux permission fault or provider/target execution was verified. No full-suite or performance improvement claim.
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#1737
No description provided.