Skip to content

fix(extract): canonicalize source_file to POSIX on every node and edge - #2627

Closed
rajarshidattapy wants to merge 2 commits into
Graphify-Labs:v8from
rajarshidattapy:fix/extract-source-file-posix
Closed

fix(extract): canonicalize source_file to POSIX on every node and edge#2627
rajarshidattapy wants to merge 2 commits into
Graphify-Labs:v8from
rajarshidattapy:fix/extract-source-file-posix

Conversation

@rajarshidattapy

Copy link
Copy Markdown
Contributor

Fixes #2625.

Problem

source_file is built from whatever Path an extractor was handed, and only the
relativizing branch of extract()'s remap (_sf_entry) ever calls as_posix(). A run given
relative inputs therefore keeps the native separator on Windows — and because different
extractors reach that branch differently, a single extraction emits two conventions at once:

$ python -m graphify.extract src/lib/content.ts src/pages/index.astro

"source_file": "src\\lib\\content.ts"      <- native
"source_file": "src/pages/index.astro"     <- posix

source_file is compared as a string downstream — build._norm_source_file keying,
_derive_prune_root, dedup, the semantic-cache key path, and analyze.find_import_cycles,
which matches an edge's source_file against a node's by equality. Two spellings are two
different files: the fragmentation of #683, resurfaced on a different code path.

The primary graphify extract CLI passes an explicit root and takes the already-correct
branch, which is why this stayed invisible. It is reachable from the documented
python -m graphify.extract <file> … entry point and from any library caller.

Fix

One pass at the end of extract(), where all nodes and edges are already being walked:

for _item in (*all_nodes, *all_edges):
    _sf = _item.get("source_file")
    if _sf and "\\" in str(_sf):
        _item["source_file"] = PurePath(_sf).as_posix()

Three deliberate choices, each verified rather than assumed:

  • Edges, not just nodes. A single extraction emits source_file with mixed path separators (src\lib\content.ts and src/pages/index.astro in the same output) on Windows #2625 suggests a node-only loop; edges carry source_file too.
    Without the fix the new test reports 5 node + 3 edge violations, and
    find_import_cycles compares an edge's source_file to a node's by equality — so a
    node-only fix would have left cycle detection broken on exactly this input. (target_file
    is popped earlier and never reaches the output, so it needs nothing.)
  • Running last is safe. IDs are minted through make_id, which collapses every non-word
    character — \ and / alike — to _. Confirmed:
    make_id("src\lib\content.ts") == make_id("src/lib/content.ts") == "src_lib_content_ts".
    Canonicalizing the separator after IDs exist cannot desync an id from its source_file.
  • PurePath is the native flavour on purpose. On POSIX a backslash is a legal filename
    character and must be left alone, so this only rewrites paths on the platform where \ is
    actually a separator.

Warm cache entries are healed, no version bump needed

The pass runs after the AST cache merge, so an entry written by a pre-fix version is
corrected on read:

cold: ['src/lib/content.ts', 'src/pages/index.astro']   native-separator entries: 0
warm: ['src/lib/content.ts', 'src/pages/index.astro']   native-separator entries: 0

No cache invalidation and no re-extraction cost for existing users.

Changes

  • graphify/extract.py — the normalization pass.

  • tests/test_extract.py — new test_extract_emits_posix_source_file_for_relative_inputs,
    covering nodes and edges. Uses relative inputs deliberately: passing an explicit root
    takes the branch that already normalized and would make the test vacuous.

  • tests/test_js_import_resolution.py
    test_alias_import_does_not_remap_an_owned_symbol_id looked source_file up with
    str(Path(...)), the native spelling. It passed on Windows only because the product emitted
    backslashes too; with source_file now canonical, the four lookups need as_posix(). This
    is the mirror image of Tests hardcode / in path assertions, so they fail on Windows — and some pass vacuously, hiding the assertion they were meant to make #2620 — a test coupled to a separator convention rather than to the
    property it means to assert.

    I checked for siblings: test_extract.py:84-91 keys both sides of its comparison from the
    same output, so it is self-consistent and unaffected (not silently vacuous).

Verification

