Skip to content

feat(detect): enumerate large trees via git, repo, and submodules - #3287

Open
albertbu wants to merge 2 commits into
Graphify-Labs:v8from
albertbu:albertb/detect-git-enum
Open

feat(detect): enumerate large trees via git, repo, and submodules#3287
albertbu wants to merge 2 commits into
Graphify-Labs:v8from
albertbu:albertb/detect-git-enum

Conversation

@albertbu

@albertbu albertbu commented Sep 2, 2026

Copy link
Copy Markdown

Use git ls-files, Google repo manifests, and nested worktrees so ignored trees are pruned at directory granularity instead of a Python walk.
slice review: v8...albertbu:graphify:albertb/detect-git-enum

Use git ls-files, Google repo manifests, and nested worktrees so ignored
trees are pruned at directory granularity instead of a Python walk.

Co-authored-by: Cursor <cursoragent@cursor.com>

@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 5 advisory finding(s) below merit a look before merge.

Formal verification. No changes could be formally verified in this run.


Graphify review — findings

Adds Git-aware enumeration to detect() so large trees are listed via git ls-files (--cached plus --others --exclude-standard), Google repo project.list worktrees, and nested git submodules/worktrees rather than walked, keeping gitignored and submodule-internal paths out of the scan. Prunes Buildroot output//output_<board> sysroots and .repo metadata via _is_buildroot_output and expanded _SKIP_DIRS, and drops non-classifiable names under --code-only through _maybe_code_filename. Falls back to the existing os.walk when git ls-files fails or times out (300s), parallelises word counting and regular-file checks via the thread pool, and emits rate-limited --verbose heartbeats during long walks.

Worth a look

  • Symlink outside root now scanned when not a symlink but resolves outside rootgraphify/detect.py · Escalate · high
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • Files reached through an out-of-root symlinked directory are no longer rejectedgraphify/detect.py · Escalate · high
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • Memory scan follows out-of-root symlink directoriesgraphify/detect.py:2391 · Escalate · high
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • Files under out-of-root symlinked directories can be admittedgraphify/detect.py:2562 · Escalate · high
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • Symlink files with targets outside root are no longer skipped for non-symlinks; but non-symlink out-of-root resolution check droppedgraphify/detect.py · Escalate · medium
    • 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 — 2140 functions depend on the 807 functions this change touches.

Health — this change adds coupling hotspots:

  • new: extract() — 520 callers, 43 callees
  • new: _rebuild_code() — 113 callers, 50 callees
  • new: detect() — 116 callers, 17 callees
  • new: save_manifest() — 40 callers, 11 callees
  • new: extract_files_direct() — 17 callers, 20 callees
  • new: extract_xaml() — 19 callers, 17 callees
  • new: extract_corpus_parallel() — 26 callers, 11 callees
  • new: dispatch_command() — 2 callers, 123 callees
  • …and 33 more — each is listed as a finding

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

Formal verification

Could not verify: Could not verify detect.

The verifier did not have enough to check detect, 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 `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify detect\_incremental.

The verifier did not have enough to check detect\_incremental, 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 `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify \_git\_tracked\_path\_keys.

