Skip to content

fix(windows): preserve deep local and UNC files across the corpus pipeline - #2317

Open
nimide wants to merge 5 commits into
Graphify-Labs:v8from
nimide:fix/windows-long-paths-0930
Open

fix(windows): preserve deep local and UNC files across the corpus pipeline#2317
nimide wants to merge 5 commits into
Graphify-Labs:v8from
nimide:fix/windows-long-paths-0930

Conversation

@nimide

@nimide nimide commented Jul 29, 2026

Copy link
Copy Markdown

Summary

Graphify 0.9.30 enters recursive corpus enumeration with ordinary Windows drive or UNC paths. A real descendant that crosses the legacy MAX_PATH boundary can therefore fail before Graphify discovers it, producing a misleading [WinError 3] The system cannot find the path specified warning and an incomplete graph.

This change introduces a centralized, cross-platform filesystem I/O boundary in graphify.paths and applies it throughout the source-corpus pipeline. Windows filesystem calls use extended-length path spelling internally; Linux and macOS remain pass-through. Graph IDs, manifests, cache keys, ignore matching, diagnostics, and user-visible output continue to use ordinary logical paths.

Representative UNC translation:

Logical/public path:  \\server\share\manuals\...\deep\file.pdf
Windows I/O path:     \\?\UNC\server\share\manuals\...\deep\file.pdf

Problem and root cause

Selected late-stage reads already used a private Windows path helper, but recursive discovery began with an ordinary path:

for dirpath, dirnames, filenames in os.walk(scan_root, ...):
    ...

On Windows, os.walk() uses os.scandir() for recursive enumeration. When the ordinary path reaches the legacy boundary, enumeration can fail even though the directory exists. A directory just below 260 characters can also fail because native enumeration adds a search suffix.

Changing only the first os.walk() call would not close the defect. A deep file could be discovered and then fail during classification, hashing, cache access, structural extraction, semantic extraction, project resolution, incremental reconciliation, or document/media processing. This migration therefore covers the corpus from discovery through graph production.

Design

Separate logical identity from filesystem transport

The implementation maintains two path representations:

  1. Logical/public path

    • Ordinary drive or UNC spelling.
    • Used for graph identity, manifests, cache metadata, ignore matching, diagnostics, and serialized output.
  2. Filesystem I/O path

    • \\?\C:\... for local Windows paths.
    • \\?\UNC\server\share\... for UNC paths.
    • Used only while calling direct filesystem APIs.

This prevents the same file from acquiring two Graphify identities and keeps generated artifacts portable.

Centralize the boundary

graphify.paths now owns the path translation and filesystem helpers used by the corpus pipeline, including:

  • io_path() and logical_path();
  • resolution, existence, type, symlink, and stat operations;
  • safe text and binary reads;
  • directory creation, iteration, globbing, and walking;
  • unlink and replace operations;
  • long-path-safe atomic writes.

The adapter normalizes and makes a Windows path absolute before adding the extended prefix. Already-extended paths and device paths remain unchanged. Off Windows, the helpers pass paths through unchanged.

Traversal reconstructs returned paths from the caller's logical root, so relative inputs remain relative even though Windows uses an absolute extended path internally.

Keep extended spelling away from external programs

The \\?\ spelling remains an internal filesystem transport detail. External tools receive ordinary absolute paths. Tests cover this rule for Google Workspace export and the C preprocessor.

Preserve real errors

The change does not suppress traversal failures. Permission errors, disconnected shares, deletion races, broken links, and genuinely missing paths still flow to existing diagnostics. Only the transport-only prefix is removed before an error reaches user-facing code.

Implementation scope

The migration covers source-corpus filesystem boundaries used by:

  • detection and recursive enumeration;
  • direct collect_files() extraction;
  • incremental manifests and source-liveness checks;
  • content hashing, stat indexes, and AST/semantic caches;
  • structural extraction and language/workspace resolvers;
  • semantic text and image reads;
  • Markdown, PDF, Office, Google Workspace, and transcription inputs;
  • XAML and project/workspace scans;
  • incremental/watch reconciliation;
  • graph/root markers and atomic graph/cache writes involved in the corpus pipeline.