Check Result
Issue repro via python -m graphify.extract … mixed → all POSIX
test_astro_import_ids::test_astro_relative_inputs_keep_canonical_ids (collateral evidence in #2625) now passes
graphify extract . (CLI path) unregressed, still POSIX
New test without the fix fails, listing 5 node + 3 edge offenders
Warm AST-cache replay healed
Full suite 41 → 40 failures, zero new; 4122 → 4123 passed

The single removed failure is the astro test above. The remaining 40 are the pre-existing
Windows baseline (symlink privilege, os.geteuid, POSIX mode bits), untouched here.

graphify/extract.py                | 27 ++++++++++++++++++++++++++-
tests/test_extract.py              | 51 +++++++++++++++++++++++++++++++++++++
tests/test_js_import_resolution.py |  9 ++++++---

Migration note

Any Windows-built graph currently holding backslash spellings will re-key those nodes on the
next rebuild — the same one-time churn as #683's fix, moving them onto the canonical form the
rest of the pipeline already expects.

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Graphify reviewed this change.

Worth a look — the grounded gate found no coupling regressions or blocking issues, but 1 advisory finding(s) below merit a look before merge.

Formal verification. 5 change(s) tested, no difference found (not proven).


Graphify review — findings

This pull request appears to revert or roll back a set of changes: the CHANGELOG.md is edited to remove the 0.9.39 and 0.9.40 release note sections and to mark 0.9.38 as "unreleased" again. Correspondingly, graphify/build.py replaces uses of a cross-platform absolute-path helper (is_absolute_any_platform / _is_abs) with os.path.isabs and Path.is_absolute in path normalization, id remapping, and legacy-id detection logic. The surface area spans the build, cache, extraction, resolution, watch, and serve modules along with a large number of associated tests, so the change touches path handling, import/edge resolution, and related test coverage broadly.

Worth a look

  • graph_has_legacy_ids no longer detects POSIX-absolute source_files on Windowsgraphify/build.py:688 · Escalate · high
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 4829 functions depend on the 2969 functions this change touches.

Health — grade B; 10 existing hotspot(s) in the area this change touches (pre-existing, not introduced here):

  • extract() — 401 callers, 39 callees (high)
  • _rebuild_code() — 93 callers, 51 callees (high)
  • build_from_json() — 151 callers, 17 callees (high)
  • detect() — 82 callers, 13 callees (high)
  • deduplicate_entities() — 53 callers, 19 callees (high)
  • build_merge() — 41 callers, 14 callees (high)
  • save_semantic_cache() — 50 callers, 9 callees (high)
  • _extract_generic() — 18 callers, 22 callees (high)
  • …and 2 more

Verification — 4829 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 4548 function(s) in the blast radius were not formally verified this run

Formal verification

Could not verify: Could not verify \_abs\_identity.

The verifier did not have enough to check \_abs\_identity, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: the input domain has 100 values but only 10 distinct were tested — a small finite domain must be EXHAUSTED, not sampled (an untested input could invert the result)

Could not verify: Could not verify \_derive\_prune\_root.

The verifier did not have enough to check \_derive\_prune\_root, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `prune_sources` is annotated `'list[str]'` — outside the synthesizable primitive/collection set

Could not verify: Could not verify \_norm\_source\_file.

The verifier did not have enough to check \_norm\_source\_file, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: the input domain has 100 values but only 10 distinct were tested — a small finite domain must be EXHAUSTED, not sampled (an untested input could invert the result)

No difference found (not proven): No behavior difference found in \_semantic\_id\_remap (not a proof).

The verifier ran both versions of \_semantic\_id\_remap on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.

Note: An input the sampler did not try could still differ.

Could not verify: Could not verify build\_from\_json.

The verifier did not have enough to check build\_from\_json, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 5 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly NameError — names the real obstacle, not a sampling gap)

Could not verify: Could not verify graph\_has\_legacy\_ids.

The verifier did not have enough to check graph\_has\_legacy\_ids, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `root` is annotated `str | Path | None` — outside the synthesizable primitive/collection set

Could not verify: Could not verify cached\_word\_count.

The verifier did not have enough to check cached\_word\_count, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify file\_hash.

The verifier did not have enough to check file\_hash, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify \_llm\_tiebreak.

The verifier did not have enough to check \_llm\_tiebreak, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 105 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly TypeError — names the real obstacle, not a sampling gap)

Could not verify: Could not verify deduplicate\_entities.

The verifier did not have enough to check deduplicate\_entities, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 7 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly TypeError — names the real obstacle, not a sampling gap)

Could not verify: Could not verify \_is\_ignored.

The verifier did not have enough to check \_is\_ignored, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

No difference found (not proven): No behavior difference found in \_nfc (not a proof).

The verifier ran both versions of \_nfc on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.

Note: An input the sampler did not try could still differ.

No difference found (not proven): No behavior difference found in \_parse\_gitignore\_line (not a proof).