The verifier did not have enough to check \_git\_tracked\_path\_keys, 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 `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify \_is\_noise\_dir.

The verifier did not have enough to check \_is\_noise\_dir, 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 `parent` is annotated `'Path | None'` — outside the synthesizable primitive/collection set

· 6 grounded finding(s) anchored inline below; 35 more finding(s) on lines outside this diff (see the check run).

Comment thread graphify/detect.py
return name[dot:].lower() in _CORPUS_EXT_LOWER


def _is_noise_dir(part: str, parent: "Path | None" = None) -> 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 regression_is_noise_dir()

10 callers depend on it (afferent coupling).

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

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Health flags are expected: git enumeration reuses _is_noise_dir so walk and ls-files prune the same trees. detect()’s 116 callers are pre-existing. Not splitting those helpers in this PR.

Comment thread graphify/detect.py
return _path_is_under(worktree, root) or _path_is_under(root, worktree)


def _git_enumerate_named_worktrees(

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 regression_git_enumerate_named_worktrees()

fans out to 7 callees (efferent coupling).

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

Comment thread graphify/detect.py
)


def _git_enumerate_files(

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 regression_git_enumerate_files()

fans out to 8 callees (efferent coupling).

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

Comment thread graphify/detect.py


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:
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, code_only: bool = False) -> 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 17 callees (efferent coupling); 116 callers depend on it (afferent coupling).

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

Comment thread graphify/detect.py
vprint(f"scanning {root} ...")
heartbeat = _Heartbeat("scanning")

def _walk_from(

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 regression_walk_from()

fans out to 8 callees (efferent coupling).

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

Comment thread graphify/detect.py

for p in all_files:
# For memory dir files, skip hidden/noise filtering
def _admit(p: Path) -> tuple:

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 regression_admit()

fans out to 8 callees (efferent coupling).

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

Gating the out-of-root check on islink() admitted regular files reached
by following a symlink directory, including graphify-out/memory/ which
skips ignore pruning. Restore the v8 resolve()-under-root admit guard
and prune those dirs during the memory walk too.

Co-authored-by: Cursor <cursoragent@cursor.com>
@albertbu

albertbu commented Sep 2, 2026

Copy link
Copy Markdown
Author

Out-of-root resolve() guard is restored

@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 5 advisory finding(s) below merit a look before merge.

Formal verification. No changes could be formally verified in this run.


Graphify review — findings

Adds a git-aware enumeration path to detect() that lists large trees via git ls-files (cached + untracked, excluding standard ignores), Google repo .repo/project.list project sets, and nested git worktrees/submodules, so gitignored and submodule directories are enumerated from their own indexes instead of being walked. Prunes Buildroot output//output_<board>/ sysroots (detected by host/target/images/staging layout) and .repo metadata, drops non-classifiable filenames under --code-only, and parallelizes word counting via a thread pool with a serial fallback. Emits rate-limited stderr heartbeats during long walks under --verbose, with a 300s git ls-files timeout that falls back to the Python os.walk scan when Git is unavailable or times out.

Worth a look

  • memory_dir files may be lost when git enumeration replaces all_filesgraphify/detect.py · Escalate · high
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • code_only path skips Google Workspace/Office conversion but stores original binary as readable filegraphify/detect.py · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • non-descend walk clears dirnames only when not in_memory, leaking subtree files into top-level bucketgraphify/detect.py · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • Escaping .gitmodules path can make scanner run Git outside the scan rootgraphify/detect.py:1542 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • Concurrent subtree walkers mutate shared ignored_dirs without synchronizationgraphify/detect.py:2287 · Escalate · medium
    • 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 — 2142 functions depend on the 809 functions this change touches.

Health — this change adds coupling hotspots:

  • new: extract() — 520 callers, 43 callees
  • new: _rebuild_code() — 113 callers, 50 callees
  • new: detect() — 117 callers, 17 callees
  • new: save_manifest() — 40 callers, 11 callees
  • new: extract_files_direct() — 17 callers, 20 callees
  • new: extract_xaml() — 19 callers, 17 callees
  • new: extract_corpus_parallel() — 26 callers, 11 callees
  • new: dispatch_command() — 2 callers, 123 callees
  • …and 33 more — each is listed as a finding

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

Formal verification

Could not verify: Could not verify detect.

The verifier did not have enough to check detect, 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 `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify detect\_incremental.

The verifier did not have enough to check detect\_incremental, 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 `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify \_git\_tracked\_path\_keys.

The verifier did not have enough to check \_git\_tracked\_path\_keys, 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 `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify \_is\_noise\_dir.

The verifier did not have enough to check \_is\_noise\_dir, 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 `parent` is annotated `'Path | None'` — outside the synthesizable primitive/collection set

· 6 grounded finding(s) anchored inline below; 35 more finding(s) on lines outside this diff (see the check run).

Comment thread graphify/detect.py
return name[dot:].lower() in _CORPUS_EXT_LOWER


def _is_noise_dir(part: str, parent: "Path | None" = None) -> 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 regression_is_noise_dir()

10 callers depend on it (afferent coupling).

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

Comment thread graphify/detect.py
return _path_is_under(worktree, root) or _path_is_under(root, worktree)


def _git_enumerate_named_worktrees(

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 regression_git_enumerate_named_worktrees()

fans out to 7 callees (efferent coupling).

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

Comment thread graphify/detect.py
)


def _git_enumerate_files(

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 regression_git_enumerate_files()

fans out to 8 callees (efferent coupling).

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

Comment thread graphify/detect.py


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:
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, code_only: bool = False) -> 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 17 callees (efferent coupling); 117 callers depend on it (afferent coupling).

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

Comment thread graphify/detect.py
vprint(f"scanning {root} ...")
heartbeat = _Heartbeat("scanning")

def _walk_from(

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 regression_walk_from()

fans out to 8 callees (efferent coupling).

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

Comment thread graphify/detect.py

for p in all_files:
# For memory dir files, skip hidden/noise filtering
def _admit(p: Path) -> tuple:

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 regression_admit()

fans out to 8 callees (efferent 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