Skip to content

feat(resolver): phase 8.3d — object property write tracking in points-to analysis#1295

Merged
carlos-alm merged 6 commits into
mainfrom
feat/phase-8-3d-property-write-pts
Jun 3, 2026
Merged

feat(resolver): phase 8.3d — object property write tracking in points-to analysis#1295
carlos-alm merged 6 commits into
mainfrom
feat/phase-8-3d-property-write-pts

Conversation

@carlos-alm
Copy link
Copy Markdown
Contributor

Summary

  • Seed typeMap['obj.prop'] from assignment_expression nodes (handlers.auth = authMiddleware) in both WASM extractor (extractTypeMapWalk) and native Rust extractor (match_js_type_map), with confidence 0.85
  • Extend resolveByMethodOrGlobal (TS) and resolve_call_targets (Rust) with a step-4.5 composite pts key lookup: when a call has receiver + name, check typeMap['receiver.name'] for a direct function target before falling through
  • Skips BUILTIN_GLOBALS / built-in objects (console, Math, etc.) and chained writes (a.b.c = x)

Before: router.use(handlers.auth) produced no edge to authMiddleware
After: edge setupRoutes → authMiddleware is emitted (dynamic=1)

Test plan

  • 5 new unit tests in tests/parsers/javascript.test.ts under Phase 8.3d: property write pts tracking
  • Integration test tests/integration/issue-1292-property-write-pts.test.ts verifies full pipeline with acceptance criteria from feat(resolver): phase 8.3d — object property write tracking in points-to analysis #1292
  • Rust compiles clean (cargo check — 0 errors, 6 pre-existing warnings)
  • 70 parser + integration tests pass on the feature branch
  • Parity test suite (101 tests) green — both engines now emit the property-write edges

Closes #1292

…-to analysis

Walk assignment_expression nodes in extractTypeMapWalk (WASM) and
match_js_type_map (native). When LHS is a simple member_expression
(obj.prop) and RHS is an identifier, seed typeMap['obj.prop'] = { type:
fn, confidence: 0.85 }. This covers patterns like:

  handlers.auth = authMiddleware;
  router.use(handlers.auth); // -> edge: caller -> authMiddleware

Extend resolveByMethodOrGlobal (TS) and resolve_call_targets (Rust) with
a composite key lookup (step 4.5): when a call has receiver + name, check
typeMap['receiver.name'] for a direct pts target before falling through to
the no-match return. Skips BUILTIN_GLOBALS / builtin objects (console,
Math, etc.) and chained writes (a.b.c = x).

Closes #1292
@claude
Copy link
Copy Markdown

claude Bot commented Jun 3, 2026

Claude finished @carlos-alm's task —— View job


PR Review: feat(resolver): phase 8.3d — object property write tracking in points-to analysis

Tasks

  • Gather context and read changed files
  • Phase 1: Root cause analysis
  • Phase 2: Configuration change detection
  • Phase 3: Test integrity review
  • Phase 4: Code quality review
  • Phase 5: Backlog compliance rating
  • Final assessment

Root Cause Analysis

Problem Being Solved: Before this PR, handlers.auth = authMiddleware; router.use(handlers.auth) produced no edge to authMiddleware. The points-to analysis (typeMap) was blind to property writes — only variable declarations and type annotations were tracked. Issue #1292.

Why This Approach: The fix is minimal and architecturally sound. Rather than a new pass, it hooks into the existing extractTypeMapWalk (and match_js_type_map in Rust) to seed composite keys (obj.prop) from assignment_expression nodes. The resolver then looks up those keys as a new step 4.5 before the no-match fallback. This approach reuses the entire existing confidence-based resolution infrastructure.

Risk Assessment: Low. The new behavior only fires when a call has a receiver + name that exactly matches a previously-seen property write. The 0.85 confidence level is appropriate (below explicit type annotations at 0.9, above factory heuristics at 0.7). The BUILTIN_GLOBALS guard and chained-write guard prevent the most obvious false positives.


Phase 2: Configuration Change Detection

✅ No biome/lint rules relaxed, no TypeScript strict flags disabled, no test timeouts increased, no coverage thresholds lowered. The two minor style changes (declarator?.type instead of !declarator || declarator.type) are clean improvements, not workarounds.


Phase 3: Test Integrity

✅ No assertions removed or weakened. Five focused unit tests cover the key edge cases (simple write, chained write, non-identifier RHS, builtin guard, idempotency). Integration test builds a real graph and asserts on actual DB edges — solid end-to-end coverage.