The verifier ran both versions of \_parse\_gitignore\_line on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.

Note: An input the sampler did not try could still differ.

No difference found (not proven): No behavior difference found in to\_json (not a proof).

The verifier ran both versions of to\_json on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.

Note: An input the sampler did not try could still differ.

No difference found (not proven): No behavior difference found in to\_html (not a proof).

The verifier ran both versions of to\_html on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.

Note: An input the sampler did not try could still differ.

Could not verify: Could not verify \_rescue\_js\_dynamic\_imports.

The verifier did not have enough to check \_rescue\_js\_dynamic\_imports, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify \_resolve\_objc\_member\_calls.

The verifier did not have enough to check \_resolve\_objc\_member\_calls, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: non-vacuity: domain too small (only 1 distinct inputs exercised, need 3) — 'no divergence' would be near-vacuous

Could not verify: Could not verify extract.

The verifier did not have enough to check extract, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `cache_root` is annotated `Path | None` — outside the synthesizable primitive/collection set

Could not verify: Could not verify \_extract\_generic.

The verifier did not have enough to check \_extract\_generic, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify \_js\_local\_bound\_names.

The verifier did not have enough to check \_js\_local\_bound\_names, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 12 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly AttributeError — names the real obstacle, not a sampling gap)

Could not verify: Could not verify \_go\_collect\_type\_refs.

The verifier did not have enough to check \_go\_collect\_type\_refs, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 84 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly AttributeError — names the real obstacle, not a sampling gap)

Could not verify: Could not verify extract\_go.

The verifier did not have enough to check extract\_go, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify extract\_objc.

The verifier did not have enough to check extract\_objc, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify \_merge\_decl\_def\_classes.

The verifier did not have enough to check \_merge\_decl\_def\_classes, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: the input domain has 49 values but only 7 distinct were tested — a small finite domain must be EXHAUSTED, not sampled (an untested input could invert the result)

Could not verify: Could not verify \_probe\_python\_module\_candidate.

The verifier did not have enough to check \_probe\_python\_module\_candidate, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `candidate` is annotated `Path` — outside the synthesizable primitive/collection set

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Graphify reviewed this change.

Worth a look — the grounded gate found no coupling regressions or blocking issues, but 1 advisory finding(s) below merit a look before merge.

Formal verification. 5 change(s) tested, no difference found (not proven).


Graphify review — findings

This PR reverts changelog entries for versions 0.9.38 (marking it back to "unreleased"), 0.9.39, and 0.9.40, removing their associated notes including the cross-platform absolute-path fix (#2618). Correspondingly, it removes the is_absolute_any_platform helper usage in graphify/build.py, reverting several call sites back to os.path.isabs() / Path.is_absolute(). The touched surface spans build.py, cache.py, and a broad set of test files across build, extract, detect, watch, serve, dedup, exporters, and graphify internals. Note: the diff was truncated, so changes beyond CHANGELOG.md and the start of build.py/cache.py aren't fully visible for summarization.

Worth a look

  • Cross-platform absolute-path detection reverted to os.path.isabs, reintroducing #2618 Windows id leakgraphify/build.py:611 · Escalate · high
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 4829 functions depend on the 2969 functions this change touches.

Health — grade B; 10 existing hotspot(s) in the area this change touches (pre-existing, not introduced here):

  • extract() — 401 callers, 39 callees (high)
  • _rebuild_code() — 93 callers, 51 callees (high)
  • build_from_json() — 151 callers, 17 callees (high)
  • detect() — 82 callers, 13 callees (high)
  • deduplicate_entities() — 53 callers, 19 callees (high)
  • build_merge() — 41 callers, 14 callees (high)
  • save_semantic_cache() — 50 callers, 9 callees (high)
  • _extract_generic() — 18 callers, 22 callees (high)
  • …and 2 more

Verification — 4829 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 4548 function(s) in the blast radius were not formally verified this run

Formal verification

Could not verify: Could not verify \_abs\_identity.

The verifier did not have enough to check \_abs\_identity, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: the input domain has 100 values but only 10 distinct were tested — a small finite domain must be EXHAUSTED, not sampled (an untested input could invert the result)

Could not verify: Could not verify \_derive\_prune\_root.

The verifier did not have enough to check \_derive\_prune\_root, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `prune_sources` is annotated `'list[str]'` — outside the synthesizable primitive/collection set

Could not verify: Could not verify \_norm\_source\_file.

The verifier did not have enough to check \_norm\_source\_file, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: the input domain has 100 values but only 10 distinct were tested — a small finite domain must be EXHAUSTED, not sampled (an untested input could invert the result)

No difference found (not proven): No behavior difference found in \_semantic\_id\_remap (not a proof).

The verifier ran both versions of \_semantic\_id\_remap on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.

Note: An input the sampler did not try could still differ.

Could not verify: Could not verify build\_from\_json.

The verifier did not have enough to check build\_from\_json, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 5 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly NameError — names the real obstacle, not a sampling gap)