The extended prefix is removed immediately when traversal yields a path. It is not persisted in graph output, manifests, cache metadata, or diagnostics.

Graphify 0.9.30 baseline

This proposal is based directly on Graphify 0.9.30 commit:

ecfcd160d56b420eb8241430fa7b5b1951c7829f

The port preserves behavior introduced in 0.9.30, including TSX-specific parsing, portable AST cache anchors, fail-closed post-commit graph loading, and protected atomic graph replacement.

Tests and CI

tests/test_windows_long_paths.py provides host-neutral contract tests and native Windows integration coverage for:

  • local-drive and UNC conversion;
  • inverse conversion and idempotence;
  • normalization before prefixing;
  • os.walk() receiving extended UNC spelling;
  • logicalized traversal results and errors;
  • preservation of relative Windows traversal spelling;
  • a real source path longer than 300 characters;
  • existence, file type, stat, direct iteration, simple glob, and recursive glob;
  • detection with no walk_errors;
  • direct collection;
  • stable content hashing and cache reuse;
  • manifest write/read round trips;
  • Markdown extraction;
  • first-pass and warm-cache Python extraction;
  • verification that \\?\ never appears in returned or persisted logical artifacts.

The focused GitHub Actions matrix runs the path suite on:

matrix:
  os: [ubuntu-latest, macos-latest, windows-latest]

The Windows job creates and extracts a real 300-plus-character local path. UNC behavior is unit-tested at the conversion and traversal boundary because standard hosted runners do not provide a live SMB share.

Native Windows validation

Validation was performed against the 0.9.30 baseline above using Python 3.12 on Windows.

Focused filesystem/path suite: 73 passed, 1 skipped
Python compilation:            passed
Patch whitespace validation:   passed
Native path length exercised:  greater than 300 characters

The focused suite validates:

  • deep-file detection with no traversal errors;
  • content hashing and warm-cache reuse;
  • manifest round trips;
  • Markdown extraction;
  • Python structural extraction;
  • logical/public paths remaining free of \\?\ prefixes;
  • ordinary absolute paths being passed to external Google Workspace and C-preprocessor processes.

The one skip is the symlink-specific atomic-write test. The test host did not grant Windows symlink creation permission (WinError 1314); it requires Developer Mode, administrator rights, or SeCreateSymbolicLinkPrivilege.

Full-suite baseline comparison

An exploratory full Windows run before the final C-preprocessor follow-up produced:

3,830 passed, 42 failed, 16 skipped

The exact 42 failed node IDs were then rerun against untouched Graphify 0.9.30:

41 failed, 1 passed

One shared C-preprocessor failure exposed an extended-path prefix crossing into an external executable. That issue was corrected and the test was added to the focused cross-platform gate.

After that correction, rerunning the cached failed set on the patched tree produced:

40 failed, 1 passed

The remaining 40 failures reproduce on the untouched 0.9.30 baseline. They are existing Windows portability or test-environment issues, including unavailable symlink privileges, POSIX-only separator assertions, Windows current-directory deletion semantics, default text encoding, output filename limits, and installer/hook behavior outside this corpus-I/O change.

The one earlier non-baseline CodeBuddy directory-rename failure did not reproduce:

Targeted CodeBuddy test: 1 passed
Complete CodeBuddy module: 25 passed

This PR therefore does not claim that the complete existing Windows suite is clean. It establishes a passing native gate for the source-corpus long-path pipeline and avoids broadening this change into unrelated Windows portability work.

Why not rely only on Windows policy?

Windows can opt applications into longer ordinary Win32 paths through LongPathsEnabled and a longPathAware executable manifest. That remains useful defense in depth, but it is not a sufficient Graphify contract:

  • users may not have administrative access to change policy;
  • source and packaged execution modes may not share one executable manifest;
  • managed environments may leave the policy disabled;
  • Graphify can directly use the documented extended namespace for filesystem calls it controls.

References:

Compatibility and risk

  • Linux/macOS: io_path() is a no-op and existing path behavior is retained.
  • Windows: direct corpus filesystem operations receive normalized absolute extended spelling.
  • Artifacts: no schema or persisted-path-format migration is required.
  • Security: containment and identity logic continue to operate on logical paths rather than transport prefixes.
  • Diagnostics: genuine failures remain visible.