Phase 4: Code Quality

Bug: BUILTIN_GLOBALS parity gap between engines

The Rust is_js_builtin_global function (crates/codegraph-core/src/extractors/javascript.rs:133–141) includes "process" | "window" | "document" | "globalThis" but the TypeScript BUILTIN_GLOBALS set (src/extractors/javascript.ts:27–81) does not contain any of these four names.

This is an engine parity bug: window.onload = myFn would be silently tracked in the WASM engine but correctly skipped in the native engine.

Per CLAUDE.md policy: "If two engines produce different results, that is a bug in the less-accurate engine". The TS engine is less restrictive here (a false positive) and needs the four names added to BUILTIN_GLOBALS. This should be a one-line fix but is a correctness issue.

Minor: No kind filter on pts resolution targets

call-resolver.ts:87 (resolveByMethodOrGlobal) calls lookup.byName(ptsTarget) with no kind filter, while the method-resolution in the same function (line 74) correctly filters n.kind === 'method'. The Rust path (edge_builder.rs:264–268) also does not filter by kind. For property-write pts targets this is probably intentional — the write RHS can be a plain function, not a method — but it means a class, constant, or interface name accidentally matching a function name would be resolved. Low real-world risk given the 0.85 confidence threshold but worth noting.

Observation: computeConfidence threshold not documented

The >= 0.5 threshold used in both paths (TS: call-resolver.ts:88, Rust: edge_builder.rs:267) is the same value used in the global fallback at line 97. This is consistent, but there's no comment explaining why 0.5 is the right cutoff for a pts resolution that is itself already guarded by a 0.85 confidence write. Not a bug, just a documentation gap.

Positive: Step 4.5 ordering is correct

The composite key lookup is correctly placed after the typed method lookup (step 4) and before the global fallback (step 5). If a typed method exists, it wins. Only if no typed method is found does the pts target apply — this avoids false positives in cases where the type map already resolves the receiver to a class.


Phase 5: Backlog Compliance

  • Zero-dep: ✓ — No new runtime dependencies. Builds entirely on the existing typeMap/AST infrastructure.
  • Foundation-aligned: ✓ — Purely local, offline, no agent behavior, no cloud calls. Embeds cleanly in the existing extraction pipeline.
  • Problem-fit: 5 — This directly improves edge accuracy for callback-heavy JS patterns (Express middleware, event-based routing), which is exactly the "hallucinated dependencies / missed blast radius" problem. Higher-quality edges = less token waste when AI agents query the graph.
  • Breaking: No — Purely additive. Existing edges are not removed; new edges are added where the graph was previously silent. No CLI, API, or DB schema changes.
  • Tier: 1 (zero-dep ✓ + foundation-aligned ✓)

Critical Concerns

Engine parity bug — BUILTIN_GLOBALS divergence (must fix before merge):

// src/extractors/javascript.ts  BUILTIN_GLOBALS
+  'process',
+  'window',
+  'document',
+  'globalThis',

The Rust engine blocks writes on window, document, process, globalThis. The WASM engine doesn't. Example: window.onload = myHandler → native: no entry added; WASM: adds window.onload → myHandler to typeMap. This violates the dual-engine parity guarantee and would produce different graph edges depending on which engine is active.


Final Recommendation

  • Rating: ⭐⭐⭐⭐ (4/5)
  • Action: REQUEST CHANGES
  • Reasoning: The implementation is correct, well-structured, and meaningfully improves edge recall for real-world JS patterns. Tests are honest and cover the important cases. The only blocking issue is the BUILTIN_GLOBALS parity gap between the two engines — four names (process, window, document, globalThis) present in the Rust guard but absent from the TypeScript guard. Per project policy, engine parity gaps are bugs, not acceptable differences. This is a one-line fix in src/extractors/javascript.ts. Once that's added (and ideally a test added for process.on = fn being skipped in the WASM path), this is ready to merge.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Jun 3, 2026

Greptile Summary

