[GH-ISSUE #2526] fix(package-verifier): Claude Desktop backup readback accepts changed snapshots #843

Closed
opened 2026-10-15 18:28:55 +00:00 by bsakaguchi · 1 comment
Owner

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

Summary

The package verifier accepts a Claude Desktop backup containing the newly registered REA configuration instead of the original configuration. Its backup assertion recognizes only the text existing, which occurs in both snapshots. A direct execution of the source assertion confirmed the false acceptance with a valid live registration. This leaves the package's claimed backup readback unable to detect this client-specific retention regression.

Runtime or environment

Failure verified at a2d8b94dd5 and still present at 8b181716ac; Node v26.10.0 and npm 11.19.1. The diagnostic runs only the existing filesystem assertion with disposable JSON fixtures. It does not require Claude Desktop, a packaged CLI, or an analysis provider.

Steps to reproduce

  1. Use the stated source baseline. The package fixture starts with the original Claude Desktop configuration {"existing":true} followed by a newline.
  2. Run this diagnostic from the repository root. It reads the current assertion directly from the verifier source, runs it against a valid post-setup configuration, and replaces only the disposable backup between cases. It creates no repository files and deletes its fixture directory.
node --max-old-space-size=2048 --input-type=module <<'JS'
import assert from 'node:assert/strict';
import { readFile, writeFile, mkdtemp, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';

const source = await readFile('scripts/verify-package-setup.mjs', 'utf8');
const start = source.indexOf('const assertClientConfig =');
const end = source.indexOf('const assertOpenCodeConfig =', start);
assert(start >= 0 && end > start);
const check = new Function('readFile', 'json',
  source.slice(start, end) + '\nreturn assertClientConfig;'
)(readFile, JSON.parse);
const fixture = await mkdtemp(join(tmpdir(), 'rea-backup-check-'));
const configPath = join(fixture, 'claude_desktop_config.json');
const original = '{"existing":true}\n';
const cli = 'rea-fixture-cli';
const configured = JSON.stringify({ existing: true,
  mcpServers: { rea: { command: cli, args: ['mcp'] } }
}) + '\n';

try {
  await writeFile(configPath, configured);
  await writeFile(configPath + '.rea.backup', original);
  await check(cli, configPath);
  console.log('original backup: accepted');

  await writeFile(configPath + '.rea.backup', configured);
  assert.notEqual(await readFile(configPath + '.rea.backup', 'utf8'), original);
  await check(cli, configPath);
  console.log('post-setup backup differing from original: accepted');

  await writeFile(configPath + '.rea.backup', 'missing sentinel\n');
  await assert.rejects(check(cli, configPath), /packaged client backup failed/);
  console.log('backup without existing: rejected');

  await writeFile(configPath + '.rea.backup', original);
  await assert.rejects(check('wrong-cli', configPath), /packaged client readback failed/);
  console.log('wrong live registration: rejected');
} finally {
  await rm(fixture, { recursive: true });
}
JS
  1. Observe that the changed backup is accepted even though the independent byte comparison proves that it differs from the initial snapshot. The two rejection controls show that the diagnostic executes both the backup check and the live-registration guards.

Expected behavior

When setup starts with the known existing Claude Desktop configuration, the package verifier should accept its retained original backup and reject a backup containing the post-setup configuration. Successful backup readback must establish that the first snapshot survived, as the installation contract promises.

Actual behavior

The direct assertion probe exited 0 and printed the following results. The live configuration preserved the existing preference and supplied the expected command and arguments in every backup case.

original backup: accepted
post-setup backup differing from original: accepted
backup without existing: rejected
wrong live registration: rejected

The full packaged setup lane was not executed. Source tracing establishes that the supported-host setup lane calls this assertion for Claude Desktop and never compares that client's backup exactly afterward. Cursor uses the same helper but receives a later exact comparison, so this report concerns Claude Desktop. No production backup-writing failure or user data loss was observed.

Evidence

  • Expected source: docs/installation.md:55-59 promises retention of the first backup snapshot; scripts/verify-package-environment.mjs:64-73 supplies the known original fixture; scripts/verify-package.mjs:194-200 reports backup readback coverage after the setup stage.
  • Failure source: scripts/verify-package-setup.mjs:418-430, executed unchanged by the diagnostic above, accepts the changed backup; its invocation and subsequent guards are at scripts/verify-package-setup.mjs:526-561,755-793.
  • Evidence provenance: observed
  • Local verification: reproduced
  • Reproduction completeness: complete

The source-derived lane analysis checks the supported-host condition, the initial live fixture, the setup-apply path, and all later backup guards. The post-setup JSON satisfies the existing-preference and registration checks, then its backup passes because it contains existing. Later checks cover Cursor, Codex, and OpenCode backups. Failure recovery checks active configuration and setup status, and uninstall does not compare the Claude Desktop backup.

The direct probe uses the real filesystem reader and the same JSON parsing behavior as the verifier's helper. The synthetic command is compared as text and never executed. Current production setup uses exclusive backup creation and retains an existing destination; the verified defect is the assertion's inability to distinguish a changed snapshot.

Related history: the earlier Codex comment and backup report concerns a different production failure and is closed. Its follow-up added strict sibling backup checks but left this Claude Desktop assertion unchanged.

Restoration check

With the live registration still correct, require the original Claude Desktop fixture backup to pass and the post-setup backup to fail. Also reject a backup whose unrelated preference changed while the text existing remains. Keep rejection of an incorrect live registration and of a missing backup. Retain the later exact backup checks for the other clients. These controls must distinguish backup fidelity from successful registration; weakening registration checks or changing the promised original fixture does not restore the contract.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: The package verifier demonstrably accepts a changed Claude Desktop snapshot, so its backup-readback claim can conceal a retention regression for that client. The failure is bounded to verification; no production data-loss incident or successful full package run with a broken writer was established.
  • Workaround status: unverified
  • Workaround basis: The isolated diagnostic can compare its own fixture, but no practical comparison procedure for the full package lane was verified. That lane deletes its workspace before emitting the result, so access to its backup before cleanup remains the exact gap.
Originally created by @coygeek on GitHub (Oct 13, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/2526 ## Summary The package verifier accepts a Claude Desktop backup containing the newly registered REA configuration instead of the original configuration. Its backup assertion recognizes only the text `existing`, which occurs in both snapshots. A direct execution of the source assertion confirmed the false acceptance with a valid live registration. This leaves the package's claimed backup readback unable to detect this client-specific retention regression. ## Runtime or environment Failure verified at a2d8b94dd50dab8d966239dd63ab84718aa8b4b4 and still present at 8b181716aca2cb467be499cba226abfb5ae19141; Node v26.10.0 and npm 11.19.1. The diagnostic runs only the existing filesystem assertion with disposable JSON fixtures. It does not require Claude Desktop, a packaged CLI, or an analysis provider. ## Steps to reproduce 1. Use the stated source baseline. The package fixture starts with the original Claude Desktop configuration `{"existing":true}` followed by a newline. 2. Run this diagnostic from the repository root. It reads the current assertion directly from the verifier source, runs it against a valid post-setup configuration, and replaces only the disposable backup between cases. It creates no repository files and deletes its fixture directory. ```sh node --max-old-space-size=2048 --input-type=module <<'JS' import assert from 'node:assert/strict'; import { readFile, writeFile, mkdtemp, rm } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; const source = await readFile('scripts/verify-package-setup.mjs', 'utf8'); const start = source.indexOf('const assertClientConfig ='); const end = source.indexOf('const assertOpenCodeConfig =', start); assert(start >= 0 && end > start); const check = new Function('readFile', 'json', source.slice(start, end) + '\nreturn assertClientConfig;' )(readFile, JSON.parse); const fixture = await mkdtemp(join(tmpdir(), 'rea-backup-check-')); const configPath = join(fixture, 'claude_desktop_config.json'); const original = '{"existing":true}\n'; const cli = 'rea-fixture-cli'; const configured = JSON.stringify({ existing: true, mcpServers: { rea: { command: cli, args: ['mcp'] } } }) + '\n'; try { await writeFile(configPath, configured); await writeFile(configPath + '.rea.backup', original); await check(cli, configPath); console.log('original backup: accepted'); await writeFile(configPath + '.rea.backup', configured); assert.notEqual(await readFile(configPath + '.rea.backup', 'utf8'), original); await check(cli, configPath); console.log('post-setup backup differing from original: accepted'); await writeFile(configPath + '.rea.backup', 'missing sentinel\n'); await assert.rejects(check(cli, configPath), /packaged client backup failed/); console.log('backup without existing: rejected'); await writeFile(configPath + '.rea.backup', original); await assert.rejects(check('wrong-cli', configPath), /packaged client readback failed/); console.log('wrong live registration: rejected'); } finally { await rm(fixture, { recursive: true }); } JS ``` 3. Observe that the changed backup is accepted even though the independent byte comparison proves that it differs from the initial snapshot. The two rejection controls show that the diagnostic executes both the backup check and the live-registration guards. ## Expected behavior When setup starts with the known existing Claude Desktop configuration, the package verifier should accept its retained original backup and reject a backup containing the post-setup configuration. Successful backup readback must establish that the first snapshot survived, as the installation contract promises. ## Actual behavior The direct assertion probe exited 0 and printed the following results. The live configuration preserved the existing preference and supplied the expected command and arguments in every backup case. ```text original backup: accepted post-setup backup differing from original: accepted backup without existing: rejected wrong live registration: rejected ``` The full packaged setup lane was not executed. Source tracing establishes that the supported-host setup lane calls this assertion for Claude Desktop and never compares that client's backup exactly afterward. Cursor uses the same helper but receives a later exact comparison, so this report concerns Claude Desktop. No production backup-writing failure or user data loss was observed. ## Evidence - Expected source: `docs/installation.md:55-59` promises retention of the first backup snapshot; `scripts/verify-package-environment.mjs:64-73` supplies the known original fixture; `scripts/verify-package.mjs:194-200` reports backup readback coverage after the setup stage. - Failure source: `scripts/verify-package-setup.mjs:418-430`, executed unchanged by the diagnostic above, accepts the changed backup; its invocation and subsequent guards are at `scripts/verify-package-setup.mjs:526-561,755-793`. - Evidence provenance: observed - Local verification: reproduced - Reproduction completeness: complete The source-derived lane analysis checks the supported-host condition, the initial live fixture, the setup-apply path, and all later backup guards. The post-setup JSON satisfies the existing-preference and registration checks, then its backup passes because it contains `existing`. Later checks cover Cursor, Codex, and OpenCode backups. Failure recovery checks active configuration and setup status, and uninstall does not compare the Claude Desktop backup. The direct probe uses the real filesystem reader and the same JSON parsing behavior as the verifier's helper. The synthetic command is compared as text and never executed. Current production setup uses exclusive backup creation and retains an existing destination; the verified defect is the assertion's inability to distinguish a changed snapshot. Related history: [the earlier Codex comment and backup report](https://github.com/morluto/rea/issues/1163) concerns a different production failure and is closed. Its follow-up added strict sibling backup checks but left this Claude Desktop assertion unchanged. ## Restoration check With the live registration still correct, require the original Claude Desktop fixture backup to pass and the post-setup backup to fail. Also reject a backup whose unrelated preference changed while the text `existing` remains. Keep rejection of an incorrect live registration and of a missing backup. Retain the later exact backup checks for the other clients. These controls must distinguish backup fidelity from successful registration; weakening registration checks or changing the promised original fixture does not restore the contract. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: The package verifier demonstrably accepts a changed Claude Desktop snapshot, so its backup-readback claim can conceal a retention regression for that client. The failure is bounded to verification; no production data-loss incident or successful full package run with a broken writer was established. - Workaround status: unverified - Workaround basis: The isolated diagnostic can compare its own fixture, but no practical comparison procedure for the full package lane was verified. That lane deletes its workspace before emitting the result, so access to its backup before cleanup remains the exact gap.
bsakaguchi 2026-10-15 18:28:55 +00:00
Author
Owner

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

Verified the reported defect is addressed on main by 4d4631ac94.

Root cause and resolution: Setup verifier treated a backup as valid based on decoded/configured content rather than exact byte identity with the caller's original snapshot.

Validation: tests/boundary/filesystem/packageClientBackup.test.ts — rejects a backup replaced with the post-setup client configuration and invalid-UTF8 byte comparison reject altered backup; exact original bytes remain accepted.

Evidence: CI run 500e: owner unchanged and assertions passed.

Verification scope: Filesystem fixture contract; no live Claude Desktop install.

Current main CI has separate failures under repair; this closure does not claim the complete main workflow is green.

<!-- gh-comment-id:6162523951 --> @morluto commented on GitHub (Oct 14, 2026): Verified the reported defect is addressed on main by 4d4631ac94431bb8aae2fe3edcf1e8227048848d. Root cause and resolution: Setup verifier treated a backup as valid based on decoded/configured content rather than exact byte identity with the caller's original snapshot. Validation: `tests/boundary/filesystem/packageClientBackup.test.ts` — `rejects a backup replaced with the post-setup client configuration` and invalid-UTF8 byte comparison reject altered backup; exact original bytes remain accepted. Evidence: CI run 500e: owner unchanged and assertions passed. Verification scope: Filesystem fixture contract; no live Claude Desktop install. Current main CI has separate failures under repair; this closure does not claim the complete main workflow is green.
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#843
No description provided.