The broad call-site migration is intentional. Adapting only initial enumeration would allow a file to be discovered and then fail later during hashing or extraction, recreating the same incomplete-graph defect at another stage.

Scope and non-goals

This PR makes Graphify's source-corpus pipeline long-path-safe and closes a concrete Windows platform-parity gap. It does not claim that every external executable or third-party library accepts every possible path. It cannot prevent failures caused by permissions, share outages, authentication, invalid names, per-component filename limits, or network disconnections.

Unrelated installer, hook, exporter, and user-preference portability issues remain separate work. The watchdog subscription continues to receive the ordinary watch root so event paths retain Graphify's logical identity; a watch root that is itself already beyond the legacy boundary should receive separate native validation.

Reviewer guide

This submission is one commit created from the author-neutral 0.9.30 diff plus native-Windows test follow-ups.

Suggested review order:

  1. Review graphify/paths.py for the logical-path versus filesystem-I/O-path contract.
  2. Review discovery, caching, extraction, manifest, resolver, and watch call-site migrations.
  3. Review tests/test_windows_long_paths.py, the atomic-write/external-process tests, and the cross-platform CI matrix.

Checklist

  • based directly on Graphify 0.9.30
  • ordinary paths remain the public and storage format
  • extended paths are applied only at direct filesystem boundaries
  • extended paths are not passed to arbitrary external executables
  • relative path spelling is preserved
  • traversal and downstream extraction are both covered
  • UNC conversion and error logicalization are tested
  • a native 300-plus-character path passes on Windows
  • Linux and macOS no-op behavior is enforced in CI
  • genuine traversal errors remain reported
  • changelog entry included

@nimide
nimide marked this pull request as ready for review July 29, 2026 21:47

@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.

Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).

Graphify reviewed this change.

Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).


Graphify review — findings

This PR introduces a cross-platform filesystem path adapter (in graphify.paths) and routes I/O operations throughout the codebase—build.py, cache.py, and various extractors, detection, watch, LLM, and security modules—through helper functions like resolve_path, read_text, path_exists, and io_path. The stated intent is to let Windows scans handle deeply nested local/UNC paths that exceed the legacy MAX_PATH limit by using extended-length paths at the I/O boundary while keeping ordinary paths in graph IDs, manifests, and cache keys. It also adds a new platform-paths CI job that runs a set of path-related tests across Ubuntu, macOS, and Windows, and adds an "Unreleased" entry to the CHANGELOG describing the change. The surface area is broad but mostly mechanical: swapping direct Path/os calls for the shared adapter wrappers across many files.

No blocking issues surfaced. 4 lower-confidence candidates did not survive cross-model review.

Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 4345 functions depend on the 1683 functions this change touches.

Health — this change adds coupling hotspots:

  • worse: extract() — 353 callers, 30 callees
  • worse: _rebuild_code() — 55 callers, 57 callees
  • worse: build_from_json() — 138 callers, 14 callees
  • worse: detect() — 82 callers, 16 callees
  • worse: load_cached() — 39 callers, 8 callees
  • worse: extract_bash() — 34 callers, 9 callees
  • worse: _extract_generic() — 18 callers, 16 callees
  • worse: file_hash() — 39 callers, 7 callees
  • …and 112 more

Verification — 4345 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: 4284 function(s) in the blast radius were not formally verified this run

· 20 grounded finding(s) anchored inline below; 86 more finding(s) on lines outside this diff (see the check run); 14 additional anchorable finding(s) not shown (cap).