This PR adds phase 8.3d of the points-to analysis: object property write tracking. It seeds the type map with obj.prop → fn entries from assignment_expression nodes, then adds a step 4.5 composite key lookup in both the TypeScript and Rust call resolvers so that patterns like router.use(handlers.auth) can trace through to the concrete function (authMiddleware).

  • Extractor changes (TS + Rust): handlePropWriteTypeMap / match_js_type_map walk assignment_expression nodes, skip BUILTIN_GLOBALS and chained writes, and seed typeMap['obj.prop'] at confidence 0.85. Four missing globals (process, window, document, globalThis) are also added to the TS BUILTIN_GLOBALS set for parity with Rust.
  • Resolver changes (TS + Rust): resolveByMethodOrGlobal / resolve_call_targets gain a step 4.5 that checks typeMap['receiver.name'] directly for a function target after the existing type-aware method lookup (step 4) falls through.

Confidence Score: 5/5

Safe to merge — the new resolution step is narrowly scoped to property-write pts keys, falls through cleanly when no match is found, and does not alter existing steps 1–4.

The composite key lookup is inserted correctly inside the receiver guard in both engines, BUILTIN_GLOBALS filtering prevents noise entries from being seeded, and higher-confidence-wins deduplication is consistent between TypeScript and Rust. Unit and integration tests directly exercise the before/after behaviour.

No files require special attention.

Important Files Changed

Filename Overview
src/extractors/javascript.ts Adds handlePropWriteTypeMap with correct BUILTIN_GLOBALS check, property-kind guard, and chained-write exclusion; also adds four previously missing globals to BUILTIN_GLOBALS for Rust parity.
src/domain/graph/builder/call-resolver.ts Adds step 4.5 composite pts key lookup inside the if (call.receiver) guard — correctly placed after the existing type-aware step 4, uses the same confidence filter and entry-extraction pattern as adjacent steps.
crates/codegraph-core/src/extractors/javascript.rs New "assignment_expression" arm seeds symbols.type_map with the composite key at 0.85; is_js_builtin_global mirrors the TS BUILTIN_GLOBALS set.
crates/codegraph-core/src/edge_builder.rs Step 4.5 composite key lookup correctly placed inside the if let Some(ref receiver) block; follows the same confidence filter pattern as the adjacent scoped fallback.
tests/integration/issue-1292-property-write-pts.test.ts New integration test that builds a full graph from a fixture file and verifies that both handlers.auth and handlers.log resolve to their assigned functions with dynamic=1.
tests/parsers/javascript.test.ts Five new unit tests covering composite key seeding, chained-write exclusion, non-identifier RHS exclusion, BUILTIN_GLOBALS exclusion, first-write-wins at equal confidence, and explicit higher-confidence promotion.

Reviews (5): Last reviewed commit: "fix(extractor): sync is_js_builtin_globa..." | Re-trigger Greptile

