[GH-ISSUE #4377] [Bug] Snapshot import returns zero new entries after retaining a workflow result #1732

Closed
opened 2026-10-16 21:09:35 +00:00 by bsakaguchi · 0 comments
Owner

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

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

Source-boundary finding: AnalysisSnapshotCache.stage accepts and retains a valid snapshot with one trace_feature workflow binding but returns 0. An equivalent one-direct-result snapshot returns 1; repeats of either return 0. Thus a first workflow-only stage is indistinguishable from an idempotent repeat in the returned count. This was executed against source modules, not the compiled CLI/MCP or a real provider.

Reproduction

This is a source-boundary reproduction, not a compiled CLI/MCP or real-provider run. From a current-main source checkout, install its locked dependencies with npm ci, save the following as repro.mts, and run the command below. It creates valid snapshots through the normal cache export/parser and Evidence bundle helpers; it opens no target and starts no engine.

import assert from 'node:assert/strict';
import {pathToFileURL} from 'node:url';
const root=process.env.REA_CHECKOUT!;
const load=(p:string)=>import(pathToFileURL(`${root}/src/${p}.ts`).href);
const {AnalysisSnapshotCache}=await load('application/binary/AnalysisSnapshotCache');
const {ANALYSIS_SNAPSHOT_TARGET:target,ANALYSIS_SNAPSHOT_PROFILE:profile,ANALYSIS_SNAPSHOT_PROVIDER:provider}=await load('domain/analysisSnapshot.fixture');
const {createAnalysisExecution}=await load('application/AnalysisProvider');
const {REA_WORKFLOW_PROVIDER,workflowAnalysisProfile}=await load('application/InvestigationProviders');
const {createEvidence}=await load('domain/evidence');
const {createEvidenceBundle}=await load('domain/evidenceBundle');
for(const workflow of [false,true]){
 const source=new AnalysisSnapshotCache();
 const p=workflow?workflowAnalysisProfile(profile,'trace_feature'):profile;
 const prov=workflow?REA_WORKFLOW_PROVIDER:provider;
 const operation=workflow?'trace_feature':'address_name';
 const parameters=workflow?{query:'demo',case_sensitive:false}:{address:'0x1000',document:'fixture'};
 const result=workflow?{query:'demo',search_mode:'literal',matches:[],references:[],truncated:false,residual_unknowns:[]}:'entry';
 const execution=createAnalysisExecution(result,prov,{subject:target,analysisProfile:p});
 const input={target,profile,operation,parameters,execution};
 if(workflow)source.recordWorkflow(input);else source.record(input);
 const evidence=createEvidence(target,prov,{operation,parameters,result,analysisProfile:p,...(workflow?{confidence:'derived'}:{})});
 const exported=source.export(target,profile,createEvidenceBundle([evidence]));
 assert.equal(exported.ok,true);if(!exported.ok)throw exported.error;
 const dest=new AnalysisSnapshotCache(); const count=dest.stage(exported.value);
 const retained=dest.entries().length+dest.workflowEntries().length;
 assert.equal(retained,1);assert.equal(count,workflow?0:1);assert.equal(dest.stage(exported.value),0);
 console.log(JSON.stringify({kind:workflow?'workflow':'direct',reportedNewEntries:count,retainedEntries:retained,repeatNewEntries:0}));
}
REA_CHECKOUT="$PWD" node_modules/.bin/tsx repro.mts

The source-boundary probe was executed twice on each baseline and produced the same output on every run. For the release check, REA_CHECKOUT pointed to the release checkout and the runner was the current-main checkout's tsx v4.23.15, because the release does not depend on tsx:

{"kind":"direct","reportedNewEntries":1,"retainedEntries":1,"repeatNewEntries":0}
{"kind":"workflow","reportedNewEntries":0,"retainedEntries":1,"repeatNewEntries":0}

Expected behavior

The method documents a “new-entry count,” so a newly retained workflow binding should contribute to that count just as a new direct binding does. The shared cache capacity already includes both kinds. This report concerns the import return value, not the legacy primitive-only entries field in close/save receipts.

Impact and scope

The count is incorrect for newly retained workflow bindings and propagates through BinarySessionRecords.importAnalysisSnapshot. Imported data remains accessible; no Evidence loss or replay failure was observed. Both current in-tree binary-open callers check success and ignore the number. NOT_ENOUGH_INFO: no user-visible CLI/MCP failure or external caller depending on the count was demonstrated.

REA version

Current main source 8ab5024397ddbd3b37ab68a13cabedc2eb7ce7dd; latest published rea-agents 6.5.0 source 3b79af28e80785afce9f5998d8f03289402a1847.

Runtime and platform

Node v24.18.0; macOS arm64; locked dependencies. Source-boundary execution; no Hopper/Ghidra or compiled CLI/MCP run.

Target and artifact context

A parsed analysis snapshot with one synthetic workflow binding and matching Evidence; a direct-result snapshot is the control. No executable is opened.

Interface

Other

Application/cache source API.

Earliest failure stage

Artifact or evidence registration

Relevant output or evidence

The inline output shows a retained workflow binding with reported count zero on both baselines. The stage contract and loops document a new-entry count but increment it only for direct entries. The application import method forwards the result unchanged. Source inspection of BinarySession.ts at the current baseline shows both binary-open call sites ignore the number.

Typed outcome or recovery expected

Return 1 on first staging of the one-workflow snapshot and 0 on its repeat. For mixed snapshots, report newly retained bindings of both kinds; replacements and capacity-refused additions must not inflate the count.

Analysis or suspected cause

Source-derived: the direct-entry loop increments imported for a new query ID; the workflow-entry loop inserts into its map without incrementing it. This does not establish a CLI/MCP failure.

Additional context

Closed #1248 and merged #1265 address close/save receipt counts and intentionally preserve their legacy primitive-only entries field. This report instead covers the cache's documented new-entry count. Restoration should preserve current retained Evidence, replay, replacement, and capacity behavior. Last known good version: NOT_ENOUGH_INFO; no regression claim.

Triage assessment

  • Impact level: P2
  • Assessment status: provisional
  • Impact basis: The source-boundary probe observes an incorrect numeric import result for retained workflow bindings. Current in-tree CLI/MCP open paths ignore the number, and a user-visible caller depending on it has not been demonstrated; broader impact is unknown.
  • Workaround status: available
  • Workaround basis: For an unchanged target/profile, a caller using the cache can compare the retained direct and workflow query IDs before and after staging; the focused probe confirms that the workflow entry is retained and accessible even when the reported count is zero.
Originally created by @coygeek on GitHub (Oct 16, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/4377 ### 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 Source-boundary finding: `AnalysisSnapshotCache.stage` accepts and retains a valid snapshot with one `trace_feature` workflow binding but returns `0`. An equivalent one-direct-result snapshot returns `1`; repeats of either return `0`. Thus a first workflow-only stage is indistinguishable from an idempotent repeat in the returned count. This was executed against source modules, not the compiled CLI/MCP or a real provider. ### Reproduction This is a source-boundary reproduction, not a compiled CLI/MCP or real-provider run. From a current-main source checkout, install its locked dependencies with `npm ci`, save the following as `repro.mts`, and run the command below. It creates valid snapshots through the normal cache export/parser and Evidence bundle helpers; it opens no target and starts no engine. ```ts import assert from 'node:assert/strict'; import {pathToFileURL} from 'node:url'; const root=process.env.REA_CHECKOUT!; const load=(p:string)=>import(pathToFileURL(`${root}/src/${p}.ts`).href); const {AnalysisSnapshotCache}=await load('application/binary/AnalysisSnapshotCache'); const {ANALYSIS_SNAPSHOT_TARGET:target,ANALYSIS_SNAPSHOT_PROFILE:profile,ANALYSIS_SNAPSHOT_PROVIDER:provider}=await load('domain/analysisSnapshot.fixture'); const {createAnalysisExecution}=await load('application/AnalysisProvider'); const {REA_WORKFLOW_PROVIDER,workflowAnalysisProfile}=await load('application/InvestigationProviders'); const {createEvidence}=await load('domain/evidence'); const {createEvidenceBundle}=await load('domain/evidenceBundle'); for(const workflow of [false,true]){ const source=new AnalysisSnapshotCache(); const p=workflow?workflowAnalysisProfile(profile,'trace_feature'):profile; const prov=workflow?REA_WORKFLOW_PROVIDER:provider; const operation=workflow?'trace_feature':'address_name'; const parameters=workflow?{query:'demo',case_sensitive:false}:{address:'0x1000',document:'fixture'}; const result=workflow?{query:'demo',search_mode:'literal',matches:[],references:[],truncated:false,residual_unknowns:[]}:'entry'; const execution=createAnalysisExecution(result,prov,{subject:target,analysisProfile:p}); const input={target,profile,operation,parameters,execution}; if(workflow)source.recordWorkflow(input);else source.record(input); const evidence=createEvidence(target,prov,{operation,parameters,result,analysisProfile:p,...(workflow?{confidence:'derived'}:{})}); const exported=source.export(target,profile,createEvidenceBundle([evidence])); assert.equal(exported.ok,true);if(!exported.ok)throw exported.error; const dest=new AnalysisSnapshotCache(); const count=dest.stage(exported.value); const retained=dest.entries().length+dest.workflowEntries().length; assert.equal(retained,1);assert.equal(count,workflow?0:1);assert.equal(dest.stage(exported.value),0); console.log(JSON.stringify({kind:workflow?'workflow':'direct',reportedNewEntries:count,retainedEntries:retained,repeatNewEntries:0})); } ``` ```sh REA_CHECKOUT="$PWD" node_modules/.bin/tsx repro.mts ``` The source-boundary probe was executed twice on each baseline and produced the same output on every run. For the release check, `REA_CHECKOUT` pointed to the release checkout and the runner was the current-main checkout's tsx v4.23.15, because the release does not depend on tsx: ```text {"kind":"direct","reportedNewEntries":1,"retainedEntries":1,"repeatNewEntries":0} {"kind":"workflow","reportedNewEntries":0,"retainedEntries":1,"repeatNewEntries":0} ``` ### Expected behavior The method documents a “new-entry count,” so a newly retained workflow binding should contribute to that count just as a new direct binding does. The shared cache capacity already includes both kinds. This report concerns the import return value, not the legacy primitive-only `entries` field in close/save receipts. ### Impact and scope The count is incorrect for newly retained workflow bindings and propagates through `BinarySessionRecords.importAnalysisSnapshot`. Imported data remains accessible; no Evidence loss or replay failure was observed. Both current in-tree binary-open callers check success and ignore the number. NOT_ENOUGH_INFO: no user-visible CLI/MCP failure or external caller depending on the count was demonstrated. ### REA version Current main source `8ab5024397ddbd3b37ab68a13cabedc2eb7ce7dd`; latest published rea-agents 6.5.0 source `3b79af28e80785afce9f5998d8f03289402a1847`. ### Runtime and platform Node v24.18.0; macOS arm64; locked dependencies. Source-boundary execution; no Hopper/Ghidra or compiled CLI/MCP run. ### Target and artifact context A parsed analysis snapshot with one synthetic workflow binding and matching Evidence; a direct-result snapshot is the control. No executable is opened. ### Interface Other Application/cache source API. ### Earliest failure stage Artifact or evidence registration ### Relevant output or evidence The inline output shows a retained workflow binding with reported count zero on both baselines. The [stage contract and loops](https://github.com/morluto/rea/blob/8ab5024397ddbd3b37ab68a13cabedc2eb7ce7dd/src/application/binary/AnalysisSnapshotCache.ts#L121-L153) document a new-entry count but increment it only for direct entries. The [application import method](https://github.com/morluto/rea/blob/8ab5024397ddbd3b37ab68a13cabedc2eb7ce7dd/src/application/binary/BinarySessionRecords.ts#L217-L247) forwards the result unchanged. Source inspection of `BinarySession.ts` at the current baseline shows both binary-open call sites ignore the number. ### Typed outcome or recovery expected Return `1` on first staging of the one-workflow snapshot and `0` on its repeat. For mixed snapshots, report newly retained bindings of both kinds; replacements and capacity-refused additions must not inflate the count. ### Analysis or suspected cause Source-derived: the direct-entry loop increments `imported` for a new query ID; the workflow-entry loop inserts into its map without incrementing it. This does not establish a CLI/MCP failure. ### Additional context Closed [#1248](https://github.com/morluto/rea/issues/1248) and merged [#1265](https://github.com/morluto/rea/pull/1265) address close/save receipt counts and intentionally preserve their legacy primitive-only `entries` field. This report instead covers the cache's documented new-entry count. Restoration should preserve current retained Evidence, replay, replacement, and capacity behavior. Last known good version: NOT_ENOUGH_INFO; no regression claim. ## Triage assessment - Impact level: P2 - Assessment status: provisional - Impact basis: The source-boundary probe observes an incorrect numeric import result for retained workflow bindings. Current in-tree CLI/MCP open paths ignore the number, and a user-visible caller depending on it has not been demonstrated; broader impact is unknown. - Workaround status: available - Workaround basis: For an unchanged target/profile, a caller using the cache can compare the retained direct and workflow query IDs before and after staging; the focused probe confirms that the workflow entry is retained and accessible even when the reported count is zero.
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#1732
No description provided.