Comment thread graphify/cache.py
@@ -931,38 +952,38 @@ def save_cached(path: Path, result: dict, root: Path = Path("."), kind: str = "a

def cached_files(root: Path = Path(".")) -> set[str]:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressioncached_files()

high coupling complexity (Ca·Ce = 12).

Grounded coupling-delta finding (deterministic), not an LLM guess.

Comment thread graphify/cache.py
return hashes


def clear_cache(root: Path = Path(".")) -> None:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressionclear_cache()

7 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

Comment thread graphify/cache.py
_unlink_path(f)


def prune_semantic_cache(root: Path, live_hashes: set[str]) -> int:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressionprune_semantic_cache()

9 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

return paths


def introspect_cargo(root: str | Path) -> dict[str, Any]:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressionintrospect_cargo()

11 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

Comment thread graphify/detect.py
return True


def detect(root: Path, *, follow_symlinks: bool | None = None, google_workspace: bool | None = None, extra_excludes: list[str] | None = None, cache_root: Path | None = None, gitignore: bool = True) -> dict:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressiondetect()

fans out to 16 callees (efferent coupling); 82 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

from graphify.paths import read_bytes as _read_file_bytes


def extract_julia(path: Path) -> dict:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressionextract_julia()

16 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

from graphify.extractors.base import _file_stem, _make_id


def extract_razor(path: Path) -> dict:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressionextract_razor()

14 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

from graphify.extractors.base import _make_id


def extract_sln(path: Path) -> dict:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressionextract_sln()

10 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

@@ -51,9 +52,9 @@ def _pkg_id(name: str) -> str:
def extract_package_manifest(path: Path) -> dict[str, Any]:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressionextract_package_manifest()

7 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

Comment thread graphify/paths.py
return value


def resolve_path(path: PathLike) -> Path:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressionresolve_path()

86 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

nimide added 2 commits August 1, 2026 14:36
…h 0932]

Removed GRAPHIFY_OUT_NAME constant and updated related logic to prevent treating the default output directory as source input. Added coverage artifact detection functionality.
…ith 0932]

Refactor _git_head function to accept cwd parameter for better repo context. Update comments and logic to improve clarity on graph extraction and node preservation.

@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.

Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).

Graphify reviewed this change.

Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).


Graphify review — findings

This PR introduces a cross-platform filesystem path adapter (in graphify.paths) and threads its helpers (resolve_path, read_text, path_exists, io_path, logical_path, etc.) through build.py, cache.py, and the various language extractors, Google Workspace, and cargo-introspection modules, replacing direct Path/os filesystem calls. The stated intent is to let Windows scans handle deeply nested local and UNC paths that exceed the legacy MAX_PATH limit by using extended-length paths at I/O boundaries while keeping ordinary paths in graph IDs, manifests, and diagnostics. It also adds a new CI job matrix (Ubuntu/macOS/Windows) exercising long-path and related tests, plus a CHANGELOG entry. Surface area spans many extractor modules and rationale comment blocks in addition to the core build.py/cache.py changes and workflow/config files.

No blocking issues surfaced. 4 lower-confidence candidates did not survive cross-model review.

Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 4347 functions depend on the 1685 functions this change touches.

Health — this change adds coupling hotspots:

  • worse: extract() — 353 callers, 30 callees
  • worse: _rebuild_code() — 55 callers, 57 callees
  • worse: build_from_json() — 138 callers, 14 callees
  • worse: detect() — 82 callers, 16 callees
  • worse: load_cached() — 39 callers, 8 callees
  • worse: extract_bash() — 34 callers, 9 callees
  • worse: _extract_generic() — 18 callers, 16 callees
  • worse: file_hash() — 39 callers, 7 callees
  • …and 112 more

Verification — 4347 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: 4286 function(s) in the blast radius were not formally verified this run

· 20 grounded finding(s) anchored inline below; 86 more finding(s) on lines outside this diff (see the check run); 14 additional anchorable finding(s) not shown (cap).

