[GH-ISSUE #5406] [Bug] compare-application-versions pairs unresolved endpoints by equal expression text #3276

Closed
opened 2026-10-31 00:38:40 +00:00 by bsakaguchi · 2 comments
Owner

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

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

compare-application-versions selects a medium-confidence endpoint match when two analyzed applications contain unresolved fetch(API_BASE) expressions at different source sites. It also pairs an unresolved fetch(API_BASE) with exact fetch("API_BASE") because their displayed endpoint values match.

An independent verifier ran five directory analyses and four CLI comparisons, all exiting 0 with complete analysis coverage. Both failure comparisons returned one endpoint item with match.status: "matched", basis: "semantic-key", confidence: "medium", and empty candidate arrays. Distinct node IDs and both original observations remain retained. The first item is classified unknown; this does not undo the selected endpoint pair. No request was executed and no difference between actual runtime destinations is claimed.

Reproduction

Run from the source checkout at the revision below, with locked dependencies and the built CLI already available. The verifier executed this complete script successfully. Fixture calls are analyzed statically, never executed. The first two comparisons reproduce the failure; the last two are controls.

export WORK="$(mktemp -d)"
export HOME="$WORK/home"
export XDG_CONFIG_HOME="$WORK/config"
export XDG_DATA_HOME="$WORK/data"
export XDG_CACHE_HOME="$WORK/cache"
export XDG_STATE_HOME="$WORK/state"
export TMPDIR="$WORK/temp"
export REA_LOG_LEVEL=silent
mkdir -p "$HOME" "$XDG_CONFIG_HOME" "$XDG_DATA_HOME" "$XDG_CACHE_HOME" "$XDG_STATE_HOME" "$TMPDIR"
node --input-type=module <<'JS'
import { mkdirSync, writeFileSync } from 'node:fs';
import { join } from 'node:path';
import { execFileSync } from 'node:child_process';
const work = process.env.WORK;
const cli = (name, args) => {
  const output = execFileSync(process.execPath, ['scripts/rea.mjs', ...args], {
    encoding: 'utf8', timeout: 90000,
    env: Object.fromEntries(['PATH', 'HOME', 'XDG_CONFIG_HOME', 'XDG_DATA_HOME',
      'XDG_CACHE_HOME', 'XDG_STATE_HOME', 'TMPDIR', 'REA_LOG_LEVEL']
      .map(key => [key, process.env[key]])),
  });
  writeFileSync(join(work, `${name}.json`), output);
  return JSON.parse(output);
};
const fixtures = [
  ['left', 'a.js', 'fetch(API_BASE);\n'],
  ['moved', 'b.js', '// changed site\nfetch(API_BASE);\n'],
  ['exact', 'c.js', 'fetch("API_BASE");\n'],
  ['same-site', 'a.js', 'fetch(API_BASE);\n// artifact changed\n'],
  ['exact-control', 'c.js', '// changed artifact\nfetch("API_BASE");\n'],
];
const evidence = {};
for (const [name, file, source] of fixtures) {
  const app = join(work, `app-${name}`);
  mkdirSync(app);
  writeFileSync(join(app, 'package.json'), JSON.stringify({name: 'endpoint-check', main: file}));
  writeFileSync(join(app, file), source);
  evidence[name] = cli(name, ['analyze-javascript-application', app,
    '--artifact-format', 'directory', '--json']);
  const graph = evidence[name].normalized_result.graph;
  console.log(name, 'coverage:', graph.coverage.status);
  console.log(JSON.stringify(graph.nodes.filter(node => node.kind === 'endpoint')
    .map(node => ({node_id: node.node_id, observations: node.observations.map(obs => ({
      properties: obs.properties, location: obs.evidence.location,
    }))}))));
}
for (const [left, right] of [['left', 'moved'], ['left', 'exact'],
  ['left', 'same-site'], ['exact', 'exact-control']]) {
  const input = join(work, `${left}-${right}-input.json`);
  writeFileSync(input, JSON.stringify({left: evidence[left], right: evidence[right]}));
  const comparison = cli(`compare-${left}-${right}`,
    ['compare-application-versions', input, '--json']);
  console.log(left, right, JSON.stringify(comparison.normalized_result.items
    .filter(item => item.node_kind === 'endpoint')));
}
JS

Expected behavior

docs/javascript-application-graph.md:71-78 says partial endpoint values preserve unresolved source expressions, partial and exact identities differ even with equal displayed values, and matching partial expression text at different sites does not establish the same runtime target.

The comparison should leave correspondence unresolved when equal text is the only pairing evidence in these cases. docs/javascript-application-workflows.md, “Version comparison”, permits unique inferred matches, but equal unresolved syntax alone does not establish endpoint correspondence. Preserve justified exact-value matching across artifact changes and retain partial source-site evidence without claiming a resolved target.

Impact and scope

P2: bounded incorrect endpoint correspondence can mislead callers interpreting endpoint changes. The observed scope is the two supplied complete-coverage directory comparisons; wider frequency and other node kinds were not tested. A workaround is to inspect retained resolution properties and locations and disregard these text-only inferred pairs manually. No data loss, target execution, or security boundary crossing was observed.

REA version

6.7.0; source commit 074768901f3cdc8205b61773fb1faf6f6c7b3ca8 (prebuilt source CLI).

Runtime and platform

Node.js v26.12.0; macOS (Darwin), arm64. NOT_ENOUGH_INFO: the verifier did not record the macOS version. No external provider was used.

Target and artifact context

Five disposable application directories, each containing a package manifest and one JavaScript file. Comparisons used authentic Evidence returned by analyze-javascript-application --artifact-format directory --json. No fixture code or network service was executed.

Interface

CLI

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

The verifier's CLI outputs show:

{"comparison":"partial a.js:1 versus partial b.js:2","status":"unknown","dimensions":["location"],"match":{"status":"matched","basis":"semantic-key","confidence":"medium","candidate_left_node_ids":[],"candidate_right_node_ids":[]}}
{"comparison":"partial a.js:1 versus exact c.js:1","status":"changed","dimensions":["location","properties"],"match":{"status":"matched","basis":"semantic-key","confidence":"medium","candidate_left_node_ids":[],"candidate_right_node_ids":[]}}

These are reduced endpoint-item fields with a descriptive comparison label. All endpoints display API_BASE; the partial observations have resolution: "partial", while the exact literal has no resolution property. Both nodes and original observations remain in the comparison graph. Limitations identify deterministic inference rather than exact identity and unresolved neighboring relationship identity.

The same-site partial control receives a semantic-key match with no changed dimensions. The changed-artifact exact-value control also receives a semantic-key match, with a location difference. Both controls remain unknown because neighboring relationship alignment is unresolved.

Typed outcome or recovery expected

Return a comparison that preserves separate unresolved endpoint correspondence for the two failure cases unless additional evidence supports pairing. Keep both observations and their uncertainty. The controls need not be classified unchanged while their relationship identities remain unresolved.

Analysis or suspected cause

Source-derived reasoning at the reported revision: src/domain/javascript/javascriptApplicationVersionKeys.ts:225-248 builds the endpoint semantic key from endpoint kind and value, omitting partial resolution and source site. Its unique-only tier at line 124 selects the sole endpoint on each side. src/domain/javascript/javascriptApplicationVersionItems.ts:141-186 emits the selected matched item. This treats unresolved expression syntax and exact endpoint values as interchangeable for matching.

Additional context

Prepared with AI assistance and independently reproduced through the public CLI. The exact inline reproduction above was executed by the independent verifier. Upstream main was fetched before submission; no later commit changed the cited matching files or expected-contract files. Searches included open and closed issues and PRs for the command, partial endpoints, semantic keys, pairing, and the source module. Related reports about location-only classification (#4520), empty literal identities (#843), and module matching (#3188, #5195, #5196) concern different behavior.

NOT_ENOUGH_INFO: no last-known-good version or first-bad release was established.

Originally created by @coygeek on GitHub (Oct 20, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/5406 ### 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 `compare-application-versions` selects a medium-confidence endpoint match when two analyzed applications contain unresolved `fetch(API_BASE)` expressions at different source sites. It also pairs an unresolved `fetch(API_BASE)` with exact `fetch("API_BASE")` because their displayed endpoint values match. An independent verifier ran five directory analyses and four CLI comparisons, all exiting 0 with complete analysis coverage. Both failure comparisons returned one endpoint item with `match.status: "matched"`, `basis: "semantic-key"`, `confidence: "medium"`, and empty candidate arrays. Distinct node IDs and both original observations remain retained. The first item is classified `unknown`; this does not undo the selected endpoint pair. No request was executed and no difference between actual runtime destinations is claimed. ### Reproduction Run from the source checkout at the revision below, with locked dependencies and the built CLI already available. The verifier executed this complete script successfully. Fixture calls are analyzed statically, never executed. The first two comparisons reproduce the failure; the last two are controls. ```sh export WORK="$(mktemp -d)" export HOME="$WORK/home" export XDG_CONFIG_HOME="$WORK/config" export XDG_DATA_HOME="$WORK/data" export XDG_CACHE_HOME="$WORK/cache" export XDG_STATE_HOME="$WORK/state" export TMPDIR="$WORK/temp" export REA_LOG_LEVEL=silent mkdir -p "$HOME" "$XDG_CONFIG_HOME" "$XDG_DATA_HOME" "$XDG_CACHE_HOME" "$XDG_STATE_HOME" "$TMPDIR" node --input-type=module <<'JS' import { mkdirSync, writeFileSync } from 'node:fs'; import { join } from 'node:path'; import { execFileSync } from 'node:child_process'; const work = process.env.WORK; const cli = (name, args) => { const output = execFileSync(process.execPath, ['scripts/rea.mjs', ...args], { encoding: 'utf8', timeout: 90000, env: Object.fromEntries(['PATH', 'HOME', 'XDG_CONFIG_HOME', 'XDG_DATA_HOME', 'XDG_CACHE_HOME', 'XDG_STATE_HOME', 'TMPDIR', 'REA_LOG_LEVEL'] .map(key => [key, process.env[key]])), }); writeFileSync(join(work, `${name}.json`), output); return JSON.parse(output); }; const fixtures = [ ['left', 'a.js', 'fetch(API_BASE);\n'], ['moved', 'b.js', '// changed site\nfetch(API_BASE);\n'], ['exact', 'c.js', 'fetch("API_BASE");\n'], ['same-site', 'a.js', 'fetch(API_BASE);\n// artifact changed\n'], ['exact-control', 'c.js', '// changed artifact\nfetch("API_BASE");\n'], ]; const evidence = {}; for (const [name, file, source] of fixtures) { const app = join(work, `app-${name}`); mkdirSync(app); writeFileSync(join(app, 'package.json'), JSON.stringify({name: 'endpoint-check', main: file})); writeFileSync(join(app, file), source); evidence[name] = cli(name, ['analyze-javascript-application', app, '--artifact-format', 'directory', '--json']); const graph = evidence[name].normalized_result.graph; console.log(name, 'coverage:', graph.coverage.status); console.log(JSON.stringify(graph.nodes.filter(node => node.kind === 'endpoint') .map(node => ({node_id: node.node_id, observations: node.observations.map(obs => ({ properties: obs.properties, location: obs.evidence.location, }))})))); } for (const [left, right] of [['left', 'moved'], ['left', 'exact'], ['left', 'same-site'], ['exact', 'exact-control']]) { const input = join(work, `${left}-${right}-input.json`); writeFileSync(input, JSON.stringify({left: evidence[left], right: evidence[right]})); const comparison = cli(`compare-${left}-${right}`, ['compare-application-versions', input, '--json']); console.log(left, right, JSON.stringify(comparison.normalized_result.items .filter(item => item.node_kind === 'endpoint'))); } JS ``` ### Expected behavior `docs/javascript-application-graph.md:71-78` says partial endpoint values preserve unresolved source expressions, partial and exact identities differ even with equal displayed values, and matching partial expression text at different sites does not establish the same runtime target. The comparison should leave correspondence unresolved when equal text is the only pairing evidence in these cases. `docs/javascript-application-workflows.md`, “Version comparison”, permits unique inferred matches, but equal unresolved syntax alone does not establish endpoint correspondence. Preserve justified exact-value matching across artifact changes and retain partial source-site evidence without claiming a resolved target. ### Impact and scope P2: bounded incorrect endpoint correspondence can mislead callers interpreting endpoint changes. The observed scope is the two supplied complete-coverage directory comparisons; wider frequency and other node kinds were not tested. A workaround is to inspect retained resolution properties and locations and disregard these text-only inferred pairs manually. No data loss, target execution, or security boundary crossing was observed. ### REA version 6.7.0; source commit `074768901f3cdc8205b61773fb1faf6f6c7b3ca8` (prebuilt source CLI). ### Runtime and platform Node.js v26.12.0; macOS (Darwin), arm64. NOT_ENOUGH_INFO: the verifier did not record the macOS version. No external provider was used. ### Target and artifact context Five disposable application directories, each containing a package manifest and one JavaScript file. Comparisons used authentic Evidence returned by `analyze-javascript-application --artifact-format directory --json`. No fixture code or network service was executed. ### Interface CLI ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence The verifier's CLI outputs show: ```json {"comparison":"partial a.js:1 versus partial b.js:2","status":"unknown","dimensions":["location"],"match":{"status":"matched","basis":"semantic-key","confidence":"medium","candidate_left_node_ids":[],"candidate_right_node_ids":[]}} {"comparison":"partial a.js:1 versus exact c.js:1","status":"changed","dimensions":["location","properties"],"match":{"status":"matched","basis":"semantic-key","confidence":"medium","candidate_left_node_ids":[],"candidate_right_node_ids":[]}} ``` These are reduced endpoint-item fields with a descriptive comparison label. All endpoints display `API_BASE`; the partial observations have `resolution: "partial"`, while the exact literal has no resolution property. Both nodes and original observations remain in the comparison graph. Limitations identify deterministic inference rather than exact identity and unresolved neighboring relationship identity. The same-site partial control receives a semantic-key match with no changed dimensions. The changed-artifact exact-value control also receives a semantic-key match, with a location difference. Both controls remain `unknown` because neighboring relationship alignment is unresolved. ### Typed outcome or recovery expected Return a comparison that preserves separate unresolved endpoint correspondence for the two failure cases unless additional evidence supports pairing. Keep both observations and their uncertainty. The controls need not be classified `unchanged` while their relationship identities remain unresolved. ### Analysis or suspected cause Source-derived reasoning at the reported revision: `src/domain/javascript/javascriptApplicationVersionKeys.ts:225-248` builds the endpoint semantic key from endpoint kind and value, omitting partial resolution and source site. Its unique-only tier at line 124 selects the sole endpoint on each side. `src/domain/javascript/javascriptApplicationVersionItems.ts:141-186` emits the selected matched item. This treats unresolved expression syntax and exact endpoint values as interchangeable for matching. ### Additional context Prepared with AI assistance and independently reproduced through the public CLI. The exact inline reproduction above was executed by the independent verifier. Upstream main was fetched before submission; no later commit changed the cited matching files or expected-contract files. Searches included open and closed issues and PRs for the command, partial endpoints, semantic keys, pairing, and the source module. Related reports about location-only classification (#4520), empty literal identities (#843), and module matching (#3188, #5195, #5196) concern different behavior. NOT_ENOUGH_INFO: no last-known-good version or first-bad release was established.
Author
Owner

@matbrgz commented on GitHub (Oct 20, 2026):

Cross-referencing with sibling application version comparison reports in Cluster AO: #4681 ([Bug] Application version comparison treats unresolved relationship targets as aligned) and #5195. All three pinpoint pairing logic and identity node alignment defects in compare-application-versions.

Consolidated under Cluster AO (Application Version Diffing & Endpoint Comparison Anomalies) in tracker #4330.

<!-- gh-comment-id:6291591530 --> @matbrgz commented on GitHub (Oct 20, 2026): Cross-referencing with sibling application version comparison reports in **Cluster AO**: #4681 (`[Bug] Application version comparison treats unresolved relationship targets as aligned`) and #5195. All three pinpoint pairing logic and identity node alignment defects in `compare-application-versions`. Consolidated under **Cluster AO (Application Version Diffing & Endpoint Comparison Anomalies)** in tracker #4330.
Author
Owner

@coygeek commented on GitHub (Oct 24, 2026):

Checked the reported fix on main at d87e73f2db.

Endpoint matching now consumes a shared correspondence owner. Exact literal targets use an exact semantic key; partial/candidate targets require a source-range key that also includes interpretation and resolution. Equal expression text at different sites no longer pairs, and partial versus exact values cannot collide.

Implementation: javascriptApplicationEndpointCorrespondence.ts, javascriptApplicationEndpointCorrespondence.ts.

The relevant regression checks in javascriptApplicationEndpointCorrespondence.test.ts passed in the focused test run on this revision. This closure is based on the current source and those checks; provider fixtures do not establish a real-engine run. Closing as completed on main.

<!-- gh-comment-id:6380780706 --> @coygeek commented on GitHub (Oct 24, 2026): Checked the reported fix on `main` at d87e73f2db50be72d33a4b3c7199af35518b441c. Endpoint matching now consumes a shared correspondence owner. Exact literal targets use an exact semantic key; partial/candidate targets require a source-range key that also includes interpretation and resolution. Equal expression text at different sites no longer pairs, and partial versus exact values cannot collide. Implementation: [javascriptApplicationEndpointCorrespondence.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/domain/javascript/javascriptApplicationEndpointCorrespondence.ts#L53), [javascriptApplicationEndpointCorrespondence.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/domain/javascript/javascriptApplicationEndpointCorrespondence.ts#L65). The relevant regression checks in [javascriptApplicationEndpointCorrespondence.test.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/domain/javascript/javascriptApplicationEndpointCorrespondence.test.ts) passed in the focused test run on this revision. This closure is based on the current source and those checks; provider fixtures do not establish a real-engine run. Closing as completed on main.
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#3276
No description provided.