Comment thread tests/parsers/javascript.test.ts Outdated
Comment on lines +300 to +310
it('higher-confidence entry wins when same key appears twice', () => {
// First write seeds at 0.85; a later type annotation on handlers.auth should win
const parser = parsers.get('typescript');
const tree = parser.parse(`
handlers.auth = firstMiddleware;
handlers.auth = secondMiddleware;
`);
const symbols = extractSymbols(tree, 'test.ts');
// Second write at same confidence (0.85) does not overwrite the first
expect(symbols.typeMap.get('handlers.auth')?.type).toBe('firstMiddleware');
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Misleading test name and stale inner comment

The test is titled "higher-confidence entry wins when same key appears twice," but it exercises the equal-confidence case: two 0.85 writes, first-write wins. The higher-confidence path (a 0.9 annotation overwriting the first 0.85 write) is never asserted. This means the invariant that setTypeMapEntry promotes on strictly higher confidence is untested by this suite.

The inner comment ("a later type annotation on handlers.auth should win") describes a scenario that is completely absent from the test body — there is no type annotation, and the assertion expects firstMiddleware (i.e., the first write wins, not the annotation).

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Claude Code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — renamed the test to 'first-write wins when same key appears twice at equal confidence', removed the stale inner comment, and added a new explicit test 'higher-confidence entry promotes over lower-confidence entry' that directly calls setTypeMapEntry to assert the 0.85 → 0.9 promotion path. The BUILTIN_GLOBALS test was also extended to cover process/window/document/globalThis.

Comment on lines +1513 to +1521
const obj = lhsN.childForFieldName('object');
const prop = lhsN.childForFieldName('property');
if (!obj || !prop) return;
if (obj.type !== 'identifier') return; // skip chained: a.b.c = x

const objName = obj.text;
if (BUILTIN_GLOBALS.has(objName)) return;

setTypeMapEntry(typeMap, `${objName}.${prop.text}`, rhsN.text, 0.85);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 No property-node type guard, inconsistent with adjacent fnRefBindings handling

The existing fnRefBindings block (lines 1419–1422) explicitly checks prop.type === 'property_identifier' || prop.type === 'identifier' before using prop.text, with a comment explaining that computed subscript expressions can produce unexpected text. handlePropWriteTypeMap skips this guard and calls prop.text directly. While tree-sitter's member_expression should always yield a property_identifier for the property field (bracket-notation produces subscript_expression and is therefore excluded by the earlier lhsN.type !== 'member_expression' check), the missing guard is a defensive inconsistency that could produce malformed composite keys if the grammar ever diverges.

Suggested change
const obj = lhsN.childForFieldName('object');
const prop = lhsN.childForFieldName('property');
if (!obj || !prop) return;
if (obj.type !== 'identifier') return; // skip chained: a.b.c = x
const objName = obj.text;
if (BUILTIN_GLOBALS.has(objName)) return;
setTypeMapEntry(typeMap, `${objName}.${prop.text}`, rhsN.text, 0.85);
const obj = lhsN.childForFieldName('object');
const prop = lhsN.childForFieldName('property');
if (!obj || !prop) return;
if (obj.type !== 'identifier') return; // skip chained: a.b.c = x
if (prop.type !== 'property_identifier' && prop.type !== 'identifier') return;
const objName = obj.text;
if (BUILTIN_GLOBALS.has(objName)) return;
setTypeMapEntry(typeMap, `${objName}.${prop.text}`, rhsN.text, 0.85);

Fix in Claude Code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — added the prop.type !== 'property_identifier' && prop.type !== 'identifier' guard in handlePropWriteTypeMap, consistent with the adjacent fnRefBindings block. Added an explanatory comment matching the pattern used there.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 3, 2026

Codegraph Impact Analysis

9 functions changed23 callers affected across 7 files

  • resolve_call_targets in crates/codegraph-core/src/edge_builder.rs:222 (6 transitive callers)
  • match_js_type_map in crates/codegraph-core/src/extractors/javascript.rs:58 (0 transitive callers)
  • is_js_builtin_global in crates/codegraph-core/src/extractors/javascript.rs:134 (1 transitive callers)
  • resolveByMethodOrGlobal in src/domain/graph/builder/call-resolver.ts:62 (8 transitive callers)
  • extractDestructuredBindingsWalk in src/extractors/javascript.ts:424 (2 transitive callers)
  • extractConstDeclarators in src/extractors/javascript.ts:463 (3 transitive callers)
  • extractTypeMapWalk in src/extractors/javascript.ts:1381 (3 transitive callers)
  • walk in src/extractors/javascript.ts:1388 (8 transitive callers)
  • handlePropWriteTypeMap in src/extractors/javascript.ts:1519 (8 transitive callers)

…BALS (#1295)

Adds the four names present in Rust's is_js_builtin_global but absent
from the TypeScript BUILTIN_GLOBALS set, restoring dual-engine parity
for property-write pts tracking. Also adds prop.type guard in
handlePropWriteTypeMap consistent with the adjacent fnRefBindings block.
…romotion test (#1295)

Renames 'higher-confidence entry wins' to accurately describe equal-confidence
first-write behavior. Removes stale inner comment. Adds explicit test for
strict-higher-confidence promotion via setTypeMapEntry. Extends BUILTIN_GLOBALS
test to cover process/window/document/globalThis.
@carlos-alm
Copy link
Copy Markdown
Contributor Author

Addressed the BUILTIN_GLOBALS engine-parity bug: added process, window, document, globalThis to the TypeScript BUILTIN_GLOBALS set to match Rust's is_js_builtin_global. Tests extended to assert all four new names are skipped. Also applied Greptile's suggestions: added prop.type guard in handlePropWriteTypeMap and corrected the misleading test name + stale comment (plus added the missing higher-confidence promotion test).

@carlos-alm
Copy link
Copy Markdown
Contributor Author

@greptileai

@carlos-alm
Copy link
Copy Markdown
Contributor Author

@claude

@claude
Copy link
Copy Markdown

claude Bot commented Jun 3, 2026

Claude finished @carlos-alm's task —— View job


Re-review: feat(resolver): phase 8.3d — object property write tracking in points-to analysis

Tasks

  • Gather context
  • Verify BUILTIN_GLOBALS parity fix (process, window, document, globalThis)
  • Verify prop.type guard in handlePropWriteTypeMap
  • Verify test fixes (renamed test + stale comment removed + higher-confidence promotion test)
  • Check remaining parity gap in is_js_builtin_global
  • Final assessment

Verified Fixes

All three requested changes are correctly applied:

1. BUILTIN_GLOBALS parity (src/extractors/javascript.ts:81–84)
process, window, document, globalThis are now in the TS set, matching the Rust guard.

2. prop.type guard (src/extractors/javascript.ts:1521–1523)

// Guard: only static property access (property_identifier or identifier), not
// computed subscript expressions — consistent with the adjacent fnRefBindings block.
if (prop.type !== 'property_identifier' && prop.type !== 'identifier') return;

Exact pattern from the Greptile suggestion, with appropriate comment.

3. Test corrections (tests/parsers/javascript.test.ts)

  • Test renamed to 'first-write wins when same key appears twice at equal confidence'
  • Stale inner comment removed; replaced with: // Both writes are at 0.85; first-write wins (equal confidence does not promote)
  • New explicit test 'higher-confidence entry promotes over lower-confidence entry (setTypeMapEntry)' directly calls setTypeMapEntry(typeMap, 'handlers.auth', 'firstMiddleware', 0.85) then setTypeMapEntry(typeMap, 'handlers.auth', 'AnnotatedHandler', 0.9) and asserts the 0.9-confidence entry wins
  • BUILTIN_GLOBALS test extended to cover process.on, window.onload, document.ready, globalThis.fetch

Remaining Finding: is_js_builtin_global is still a subset of TS BUILTIN_GLOBALS

The Rust is_js_builtin_global (crates/codegraph-core/src/extractors/javascript.rs:133–141) was introduced in this PR with 20 names. The TS BUILTIN_GLOBALS set has ~40 names. The 4 blocking names (process, window, document, globalThis) are now correctly present in both engines. However, ~20 names present in TS are absent from Rust:

ArrayBuffer, SharedArrayBuffer, DataView, Atomics, BigInt, Float32Array, Float64Array, Int8Array, Int16Array, Int32Array, Uint8Array, Uint16Array, Uint32Array, Uint8ClampedArray, URL, URLSearchParams, TextEncoder, TextDecoder, AbortController, AbortSignal, Headers, Request, Response, FormData, Blob, File, ReadableStream, WritableStream, TransformStream, Buffer, EventEmitter, Stream

This means ArrayBuffer.isView = myFn would be blocked by WASM but tracked by native. The risk is low (nobody writes to ArrayBuffer or Float32Array properties in real code), but the parity gap exists per the project's dual-engine guarantee.

Assessment: Since is_js_builtin_global was introduced in this PR, this is in scope. I recommend either (a) syncing Rust to the full TS list before merge, or (b) opening a follow-up issue. Given CLAUDE.md policy ("engine parity gaps are bugs, not acceptable differences"), I'd lean toward (a) since the fix is mechanical.


Final Recommendation

  • Rating: ⭐⭐⭐⭐½ (4.5/5)
  • Action: The three explicitly-requested fixes are all correctly implemented. The only remaining finding is the incomplete sync of is_js_builtin_global to the full TS list — a continuation of the original parity concern that was the reason for the previous REQUEST CHANGES, now affecting the opposite direction for the TypedArray/Web API names. This is either a quick Rust-side sync or a follow-up issue.

CI runs (CI, Build Native, Commitlint) are still in progress at time of this review.

View job · Branch

@carlos-alm
Copy link
Copy Markdown
Contributor Author

Addressed remaining reviewer findings:

  • Step 4.5 fall-through fix (call-resolver.ts): Changed unconditional return to check resolved.length > 0 before returning, matching the Rust counterpart's if !resolved.is_empty() { return resolved; } pattern. Future resolution steps inserted after step 4.5 will now fire correctly when pts lookup yields no results.

  • is_js_builtin_global parity (crates/codegraph-core/src/extractors/javascript.rs): Synced the Rust builtin list to match the full TypeScript BUILTIN_GLOBALS set (43 entries). Added the missing ~20 entries: TypedArrays (Float32Array, Int8Array, Uint8Array, etc.), Web platform globals (URL, URLSearchParams, Headers, Request, Response, FormData, Blob, File, ReadableStream, WritableStream, TransformStream, AbortController, AbortSignal, TextEncoder, TextDecoder, ArrayBuffer, SharedArrayBuffer, DataView, Atomics, BigInt), and Node.js built-ins (Buffer, EventEmitter, Stream).

  • Merge conflicts with main resolved: Phase 8.3c's extractParamBindingsWalk (from feat(resolver): parameter-flow tracking in points-to analysis (Phase 8.3c) #1294) and Phase 8.3d's handlePropWriteTypeMap both preserved in src/extractors/javascript.ts.

@carlos-alm
Copy link
Copy Markdown
Contributor Author

@greptileai

@carlos-alm
Copy link
Copy Markdown
Contributor Author

@claude

@claude
Copy link
Copy Markdown

claude Bot commented Jun 3, 2026

Claude finished @carlos-alm's task —— View job


Re-review: feat(resolver): phase 8.3d — object property write tracking in points-to analysis

Tasks

  • Gather context
  • Verify step 4.5 fall-through fix (call-resolver.ts)
  • Verify is_js_builtin_global full parity (Rust ↔ TypeScript)
  • Check test coverage for new changes
  • Final assessment

Verified Fixes (Round 3)

All remaining findings from the previous review are correctly addressed:

1. Step 4.5 fall-through fix (src/domain/graph/builder/call-resolver.ts:87–92)

if (ptsTarget) {
  const resolved = lookup
    .byName(ptsTarget)
    .filter((t) => computeConfidence(relPath, t.file, null) >= 0.5);
  if (resolved.length > 0) return resolved;  // ← non-empty guard added
}

The unconditional return is gone. Now matches the Rust pattern exactly (if !resolved.is_empty() { return resolved; }). Future steps inserted between 4.5 and 5 will fire correctly when pts lookup yields no results.

2. is_js_builtin_global full parity (crates/codegraph-core/src/extractors/javascript.rs:134–156)

The Rust guard now contains all 43 entries from the TypeScript BUILTIN_GLOBALS set:

  • Core JS globals: Math, JSON, Promise, Array, Object, Date, Error, Symbol, Map, Set, RegExp, Number, String, Boolean, WeakMap, WeakSet, WeakRef, Proxy, Reflect, Intl
  • Binary/typed data: ArrayBuffer, SharedArrayBuffer, DataView, Atomics, BigInt, Float32Array, Float64Array, Int8Array, Int16Array, Int32Array, Uint8Array, Uint16Array, Uint32Array, Uint8ClampedArray
  • Web platform: URL, URLSearchParams, TextEncoder, TextDecoder, AbortController, AbortSignal, Headers, Request, Response, FormData, Blob, File, ReadableStream, WritableStream, TransformStream
  • Browser/runtime: console, process, window, document, globalThis
  • Node.js: Buffer, EventEmitter, Stream

Cross-checking both sets: identical. Parity guarantee restored.

3. prop.type guard (src/extractors/javascript.ts:1530–1532)

Correctly added, with explanatory comment matching the adjacent fnRefBindings pattern.

4. Test fixes (tests/parsers/javascript.test.ts:309–327)

  • 'first-write wins when same key appears twice at equal confidence' — correctly tests equal-confidence case
  • 'higher-confidence entry promotes over lower-confidence entry (setTypeMapEntry)' — explicitly tests the 0.85→0.9 promotion path by calling setTypeMapEntry directly
  • BUILTIN_GLOBALS test covers process.on, window.onload, document.ready, globalThis.fetch

No Remaining Findings

All three rounds of review findings are fully addressed:

  • Round 1: BUILTIN_GLOBALS parity (process, window, document, globalThis) ✅
  • Round 2: Step 4.5 fall-through + is_js_builtin_global full sync (all 43 entries) ✅
  • Round 3: Verified — no new issues

Final Recommendation

  • Rating: ⭐⭐⭐⭐⭐ (5/5)
  • Action: APPROVE
  • Reasoning: The implementation is correct, architecturally sound, and both engines now produce identical results for all tracked cases. The step 4.5 fall-through is consistent with the Rust reference implementation. is_js_builtin_global is fully in sync with BUILTIN_GLOBALS. Tests are honest and cover the important edge cases including the equal-confidence first-write-wins path and the higher-confidence promotion path. Ready to merge.
    · Branch

@carlos-alm carlos-alm merged commit 4dd4331 into main Jun 3, 2026
28 checks passed
@carlos-alm carlos-alm deleted the feat/phase-8-3d-property-write-pts branch June 3, 2026 21:51
@github-actions github-actions Bot locked and limited conversation to collaborators Jun 3, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(resolver): phase 8.3d — object property write tracking in points-to analysis

1 participant