Comment thread graphify/cache.py
@@ -931,38 +952,38 @@ def save_cached(path: Path, result: dict, root: Path = Path("."), kind: str = "a

def cached_files(root: Path = Path(".")) -> set[str]:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressioncached_files()

high coupling complexity (Ca·Ce = 12).

Grounded coupling-delta finding (deterministic), not an LLM guess.

Comment thread graphify/cache.py
return hashes


def clear_cache(root: Path = Path(".")) -> None:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressionclear_cache()

7 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

Comment thread graphify/cache.py
_unlink_path(f)


def prune_semantic_cache(root: Path, live_hashes: set[str]) -> int:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressionprune_semantic_cache()

9 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

return paths


def introspect_cargo(root: str | Path) -> dict[str, Any]:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressionintrospect_cargo()

11 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

Comment thread graphify/detect.py
return True


def detect(root: Path, *, follow_symlinks: bool | None = None, google_workspace: bool | None = None, extra_excludes: list[str] | None = None, cache_root: Path | None = None, gitignore: bool = True) -> dict:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressiondetect()

fans out to 16 callees (efferent coupling); 82 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

from graphify.paths import read_bytes as _read_file_bytes


def extract_julia(path: Path) -> dict:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressionextract_julia()

16 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

from graphify.extractors.base import _file_stem, _make_id


def extract_razor(path: Path) -> dict:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressionextract_razor()

14 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

from graphify.extractors.base import _make_id


def extract_sln(path: Path) -> dict:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressionextract_sln()

10 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

@@ -51,9 +52,9 @@ def _pkg_id(name: str) -> str:
def extract_package_manifest(path: Path) -> dict[str, Any]:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressionextract_package_manifest()

7 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

Comment thread graphify/paths.py
return value


def resolve_path(path: PathLike) -> Path:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressionresolve_path()

86 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

@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.

Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).

Graphify reviewed this change.

Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).


Graphify review — findings

This PR introduces a cross-platform filesystem path adapter (in graphify.paths) to handle Windows extended-length paths, wiring helper functions like resolve_path, path_exists, read_text, io_path, make_dirs, and others through call sites in build.py, cache.py, and across many extractors, watch, CLI, and LLM modules. The intent is to use extended-length paths only at the I/O boundary while keeping ordinary paths in graph IDs, manifests, cache keys, and diagnostics. It also adds a new platform-paths CI matrix job running path-related tests on Ubuntu/macOS/Windows and a corresponding changelog entry. The surface area is broad—touching path handling in build/merge logic, cache stat-indexing and cleanup, and numerous extractor and resolution modules—but the changes appear to be mechanical substitutions swapping direct Path/os filesystem calls for the shared adapter helpers.

No blocking issues surfaced. 3 lower-confidence candidates did not survive cross-model review.

Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 4347 functions depend on the 1685 functions this change touches.

Health — this change adds coupling hotspots:

  • worse: extract() — 353 callers, 30 callees
  • worse: _rebuild_code() — 55 callers, 57 callees
  • worse: build_from_json() — 138 callers, 14 callees
  • worse: detect() — 82 callers, 16 callees
  • worse: load_cached() — 39 callers, 8 callees
  • worse: extract_bash() — 34 callers, 9 callees
  • worse: _extract_generic() — 18 callers, 16 callees
  • worse: file_hash() — 39 callers, 7 callees
  • …and 112 more

Verification — 4347 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: 4286 function(s) in the blast radius were not formally verified this run

· 20 grounded finding(s) anchored inline below; 88 more finding(s) on lines outside this diff (see the check run); 12 additional anchorable finding(s) not shown (cap).

Comment thread graphify/cache.py
@@ -931,38 +952,38 @@ def save_cached(path: Path, result: dict, root: Path = Path("."), kind: str = "a

def cached_files(root: Path = Path(".")) -> set[str]:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressioncached_files()

high coupling complexity (Ca·Ce = 12).

Grounded coupling-delta finding (deterministic), not an LLM guess.

Comment thread graphify/cache.py
return hashes


def clear_cache(root: Path = Path(".")) -> None:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressionclear_cache()

7 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

Comment thread graphify/cache.py
_unlink_path(f)


def prune_semantic_cache(root: Path, live_hashes: set[str]) -> int:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressionprune_semantic_cache()

9 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

return paths


def introspect_cargo(root: str | Path) -> dict[str, Any]:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressionintrospect_cargo()

11 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

Comment thread graphify/detect.py
return True


def detect(root: Path, *, follow_symlinks: bool | None = None, google_workspace: bool | None = None, extra_excludes: list[str] | None = None, cache_root: Path | None = None, gitignore: bool = True) -> dict:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressiondetect()

fans out to 16 callees (efferent coupling); 82 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

from graphify.extractors.base import _make_id


def extract_sln(path: Path) -> dict:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressionextract_sln()

10 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

@@ -51,9 +52,9 @@ def _pkg_id(name: str) -> str:
def extract_package_manifest(path: Path) -> dict[str, Any]:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressionextract_package_manifest()

7 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

Comment thread graphify/paths.py
return value


def resolve_path(path: PathLike) -> Path:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressionresolve_path()

86 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

Comment thread graphify/paths.py
return Path(logical_path(os.path.realpath(io_path(path))))


def path_exists(path: PathLike) -> bool:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressionpath_exists()

47 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

Comment thread graphify/paths.py
return os.path.exists(io_path(path))


def path_is_file(path: PathLike) -> bool:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressionpath_is_file()

43 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

@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.

Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).

