[GH-ISSUE #2522] fix(javascript): complete version-change graphs lose input coverage limits #839

Closed
opened 2026-10-15 18:28:54 +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/2522

Summary

Comparing two complete JavaScript application graphs removes their declared coverage limits from the resulting change graph and from inferred change-edge Evidence. Complete input coverage can legitimately report configured bounds even when nothing was omitted. The same limits survive when an input is truncated, so the provenance loss depends on the complete-coverage branch.

Steps to reproduce

  1. Use the source checkout described under Runtime or environment with installed lockfile dependencies.
  2. Save this focused fixture as src/domain/javascript/completeComparisonLimits.test.ts.
  3. Run npm run test:focused -- src/domain/javascript/completeComparisonLimits.test.ts.
import { expect, it } from "vitest";
import { artifactEvidence, fixtureNode } from "./javascriptApplicationGraph.fixture.js";
import { createJavaScriptApplicationGraph } from "./javascriptApplicationGraph.js";
import { completeApplicationCoverage } from "./javascriptApplicationEvidenceSchemas.js";
import { compareJavaScriptApplicationVersions } from "./javascriptApplicationVersionComparison.js";

const limits = [
  { name: "modules", value: 100, unit: "items" as const },
  { name: "source-bytes", value: 4096, unit: "bytes" as const },
];
const inputGraph = (artifact: string, source: string, side: number) => {
  const coverage = completeApplicationCoverage([limits[side]!]);
  const node = fixtureNode("javascript-module", {
    strategy: "canonical-path", stability: "artifact-version",
    artifact_sha256: artifact, path: "module.js",
  }, "module.js", {
    structural_fingerprint_sha256: "f".repeat(64),
    structural_fingerprint_algorithm: "fixture",
    source_sha256: source,
  }, { ...artifactEvidence(artifact, "module.js"), coverage });
  return createJavaScriptApplicationGraph({
    schema: "JavaScriptApplicationGraph", root_node_ids: [node.node_id],
    nodes: [node], edges: [], coverage, limitations: [],
  });
};

it("retains the union of complete input limits", () => {
  const result = compareJavaScriptApplicationVersions({
    left: { evidenceId: `ev_${"1".repeat(64)}`,
      rootArtifactSha256: "a".repeat(64),
      graph: inputGraph("a".repeat(64), "1".repeat(64), 0) },
    right: { evidenceId: `ev_${"2".repeat(64)}`,
      rootArtifactSha256: "b".repeat(64),
      graph: inputGraph("b".repeat(64), "2".repeat(64), 1) },
    leftNativeEvidence: [], rightNativeEvidence: [],
  });
  const edges = result.graph.edges.filter(edge => edge.relation === "changed_from");
  expect(edges).toHaveLength(1);
  expect(result.graph.coverage.limits).toEqual(limits);
  expect(edges[0]?.evidence.coverage.limits).toEqual(limits);
});

Expected behavior

The change graph and its inferred change edge carry both input limits while retaining complete coverage, zero omissions and no truncation. Declaring a configured limit does not imply that the input exceeded it.

Actual behavior

A local observation test of these distinct input limits returned limits: [] for both the change graph and its one inferred changed_from edge. Both coverage blocks were still complete. A control with left coverage changed to partial, truncated and omitted_count 1 retained both limits in both places. The observation test exited 0 because it asserted the current loss and the control; the fixture above states restoration assertions.

Evidence

  • Expected source: docs/javascript-application-workflows.md:149 says comparison coverage carries counts and limits from each input graph. src/domain/javascript/javascriptApplicationEvidenceSchemas.ts:226 preserves configured limits in complete coverage, and its schema accepts them.
  • Failure source: src/domain/javascript/javascriptApplicationChangeGraph.ts:183 assigns empty limits to complete change-edge Evidence, and line 213 does the same to complete change-graph coverage. The executed comparison fixture observed both empty arrays and verified a nonempty changed-edge set.
  • Evidence provenance: observed
  • Local verification: reproduced
  • Reproduction completeness: complete

One focused observation test passed in 399ms. Input graph construction and output schema validation succeeded. The partial control used the same graphs, bounds and unique module pairing. Verification exercised the production comparison producer; a separate CLI or MCP comparison was not run.

Restoration check

The unchanged complete-input fixture carries the deduplicated union of both limits on the change graph and its inferred change edge. The partial/truncated control continues preserving both limits, its omitted count and truncation flag. Input graph coverage and original observation Evidence remain intact.

Runtime or environment

Source baseline 8b181716aca2cb467be499cba226abfb5ae19141, Node v26.10.0, npm 11.19.1 and Vitest 5.0.3, with installed lockfile dependencies.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: The observed comparison silently removes configured analysis bounds from retained graph and change-edge provenance for complete inputs. It does not lose nodes or demonstrate that classification itself is wrong.
  • Workaround status: available
  • Workaround basis: The caller can inspect the complete input graphs, whose coverage limits remain available, and retain those bounds alongside the comparison. This preserves the provenance facts without rewriting or executing the analyzed application.
Originally created by @coygeek on GitHub (Oct 13, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/2522 ## Summary Comparing two complete JavaScript application graphs removes their declared coverage limits from the resulting change graph and from inferred change-edge Evidence. Complete input coverage can legitimately report configured bounds even when nothing was omitted. The same limits survive when an input is truncated, so the provenance loss depends on the complete-coverage branch. ## Steps to reproduce 1. Use the source checkout described under Runtime or environment with installed lockfile dependencies. 2. Save this focused fixture as `src/domain/javascript/completeComparisonLimits.test.ts`. 3. Run `npm run test:focused -- src/domain/javascript/completeComparisonLimits.test.ts`. ```ts import { expect, it } from "vitest"; import { artifactEvidence, fixtureNode } from "./javascriptApplicationGraph.fixture.js"; import { createJavaScriptApplicationGraph } from "./javascriptApplicationGraph.js"; import { completeApplicationCoverage } from "./javascriptApplicationEvidenceSchemas.js"; import { compareJavaScriptApplicationVersions } from "./javascriptApplicationVersionComparison.js"; const limits = [ { name: "modules", value: 100, unit: "items" as const }, { name: "source-bytes", value: 4096, unit: "bytes" as const }, ]; const inputGraph = (artifact: string, source: string, side: number) => { const coverage = completeApplicationCoverage([limits[side]!]); const node = fixtureNode("javascript-module", { strategy: "canonical-path", stability: "artifact-version", artifact_sha256: artifact, path: "module.js", }, "module.js", { structural_fingerprint_sha256: "f".repeat(64), structural_fingerprint_algorithm: "fixture", source_sha256: source, }, { ...artifactEvidence(artifact, "module.js"), coverage }); return createJavaScriptApplicationGraph({ schema: "JavaScriptApplicationGraph", root_node_ids: [node.node_id], nodes: [node], edges: [], coverage, limitations: [], }); }; it("retains the union of complete input limits", () => { const result = compareJavaScriptApplicationVersions({ left: { evidenceId: `ev_${"1".repeat(64)}`, rootArtifactSha256: "a".repeat(64), graph: inputGraph("a".repeat(64), "1".repeat(64), 0) }, right: { evidenceId: `ev_${"2".repeat(64)}`, rootArtifactSha256: "b".repeat(64), graph: inputGraph("b".repeat(64), "2".repeat(64), 1) }, leftNativeEvidence: [], rightNativeEvidence: [], }); const edges = result.graph.edges.filter(edge => edge.relation === "changed_from"); expect(edges).toHaveLength(1); expect(result.graph.coverage.limits).toEqual(limits); expect(edges[0]?.evidence.coverage.limits).toEqual(limits); }); ``` ## Expected behavior The change graph and its inferred change edge carry both input limits while retaining complete coverage, zero omissions and no truncation. Declaring a configured limit does not imply that the input exceeded it. ## Actual behavior A local observation test of these distinct input limits returned `limits: []` for both the change graph and its one inferred `changed_from` edge. Both coverage blocks were still complete. A control with left coverage changed to partial, truncated and omitted_count 1 retained both limits in both places. The observation test exited 0 because it asserted the current loss and the control; the fixture above states restoration assertions. ## Evidence - Expected source: `docs/javascript-application-workflows.md:149` says comparison coverage carries counts and limits from each input graph. `src/domain/javascript/javascriptApplicationEvidenceSchemas.ts:226` preserves configured limits in complete coverage, and its schema accepts them. - Failure source: `src/domain/javascript/javascriptApplicationChangeGraph.ts:183` assigns empty limits to complete change-edge Evidence, and line 213 does the same to complete change-graph coverage. The executed comparison fixture observed both empty arrays and verified a nonempty changed-edge set. - Evidence provenance: observed - Local verification: reproduced - Reproduction completeness: complete One focused observation test passed in 399ms. Input graph construction and output schema validation succeeded. The partial control used the same graphs, bounds and unique module pairing. Verification exercised the production comparison producer; a separate CLI or MCP comparison was not run. ## Restoration check The unchanged complete-input fixture carries the deduplicated union of both limits on the change graph and its inferred change edge. The partial/truncated control continues preserving both limits, its omitted count and truncation flag. Input graph coverage and original observation Evidence remain intact. ## Runtime or environment Source baseline `8b181716aca2cb467be499cba226abfb5ae19141`, Node v26.10.0, npm 11.19.1 and Vitest 5.0.3, with installed lockfile dependencies. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: The observed comparison silently removes configured analysis bounds from retained graph and change-edge provenance for complete inputs. It does not lose nodes or demonstrate that classification itself is wrong. - Workaround status: available - Workaround basis: The caller can inspect the complete input graphs, whose coverage limits remain available, and retain those bounds alongside the comparison. This preserves the provenance facts without rewriting or executing the analyzed application.
bsakaguchi 2026-10-15 18:28:54 +00:00
Author
Owner

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

Addressed on main in 896696b2e761e34c267977bb5c6982c607a622d0.

Complete version-change graph coverage retains the input limits and their Evidence provenance.

Validation: Affected suites passed in the merged 65-file run (1169 passed, 2 explicit skips), with full static/architecture checks and documentation checks passing. Final installed-package verification is recorded separately in the audit.

Verification scope: Controlled source and production-boundary verification on macOS with pinned Node 24.18.0; real external engines and Windows hosts were not independently rerun. Closure covers the described source defect, not broader provider conformance or performance.

<!-- gh-comment-id:6159194241 --> @morluto commented on GitHub (Oct 14, 2026): Addressed on main in [896696b2e761e34c267977bb5c6982c607a622d0](https://github.com/morluto/rea/commit/896696b2e761e34c267977bb5c6982c607a622d0). Complete version-change graph coverage retains the input limits and their Evidence provenance. Validation: Affected suites passed in the merged 65-file run (1169 passed, 2 explicit skips), with full static/architecture checks and documentation checks passing. Final installed-package verification is recorded separately in the audit. Verification scope: Controlled source and production-boundary verification on macOS with pinned Node 24.18.0; real external engines and Windows hosts were not independently rerun. Closure covers the described source defect, not broader provider conformance or performance.
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#839
No description provided.