[GH-ISSUE #2569] fix(javascript-semantic-fingerprints): fingerprints collide across yield and increment forms #888

Closed
opened 2026-10-15 18:29:18 +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/2569

Summary

JavaScript analysis produces identical complete function fingerprint components when a function changes from yielding an iterator result to delegating to that iterator, or from returning a postfix increment to returning a prefix increment. Both changes affect the function's result, but the normalized syntax digest loses the distinction. Function fingerprint seeds consequently cannot distinguish these inputs using the recorded components.

Steps to reproduce

Use the source checkout and dependencies described under Runtime or environment. Add the following test as src/domain/javascript/semanticFingerprintRegression.test.ts and run npm run test:focused -- src/domain/javascript/semanticFingerprintRegression.test.ts. Remove the added test after collecting the result.

import { expect, it } from "vitest";
import { analyzeJavaScriptSemantics } from "./javascriptSemanticAnalysis.js";

const components = (source: string) => {
  const ir = analyzeJavaScriptSemantics(source);
  expect(ir.functionFingerprints).toHaveLength(1);
  expect(ir.functionFingerprints[0]!.status).toBe("complete");
  return ir.functionFingerprints[0]!.components;
};

it("distinguishes yield delegation", () => {
  const plain = components("function* sample(gen) { yield gen(); }");
  const delegated = components("function* sample(gen) { yield* gen(); }");
  expect(delegated.normalizedAstSha256).not.toEqual(plain.normalizedAstSha256);
});

it("distinguishes a returned prefix update from a postfix update", () => {
  const postfix = components("function sample(i) { return i++; }");
  const prefix = components("function sample(i) { return ++i; }");
  expect(prefix.normalizedAstSha256).not.toEqual(postfix.normalizedAstSha256);
});

it("preserves formatting and local rename normalization", () => {
  const original = components("function sample(i) { return i++; }");
  const formatted = components("function renamed(value) {\n return value++ ;\n }");
  expect(formatted).toEqual(original);
});

Expected behavior

Function fingerprints commit normalized syntax while ignoring local names and source offsets. Changing yield gen() to yield* gen() or return i++ to return ++i must retain that syntax distinction in the normalized AST digest and aggregate function fingerprint. Changing formatting or local identifiers alone should preserve the fingerprint. Fingerprints remain static candidates and do not prove behavioral equivalence.

Actual behavior

The two digest distinction assertions failed in an independent focused run, and the formatting/rename control passed. Each compared pair produced identical component objects, with complete status. The yield pair shared normalized AST digest 2bfda625ec1f562e9b8c0167852d9f2bb5086e216d9bfeb099b083ec8f8d05a8; the update pair shared ec0ce5a7640ddf1477aa86c47b2349011e04b13baf60891a288e3660a47a9c95. Control-flow, relation-shape, literal-set, arity, and effect components also matched within each pair.

A separate JavaScript behavior control passed in that run. With an iterator factory returning [1, 2], ordinary yield produced [[1, 2]] while delegating yield produced [1, 2]. For numeric input 1, the postfix function returned 1 while the prefix function returned 2. Four verifier tests ran in total, with two failing distinction checks and two passing controls; the focused command exited with status 1.

Evidence

  • Expected source: docs/javascript-application-workflows.md:79 documents the normalized syntax commitment, normalization exclusions, and ambiguous duplicate handling.
  • Failure source: src/domain/javascript/javascriptSemanticFingerprints.ts:193-201 omits the AST delegation and prefix attributes from normalized tokens; the focused analyzer reproduction above observed equal complete component objects for both result-changing pairs.
  • Evidence provenance: observed
  • Local verification: reproduced
  • Reproduction completeness: complete

The production JavaScript artifact path admits included, parseable JavaScript and invokes this semantic analyzer at src/application/javascript/JavaScriptArtifactAnalysis.ts:362. Component projection and graph construction retain the equality when deriving the aggregate fingerprint. Function seed resolution in src/domain/javascript/javascriptSemanticQueryIdentity.ts:56 selects every node with the matching component-derived fingerprint, so these distinctions are unavailable to that seed. The query consequence is established from source; a public CLI/MCP seed query was not executed. Equal candidates remain ambiguous rather than proving equivalent behavior.

Application version comparison uses a separate module structural fingerprint. This reproduction does not establish a failure in that comparison workflow.

Restoration check

Both result-changing pairs produce different normalized syntax digests and aggregate semantic function fingerprints. The same syntax with only formatting and local-identifier changes still produces equal components. Fingerprint queries distinguish the changed syntax while genuinely equal duplicate fingerprints continue to return all matching candidates with ambiguity. The prefix/postfix pair need not differ in every component, including its return-statement control-flow shape.

Runtime or environment

  • The failure was verified at source baseline: a2d8b94dd5 and is still present at 8b181716ac
  • Node.js: 26.10.0
  • npm: 11.19.1
  • Vitest: 5.0.3, with the existing locked dependencies.
  • Verification used one focused source test and did not build or run the complete suite.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: Complete semantic fingerprints lose distinctions that change returned or yielded values for bounded valid JavaScript inputs. This reduces their usefulness for function identification and can add fingerprint seed candidates. No wrong unique match or application version comparison failure was observed.
  • Workaround status: unverified
  • Workaround basis: The documented semantic-node seed and its exact node-ID matching branch provide an alternative for targeting a known function, but that query workflow was not exercised in this reproduction and does not repair the fingerprint itself.
Originally created by @coygeek on GitHub (Oct 13, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/2569 ## Summary JavaScript analysis produces identical complete function fingerprint components when a function changes from yielding an iterator result to delegating to that iterator, or from returning a postfix increment to returning a prefix increment. Both changes affect the function's result, but the normalized syntax digest loses the distinction. Function fingerprint seeds consequently cannot distinguish these inputs using the recorded components. ## Steps to reproduce Use the source checkout and dependencies described under Runtime or environment. Add the following test as `src/domain/javascript/semanticFingerprintRegression.test.ts` and run `npm run test:focused -- src/domain/javascript/semanticFingerprintRegression.test.ts`. Remove the added test after collecting the result. ```ts import { expect, it } from "vitest"; import { analyzeJavaScriptSemantics } from "./javascriptSemanticAnalysis.js"; const components = (source: string) => { const ir = analyzeJavaScriptSemantics(source); expect(ir.functionFingerprints).toHaveLength(1); expect(ir.functionFingerprints[0]!.status).toBe("complete"); return ir.functionFingerprints[0]!.components; }; it("distinguishes yield delegation", () => { const plain = components("function* sample(gen) { yield gen(); }"); const delegated = components("function* sample(gen) { yield* gen(); }"); expect(delegated.normalizedAstSha256).not.toEqual(plain.normalizedAstSha256); }); it("distinguishes a returned prefix update from a postfix update", () => { const postfix = components("function sample(i) { return i++; }"); const prefix = components("function sample(i) { return ++i; }"); expect(prefix.normalizedAstSha256).not.toEqual(postfix.normalizedAstSha256); }); it("preserves formatting and local rename normalization", () => { const original = components("function sample(i) { return i++; }"); const formatted = components("function renamed(value) {\n return value++ ;\n }"); expect(formatted).toEqual(original); }); ``` ## Expected behavior Function fingerprints commit normalized syntax while ignoring local names and source offsets. Changing `yield gen()` to `yield* gen()` or `return i++` to `return ++i` must retain that syntax distinction in the normalized AST digest and aggregate function fingerprint. Changing formatting or local identifiers alone should preserve the fingerprint. Fingerprints remain static candidates and do not prove behavioral equivalence. ## Actual behavior The two digest distinction assertions failed in an independent focused run, and the formatting/rename control passed. Each compared pair produced identical component objects, with complete status. The yield pair shared normalized AST digest `2bfda625ec1f562e9b8c0167852d9f2bb5086e216d9bfeb099b083ec8f8d05a8`; the update pair shared `ec0ce5a7640ddf1477aa86c47b2349011e04b13baf60891a288e3660a47a9c95`. Control-flow, relation-shape, literal-set, arity, and effect components also matched within each pair. A separate JavaScript behavior control passed in that run. With an iterator factory returning `[1, 2]`, ordinary yield produced `[[1, 2]]` while delegating yield produced `[1, 2]`. For numeric input `1`, the postfix function returned `1` while the prefix function returned `2`. Four verifier tests ran in total, with two failing distinction checks and two passing controls; the focused command exited with status 1. ## Evidence - Expected source: `docs/javascript-application-workflows.md:79` documents the normalized syntax commitment, normalization exclusions, and ambiguous duplicate handling. - Failure source: `src/domain/javascript/javascriptSemanticFingerprints.ts:193-201` omits the AST delegation and prefix attributes from normalized tokens; the focused analyzer reproduction above observed equal complete component objects for both result-changing pairs. - Evidence provenance: observed - Local verification: reproduced - Reproduction completeness: complete The production JavaScript artifact path admits included, parseable JavaScript and invokes this semantic analyzer at `src/application/javascript/JavaScriptArtifactAnalysis.ts:362`. Component projection and graph construction retain the equality when deriving the aggregate fingerprint. Function seed resolution in `src/domain/javascript/javascriptSemanticQueryIdentity.ts:56` selects every node with the matching component-derived fingerprint, so these distinctions are unavailable to that seed. The query consequence is established from source; a public CLI/MCP seed query was not executed. Equal candidates remain ambiguous rather than proving equivalent behavior. Application version comparison uses a separate module structural fingerprint. This reproduction does not establish a failure in that comparison workflow. ## Restoration check Both result-changing pairs produce different normalized syntax digests and aggregate semantic function fingerprints. The same syntax with only formatting and local-identifier changes still produces equal components. Fingerprint queries distinguish the changed syntax while genuinely equal duplicate fingerprints continue to return all matching candidates with ambiguity. The prefix/postfix pair need not differ in every component, including its return-statement control-flow shape. ## Runtime or environment - The failure was verified at source baseline: a2d8b94dd50dab8d966239dd63ab84718aa8b4b4 and is still present at 8b181716aca2cb467be499cba226abfb5ae19141 - Node.js: 26.10.0 - npm: 11.19.1 - Vitest: 5.0.3, with the existing locked dependencies. - Verification used one focused source test and did not build or run the complete suite. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: Complete semantic fingerprints lose distinctions that change returned or yielded values for bounded valid JavaScript inputs. This reduces their usefulness for function identification and can add fingerprint seed candidates. No wrong unique match or application version comparison failure was observed. - Workaround status: unverified - Workaround basis: The documented semantic-node seed and its exact node-ID matching branch provide an alternative for targeting a known function, but that query workflow was not exercised in this reproduction and does not repair the fingerprint itself.
bsakaguchi 2026-10-15 18:29:18 +00:00
Author
Owner

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

Addressed on main in 896696b2e761e34c267977bb5c6982c607a622d0.

Semantic fingerprints distinguish delegated yield and prefix/postfix update forms.

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:6159197691 --> @morluto commented on GitHub (Oct 14, 2026): Addressed on main in [896696b2e761e34c267977bb5c6982c607a622d0](https://github.com/morluto/rea/commit/896696b2e761e34c267977bb5c6982c607a622d0). Semantic fingerprints distinguish delegated yield and prefix/postfix update forms. 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#888
No description provided.