Graphify reviewed this change.

Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).


Graphify review — findings

This PR introduces a cross-platform filesystem path adapter (via graphify.paths helpers like _resolve_path, _read_text, _path_exists, _io_path, etc.) and routes build.py, cache.py, and other modules through it so that OS-level I/O uses extended-length paths on Windows while logical paths (graph IDs, manifests, cache keys) stay ordinary. It also adds a new platform-paths CI job running path-related tests across Ubuntu, macOS, and Windows, along with new/updated tests and a changelog entry describing the Windows long-path (MAX_PATH/UNC) fix. The surface area spans path handling across build, caching, extractors, detection, LLM helpers, and security modules, plus CI workflow and documentation changes.

No blocking issues surfaced. 3 lower-confidence candidates did not survive cross-model review.

Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 4421 functions depend on the 1696 functions this change touches.

Health — this change adds coupling hotspots:

  • worse: extract() — 361 callers, 31 callees
  • worse: _rebuild_code() — 68 callers, 58 callees
  • worse: build_from_json() — 142 callers, 15 callees
  • worse: detect() — 84 callers, 16 callees
  • worse: load_cached() — 39 callers, 8 callees
  • worse: extract_bash() — 34 callers, 9 callees
  • worse: build_merge() — 29 callers, 10 callees
  • worse: _extract_generic() — 18 callers, 16 callees
  • …and 113 more

Verification — 4421 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: 4360 function(s) in the blast radius were not formally verified this run

· 20 grounded finding(s) anchored inline below; 87 more finding(s) on lines outside this diff (see the check run); 14 additional anchorable finding(s) not shown (cap).

Comment thread graphify/cache.py
@@ -931,38 +952,38 @@ def save_cached(path: Path, result: dict, root: Path = Path("."), kind: str = "a

def cached_files(root: Path = Path(".")) -> set[str]:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressioncached_files()

high coupling complexity (Ca·Ce = 12).

Grounded coupling-delta finding (deterministic), not an LLM guess.

Comment thread graphify/cache.py
return hashes


def clear_cache(root: Path = Path(".")) -> None:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressionclear_cache()

7 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

Comment thread graphify/cache.py
_unlink_path(f)


def prune_semantic_cache(root: Path, live_hashes: set[str]) -> int:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressionprune_semantic_cache()

9 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

return paths


def introspect_cargo(root: str | Path) -> dict[str, Any]:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressionintrospect_cargo()

11 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

Comment thread graphify/detect.py
return True


def detect(root: Path, *, follow_symlinks: bool | None = None, google_workspace: bool | None = None, extra_excludes: list[str] | None = None, cache_root: Path | None = None, gitignore: bool = True) -> dict:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressiondetect()

fans out to 16 callees (efferent coupling); 84 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

from graphify.paths import read_bytes as _read_file_bytes


def extract_julia(path: Path) -> dict:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressionextract_julia()

16 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

from graphify.extractors.base import _file_stem, _make_id


def extract_razor(path: Path) -> dict:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressionextract_razor()

14 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

from graphify.extractors.base import _make_id


def extract_sln(path: Path) -> dict:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressionextract_sln()

10 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

@@ -51,9 +52,9 @@ def _pkg_id(name: str) -> str:
def extract_package_manifest(path: Path) -> dict[str, Any]:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressionextract_package_manifest()

7 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

Comment thread graphify/paths.py
return value


def resolve_path(path: PathLike) -> Path:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressionresolve_path()

86 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

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.

1 participant