Could not verify: Could not verify graph\_has\_legacy\_ids.

The verifier did not have enough to check graph\_has\_legacy\_ids, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `root` is annotated `str | Path | None` — outside the synthesizable primitive/collection set

Could not verify: Could not verify cached\_word\_count.

The verifier did not have enough to check cached\_word\_count, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify file\_hash.

The verifier did not have enough to check file\_hash, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify \_llm\_tiebreak.

The verifier did not have enough to check \_llm\_tiebreak, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 105 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly TypeError — names the real obstacle, not a sampling gap)

Could not verify: Could not verify deduplicate\_entities.

The verifier did not have enough to check deduplicate\_entities, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 7 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly TypeError — names the real obstacle, not a sampling gap)

Could not verify: Could not verify \_is\_ignored.

The verifier did not have enough to check \_is\_ignored, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

No difference found (not proven): No behavior difference found in \_nfc (not a proof).

The verifier ran both versions of \_nfc on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.

Note: An input the sampler did not try could still differ.

No difference found (not proven): No behavior difference found in \_parse\_gitignore\_line (not a proof).

The verifier ran both versions of \_parse\_gitignore\_line on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.

Note: An input the sampler did not try could still differ.

No difference found (not proven): No behavior difference found in to\_json (not a proof).

The verifier ran both versions of to\_json on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.

Note: An input the sampler did not try could still differ.

No difference found (not proven): No behavior difference found in to\_html (not a proof).

The verifier ran both versions of to\_html on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.

Note: An input the sampler did not try could still differ.

Could not verify: Could not verify \_rescue\_js\_dynamic\_imports.

The verifier did not have enough to check \_rescue\_js\_dynamic\_imports, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify \_resolve\_objc\_member\_calls.

The verifier did not have enough to check \_resolve\_objc\_member\_calls, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: non-vacuity: domain too small (only 1 distinct inputs exercised, need 3) — 'no divergence' would be near-vacuous

Could not verify: Could not verify extract.

The verifier did not have enough to check extract, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `cache_root` is annotated `Path | None` — outside the synthesizable primitive/collection set

Could not verify: Could not verify \_extract\_generic.

The verifier did not have enough to check \_extract\_generic, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify \_js\_local\_bound\_names.

The verifier did not have enough to check \_js\_local\_bound\_names, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 12 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly AttributeError — names the real obstacle, not a sampling gap)

Could not verify: Could not verify \_go\_collect\_type\_refs.

The verifier did not have enough to check \_go\_collect\_type\_refs, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 84 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly AttributeError — names the real obstacle, not a sampling gap)

Could not verify: Could not verify extract\_go.

The verifier did not have enough to check extract\_go, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify extract\_objc.

The verifier did not have enough to check extract\_objc, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify \_merge\_decl\_def\_classes.

The verifier did not have enough to check \_merge\_decl\_def\_classes, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: the input domain has 49 values but only 7 distinct were tested — a small finite domain must be EXHAUSTED, not sampled (an untested input could invert the result)

Could not verify: Could not verify \_probe\_python\_module\_candidate.

The verifier did not have enough to check \_probe\_python\_module\_candidate, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `candidate` is annotated `Path` — outside the synthesizable primitive/collection set

safishamsi added a commit that referenced this pull request Aug 12, 2026
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@safishamsi

Copy link
Copy Markdown
Collaborator

Fixed in v0.9.41 (graphifyy==0.9.41 on PyPI). source_file is canonicalized to POSIX separators, so a run given relative inputs on Windows no longer produces non-portable node ids with backslashes. Thanks @rajarshidattapy.

@safishamsi safishamsi closed this Aug 12, 2026
@safishamsi

Copy link
Copy Markdown
Collaborator

Shipped in v0.9.41 (graphifyy==0.9.41 on PyPI). Credited in the release notes. Thanks @rajarshidattapy!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A single extraction emits source_file with mixed path separators (src\lib\content.ts and src/pages/index.astro in the same output) on Windows

2 participants