[GH-ISSUE #7311] [Bug] Archive snapshot refusal reports invalid Evidence and unrelated native recovery #4342

Closed
opened 2026-10-31 00:49:04 +00:00 by bsakaguchi · 0 comments
Owner

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

Before submitting

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

Area

Evidence, artifacts, or reconstruction

Observed behavior

On macOS, opening a fresh one-member ZIP through open_binary succeeds. Immediately requesting close_binary with a new snapshot_path returns evidence_integrity_mismatch and tells the caller to recreate or re-import invalid Evidence. Its detailed reason is Profileless native snapshots may contain only current dispatch workflow observations, although this session contains an archive and no imported snapshot or analysis observations.

No snapshot is created. The archive remains unchanged and the session stays open; closing without snapshot_path succeeds. This report concerns the misleading failure explanation and recovery advice. Remaining open after a failed save is the documented behavior.

Reproduction

Run the following from the built source checkout with its locked dependencies already available, using Node.js on macOS. It creates an isolated configuration and a stored ZIP containing only sample.txt with hello followed by a newline. The target is parsed, never executed. The script starts the production stdio MCP entry point and prints the actual replies.

PROBE_DIR="${PROBE_DIR:-$(mktemp -d)}"
export PROBE_DIR
mkdir -p "$PROBE_DIR/home" "$PROBE_DIR/tmp"
python3 - <<'PY'
import os, zipfile
from pathlib import Path
root = Path(os.environ['PROBE_DIR'])
with zipfile.ZipFile(root / 'sample.zip', 'w', compression=zipfile.ZIP_STORED) as z:
    z.writestr('sample.txt', 'hello\n')
with zipfile.ZipFile(root / 'sample.zip') as z:
    assert z.testzip() is None
PY
node --input-type=module <<'JS'
import assert from 'node:assert/strict';
import { access, readFile } from 'node:fs/promises';
import { createHash } from 'node:crypto';
import { join, resolve } from 'node:path';
import { Client } from './node_modules/@modelcontextprotocol/client/dist/index.mjs';
import { StdioClientTransport } from './node_modules/@modelcontextprotocol/client/dist/stdio.mjs';
const root = process.cwd(), work = resolve(process.env.PROBE_DIR);
const archive = join(work, 'sample.zip');
const snapshot = join(work, 'snapshot.json');
const digest = async () => createHash('sha256').update(await readFile(archive)).digest('hex');
const before = await digest();
const client = new Client({ name: 'archive-snapshot-repro', version: '1' });
await client.connect(new StdioClientTransport({
  command: process.execPath,
  args: [join(root, 'scripts/rea.mjs'), '--mcp'],
  cwd: work,
  env: { PATH: process.env.PATH, HOME: join(work, 'home'), TMPDIR: join(work, 'tmp') },
  stderr: 'pipe',
}));
async function call(name, args = {}) {
  const reply = await client.callTool({ name, arguments: args });
  const payload = JSON.parse(reply.content.find(p => p.type === 'text').text);
  console.log(name, JSON.stringify(payload));
  return { reply, payload };
}
try {
  assert.equal((await call('open_binary', { path: archive })).payload.result.kind, 'archive');
  const refused = await call('close_binary', { snapshot_path: snapshot });
  assert.equal(refused.reply.isError, true);
  assert.equal(refused.payload.error.details.reason,
    'Profileless native snapshots may contain only current dispatch workflow observations');
  assert.equal(await access(snapshot).then(() => true, () => false), false);
  assert.equal((await call('binary_session')).payload.result.open, true);
  assert.equal((await call('close_binary')).payload.result, null);
  assert.equal((await call('binary_session')).payload.result.open, false);
  assert.equal(await digest(), before);
  console.log('PASS: refusal reproduced; no snapshot; normal close succeeded; ZIP unchanged');
} finally {
  await client.close();
}
JS

The same diagnostic occurred in two separate MCP server processes with newly generated ZIP fixtures. The exact block above was executed once as the second reproduction; the first also ran the resource-only control described below.

Expected behavior

When the current archive session cannot supply an analysis snapshot, explain the unmet snapshot prerequisite or applicability limit and give recovery that applies to this session. Do not describe freshly accepted, unchanged input as corrupt Evidence or recommend recreating/re-importing Evidence that was never supplied. Ordinary closure without a snapshot is an available recovery for the tested empty session.

The repository requires meaningful failure reasons (AGENTS.md, Architecture and Cleanup). The existing resource-only snapshot refusal demonstrates this contract: it explicitly says analysis snapshots do not apply to that session, rather than diagnosing corrupted Evidence. Preserve failed-save session retention and refusal of invalid snapshot bindings. Whether archive snapshots should be supported is a separate capability question; this report does not infer support from an omitted exclusion or require snapshot creation to succeed.

Impact and scope

Observed impact is incorrect diagnostic and recovery wording in the tested fresh ZIP session. No output corruption, invalid replay, target change or lost analysis history was observed; the fixture had no analysis observations. Omitting snapshot_path completes ordinary closure but does not provide snapshot persistence. Other operating systems, archive formats, populated sessions and real-provider persistence were not tested.

REA version

rea-agents 6.8.0; source revision e89192653402222c5b099216f0d83a2d1bceace0. The existing compiled MCP runtime reported server version 6.8.0.

Runtime and platform

Node.js v24.22.1; macOS 27.0.1 arm64; @modelcontextprotocol/client 2.3.1; actual stdio MCP transport through scripts/rea.mjs --mcp. No external analysis engine was started.

Target and artifact context

Locally generated ZIP, one stored regular member containing six bytes of text; open_binary reported format: "zip", kind: "archive", a SHA-256 identity and null architecture. Python's ZIP member CRC check passed, and the archive's SHA-256 before and after the MCP sequence matched. No snapshot or Evidence bundle was imported.

Interface

MCP

Earliest failure stage

Cleanup, cancellation, or lifecycle

Relevant output or evidence

The failing MCP call returned isError: true and this text payload:

{"error":{"code":"evidence_integrity_mismatch","category":"integrity_mismatch","message":"Evidence is invalid or has changed. Recreate or re-import it, then try again.","retryable":false,"remediation":{"action":"Evidence is invalid or has changed. Recreate or re-import it, then try again."},"details":{"reason":"Profileless native snapshots may contain only current dispatch workflow observations"}}}

The executed assertions confirmed that the selected snapshot path did not exist, binary_session.result.open remained true, close_binary {} returned {"result":null}, the next session status reported open: false, and the ZIP hash was unchanged.

A separate control in the first server process opened an authored resource-only app with open_bundle_resources, then requested a snapshot close. Its main message and details.reason both correctly stated: Analysis snapshots do not apply to a resource-only app session because it has no executable identity or replay binding. Its error code was also evidence_integrity_mismatch; the diagnostic defect here is not established by that code alone.

  • Expected source: AGENTS.md, Architecture and Cleanup, requires meaningful failure reasons; src/contracts/sessionToolContracts.ts:68-79 admits archives and documents optional snapshot close with failed-save retention; docs/cli.md:509-513 repeats retention; src/application/binary/BinarySession.ts:1078-1085 implements the accurate resource-only refusal used as a control.
  • Failure source: observed production stdio MCP replies and assertions from the reproduction above; src/application/binary/BinarySessionRecords.ts:180-215 reaches the profileless-native export guard and constructs the base integrity error, whose main message is selected by src/domain/analysisErrorPresentation.ts:320-321.
  • Evidence provenance: observed MCP failure and lifecycle controls; the source explanation and freshness comparison are static observations.
  • Local verification: reproduced
  • Reproduction completeness: complete

Typed outcome or recovery expected

Return an accurate snapshot prerequisite or applicability explanation and usable recovery for this session. Closing without snapshot_path should continue to work. Do not weaken integrity checks, silently publish an invalid snapshot or close the session after a failed save to hide the error. Restoration is observable when the same fresh ZIP request either legitimately saves a supported snapshot or refuses it with a truthful explanation; the generic corrupt-Evidence advice and unrelated native-workflow reason must no longer be the archive caller's recovery path.

Analysis or suspected cause

Source-derived: src/server/registerCloseLifecycleTool.ts:94-115 routes the supplied path into BinarySession.closeWithSnapshot, which exports before writing or closing (src/application/binary/BinarySession.ts:828-843). On macOS the available native-dispatch capability can supply a profileless reader commitment even though the fresh session's selected target is an archive. The export guard then describes its required native workflow observations through a generic EvidenceIntegrityError, selecting the corruption message rather than an applicability explanation. The native guard may correctly refuse this snapshot; its caller-visible diagnosis is the defect.

Fresh source reads of upstream main and the latest published release found the same export guard and generic Evidence message. Those artifacts were compared as source; separately built main and release runtimes were not executed.

Additional context

Closed issue #5945 addressed generic recovery for a valid existing snapshot replayed against different target bytes. Its closure describes a fix in the direct-analysis import path. This report instead exports from a fresh archive session, with no snapshot supplied, and reaches the separate session export guard. No open exact duplicate was found in the searched issues and PRs.

NOT_ENOUGH_INFO: the first affected revision and intended general archive snapshot support have not been established. Neither is needed to distinguish this observed refusal from an Evidence integrity failure.

Triage assessment

  • Impact level: P3
  • Assessment status: supported
  • Impact basis: The observed defect is misleading failure wording and recovery guidance. The refusal preserves the target bytes and live session; no consequential behavior change or data loss was demonstrated.
  • Workaround status: available
  • Workaround basis: For the tested empty archive session, close_binary without snapshot_path succeeds and avoids the misleading refusal. This completes ordinary closure, not snapshot persistence; persistence support is not the contract claimed by this report.
Originally created by @coygeek on GitHub (Oct 26, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/7311 ## 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 Evidence, artifacts, or reconstruction ## Observed behavior On macOS, opening a fresh one-member ZIP through `open_binary` succeeds. Immediately requesting `close_binary` with a new `snapshot_path` returns `evidence_integrity_mismatch` and tells the caller to recreate or re-import invalid Evidence. Its detailed reason is `Profileless native snapshots may contain only current dispatch workflow observations`, although this session contains an archive and no imported snapshot or analysis observations. No snapshot is created. The archive remains unchanged and the session stays open; closing without `snapshot_path` succeeds. This report concerns the misleading failure explanation and recovery advice. Remaining open after a failed save is the documented behavior. ## Reproduction Run the following from the built source checkout with its locked dependencies already available, using Node.js on macOS. It creates an isolated configuration and a stored ZIP containing only `sample.txt` with `hello` followed by a newline. The target is parsed, never executed. The script starts the production stdio MCP entry point and prints the actual replies. ```sh PROBE_DIR="${PROBE_DIR:-$(mktemp -d)}" export PROBE_DIR mkdir -p "$PROBE_DIR/home" "$PROBE_DIR/tmp" python3 - <<'PY' import os, zipfile from pathlib import Path root = Path(os.environ['PROBE_DIR']) with zipfile.ZipFile(root / 'sample.zip', 'w', compression=zipfile.ZIP_STORED) as z: z.writestr('sample.txt', 'hello\n') with zipfile.ZipFile(root / 'sample.zip') as z: assert z.testzip() is None PY node --input-type=module <<'JS' import assert from 'node:assert/strict'; import { access, readFile } from 'node:fs/promises'; import { createHash } from 'node:crypto'; import { join, resolve } from 'node:path'; import { Client } from './node_modules/@modelcontextprotocol/client/dist/index.mjs'; import { StdioClientTransport } from './node_modules/@modelcontextprotocol/client/dist/stdio.mjs'; const root = process.cwd(), work = resolve(process.env.PROBE_DIR); const archive = join(work, 'sample.zip'); const snapshot = join(work, 'snapshot.json'); const digest = async () => createHash('sha256').update(await readFile(archive)).digest('hex'); const before = await digest(); const client = new Client({ name: 'archive-snapshot-repro', version: '1' }); await client.connect(new StdioClientTransport({ command: process.execPath, args: [join(root, 'scripts/rea.mjs'), '--mcp'], cwd: work, env: { PATH: process.env.PATH, HOME: join(work, 'home'), TMPDIR: join(work, 'tmp') }, stderr: 'pipe', })); async function call(name, args = {}) { const reply = await client.callTool({ name, arguments: args }); const payload = JSON.parse(reply.content.find(p => p.type === 'text').text); console.log(name, JSON.stringify(payload)); return { reply, payload }; } try { assert.equal((await call('open_binary', { path: archive })).payload.result.kind, 'archive'); const refused = await call('close_binary', { snapshot_path: snapshot }); assert.equal(refused.reply.isError, true); assert.equal(refused.payload.error.details.reason, 'Profileless native snapshots may contain only current dispatch workflow observations'); assert.equal(await access(snapshot).then(() => true, () => false), false); assert.equal((await call('binary_session')).payload.result.open, true); assert.equal((await call('close_binary')).payload.result, null); assert.equal((await call('binary_session')).payload.result.open, false); assert.equal(await digest(), before); console.log('PASS: refusal reproduced; no snapshot; normal close succeeded; ZIP unchanged'); } finally { await client.close(); } JS ``` The same diagnostic occurred in two separate MCP server processes with newly generated ZIP fixtures. The exact block above was executed once as the second reproduction; the first also ran the resource-only control described below. ## Expected behavior When the current archive session cannot supply an analysis snapshot, explain the unmet snapshot prerequisite or applicability limit and give recovery that applies to this session. Do not describe freshly accepted, unchanged input as corrupt Evidence or recommend recreating/re-importing Evidence that was never supplied. Ordinary closure without a snapshot is an available recovery for the tested empty session. The repository requires meaningful failure reasons (`AGENTS.md`, Architecture and Cleanup). The existing resource-only snapshot refusal demonstrates this contract: it explicitly says analysis snapshots do not apply to that session, rather than diagnosing corrupted Evidence. Preserve failed-save session retention and refusal of invalid snapshot bindings. Whether archive snapshots should be supported is a separate capability question; this report does not infer support from an omitted exclusion or require snapshot creation to succeed. ## Impact and scope Observed impact is incorrect diagnostic and recovery wording in the tested fresh ZIP session. No output corruption, invalid replay, target change or lost analysis history was observed; the fixture had no analysis observations. Omitting `snapshot_path` completes ordinary closure but does not provide snapshot persistence. Other operating systems, archive formats, populated sessions and real-provider persistence were not tested. ## REA version `rea-agents` 6.8.0; source revision `e89192653402222c5b099216f0d83a2d1bceace0`. The existing compiled MCP runtime reported server version 6.8.0. ## Runtime and platform Node.js v24.22.1; macOS 27.0.1 arm64; `@modelcontextprotocol/client` 2.3.1; actual stdio MCP transport through `scripts/rea.mjs --mcp`. No external analysis engine was started. ## Target and artifact context Locally generated ZIP, one stored regular member containing six bytes of text; `open_binary` reported `format: "zip"`, `kind: "archive"`, a SHA-256 identity and null architecture. Python's ZIP member CRC check passed, and the archive's SHA-256 before and after the MCP sequence matched. No snapshot or Evidence bundle was imported. ## Interface MCP ## Earliest failure stage Cleanup, cancellation, or lifecycle ## Relevant output or evidence The failing MCP call returned `isError: true` and this text payload: ```json {"error":{"code":"evidence_integrity_mismatch","category":"integrity_mismatch","message":"Evidence is invalid or has changed. Recreate or re-import it, then try again.","retryable":false,"remediation":{"action":"Evidence is invalid or has changed. Recreate or re-import it, then try again."},"details":{"reason":"Profileless native snapshots may contain only current dispatch workflow observations"}}} ``` The executed assertions confirmed that the selected snapshot path did not exist, `binary_session.result.open` remained true, `close_binary {}` returned `{"result":null}`, the next session status reported `open: false`, and the ZIP hash was unchanged. A separate control in the first server process opened an authored resource-only app with `open_bundle_resources`, then requested a snapshot close. Its main message and `details.reason` both correctly stated: `Analysis snapshots do not apply to a resource-only app session because it has no executable identity or replay binding.` Its error code was also `evidence_integrity_mismatch`; the diagnostic defect here is not established by that code alone. - Expected source: `AGENTS.md`, Architecture and Cleanup, requires meaningful failure reasons; `src/contracts/sessionToolContracts.ts:68-79` admits archives and documents optional snapshot close with failed-save retention; `docs/cli.md:509-513` repeats retention; `src/application/binary/BinarySession.ts:1078-1085` implements the accurate resource-only refusal used as a control. - Failure source: observed production stdio MCP replies and assertions from the reproduction above; `src/application/binary/BinarySessionRecords.ts:180-215` reaches the profileless-native export guard and constructs the base integrity error, whose main message is selected by `src/domain/analysisErrorPresentation.ts:320-321`. - Evidence provenance: observed MCP failure and lifecycle controls; the source explanation and freshness comparison are static observations. - Local verification: reproduced - Reproduction completeness: complete ## Typed outcome or recovery expected Return an accurate snapshot prerequisite or applicability explanation and usable recovery for this session. Closing without `snapshot_path` should continue to work. Do not weaken integrity checks, silently publish an invalid snapshot or close the session after a failed save to hide the error. Restoration is observable when the same fresh ZIP request either legitimately saves a supported snapshot or refuses it with a truthful explanation; the generic corrupt-Evidence advice and unrelated native-workflow reason must no longer be the archive caller's recovery path. ## Analysis or suspected cause Source-derived: `src/server/registerCloseLifecycleTool.ts:94-115` routes the supplied path into `BinarySession.closeWithSnapshot`, which exports before writing or closing (`src/application/binary/BinarySession.ts:828-843`). On macOS the available native-dispatch capability can supply a profileless reader commitment even though the fresh session's selected target is an archive. The export guard then describes its required native workflow observations through a generic `EvidenceIntegrityError`, selecting the corruption message rather than an applicability explanation. The native guard may correctly refuse this snapshot; its caller-visible diagnosis is the defect. Fresh source reads of upstream main and the latest published release found the same export guard and generic Evidence message. Those artifacts were compared as source; separately built main and release runtimes were not executed. ## Additional context [Closed issue #5945](https://github.com/morluto/rea/issues/5945) addressed generic recovery for a valid existing snapshot replayed against different target bytes. Its closure describes a fix in the direct-analysis import path. This report instead exports from a fresh archive session, with no snapshot supplied, and reaches the separate session export guard. No open exact duplicate was found in the searched issues and PRs. NOT_ENOUGH_INFO: the first affected revision and intended general archive snapshot support have not been established. Neither is needed to distinguish this observed refusal from an Evidence integrity failure. ## Triage assessment - Impact level: P3 - Assessment status: supported - Impact basis: The observed defect is misleading failure wording and recovery guidance. The refusal preserves the target bytes and live session; no consequential behavior change or data loss was demonstrated. - Workaround status: available - Workaround basis: For the tested empty archive session, `close_binary` without `snapshot_path` succeeds and avoids the misleading refusal. This completes ordinary closure, not snapshot persistence; persistence support is not the contract claimed by this report.
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#4342
No description provided.