Skip to content

feat(config): let a project declare what an ambiguous extension means via .graphifyrc (#2961) - #3075

Open
abhay-codes07 wants to merge 2 commits into
Graphify-Labs:v8from
abhay-codes07:feat/graphifyrc-language-overrides
Open

feat(config): let a project declare what an ambiguous extension means via .graphifyrc (#2961)#3075
abhay-codes07 wants to merge 2 commits into
Graphify-Labs:v8from
abhay-codes07:feat/graphifyrc-language-overrides

Conversation

@abhay-codes07

Copy link
Copy Markdown
Contributor

Closes #2961.

The problem

.inc is hard-mapped to the Pascal extractor, but it is "include file" in whatever language a project happens to use: PHP on pfSense, Pascal in a Delphi tree, SQL or assembly elsewhere. A PHP .inc parsed as Pascal does not fail — it yields a handful of incidental nodes, so the graph looks populated while the shipped runtime is missing from it. On the reporter's file: 7 nodes instead of 471, from identical bytes.

Hardcoding one more extension (what closed #1042) cannot fix this, because .inc has no single correct global meaning. The same is true of .h (C or C++) and .m (Objective-C or MATLAB), which today get a content sniff that is right most of the time.

The change

.graphifyrc — which already exists for viz_node_limit — gains a project-level declaration:

# .graphifyrc
language.inc=php        # a language name ...
language.tpl=.ts        # ... or an extension graphify already knows

The declaration reaches every place graphify keys a decision on the suffix, not just the dispatch table:

site effect
detect.classify_file a declared extension counts as code (so .tpl → PHP is scanned at all)
extract._get_extractor dispatch, ahead of the .h/.m content sniffs
_lang_is_case_insensitive, _lang_family, the PHP type pass cross-file resolution rules follow the declared language
resolver_registry.run_language_resolvers the declared language's resolvers wake (PHP's, not Pascal's)
cache.load_cached / save_cached a new salt kwarg folds the target language into the key — the same bytes cached under the old extractor are never served for the new one, in either direction
the extraction pool an initializer forwards the mapping to workers, which start with empty module state under spawn

detect() and extract() activate <root>/.graphifyrc themselves, so the CLI, update/watch, the git hooks, the MCP server and the skill runbook all pick it up with no further wiring — including the issue's own library-level repro. A malformed file is reported once on stderr and the scan continues with graphify's defaults; a typo must be loud, but it must not kill a scan.

The parser moves from hooks.py into a dependency-free graphify/rcfile.py so detect/extract (and the workers) can import it; hooks._load_graphifyrc keeps its name, behaviour and error messages, and the existing hooks tests pass unchanged.

Before / after

Same PHP source as a.inc and b.php:

no .graphifyrc          a.inc  nodes=  1 edges=  0
                        b.php  nodes=  7 edges= 10
language.inc=php        a.inc  nodes=  7 edges= 10
                        b.php  nodes=  7 edges= 10
language.inc=pascal     a.inc  nodes=  1 edges=  0    <- not the cached PHP entry
language.inc=klingon    a.inc  nodes=  1 edges=  0    + one stderr warning naming line 1

Through the real CLI:

$ graphify extract . --code-only
  language overrides (.graphifyrc): .inc -> .php
[graphify extract] wrote graphify-out/graph.json — 7 nodes, 10 edges

Tests

tests/test_language_overrides.py — 42 tests: the parser (aliases, explicit extensions, key normalisation, line-numbered errors, the existing option and unknown keys untouched, hooks._load_graphifyrc compatibility); each suffix-keyed site individually; the reporter's same-bytes repro; the library-caller path; detect() counting a declared extension as code; the cache round-trip in both directions; the pool initializer and the mapping it is handed; the once-only warning for a malformed file.

With the wiring reverted and only the new module kept, 12 of them fail; with it, all 42 pass. The related existing suites (test_cache, test_detect, test_extract*, test_hooks, test_incremental*, test_pascal*, test_php_type_resolution, test_extractors_registry) are unchanged; the full suite matches the v8 baseline (the same platform-specific failures on Windows, nothing new).

README gains a short "Project configuration" section.

Copilot AI lite review requested due to automatic review settings August 25, 2026 08:57

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@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 .graphifyrc project config whose language.<ext>=<lang> lines remap ambiguous extensions (.inc, .h, .m, etc.) per-repo, so file classification (classify_file), extractor dispatch (_get_extractor), case-insensitivity and interop-family checks, and cross-file resolution all key off effective_suffix instead of the raw suffix, with detect/extract activating overrides for the scan root and reporting typos once on stderr. Salts the AST cache key with cache_salt via load_cached/save_cached so a file re-parsed under a different declared language never reuses the entry from the old extractor. Propagates the parent's overrides into spawn pool workers through _worker_init so _get_extractor and the cache key stay consistent across processes.

Worth a look

  • classify_file now depends on previously activated project overridesgraphify/detect.py:518 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • Global language-override state activated per-scan without synchronizationgraphify/detect.py:1520 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • Project language overrides are activated as process-global scan stategraphify/detect.py:1520 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • Global language overrides can race between concurrent extractionsgraphify/extract.py:5769 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • activate_language_overrides mutates process-global overrides read non-atomically by concurrent extract callsgraphify/extract.py:5771 · 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 — 2489 functions depend on the 538 functions this change touches.

Health — this change adds coupling hotspots:

  • new: extract() — 493 callers, 45 callees
  • new: _rebuild_code() — 98 callers, 50 callees
  • new: detect() — 110 callers, 16 callees
  • new: save_semantic_cache() — 58 callers, 9 callees
  • new: save_manifest() — 40 callers, 11 callees
  • new: load_cached() — 48 callers, 8 callees
  • new: extract_files_direct() — 17 callers, 20 callees
  • new: extract_xaml() — 19 callers, 17 callees
  • …and 56 more — each is listed as a finding

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

Formal verification

Could not verify: Could not verify load\_cached.

The verifier did not have enough to check load\_cached, 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 save\_cached.

The verifier did not have enough to check save\_cached, 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 classify\_file.

The verifier did not have enough to check classify\_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: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

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 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\_parallel.

The verifier did not have enough to check \_extract\_parallel, 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 \_extract\_sequential.

The verifier did not have enough to check \_extract\_sequential, 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 \_extract\_single\_file.

The verifier did not have enough to check \_extract\_single\_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: all 8 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly ValueError — names the real obstacle, not a sampling gap)

Could not verify: Could not verify \_get\_extractor.

The verifier did not have enough to check \_get\_extractor, 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 \_lang\_family.

The verifier did not have enough to check \_lang\_family, 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 `source_file` is annotated `object` — outside the synthesizable primitive/collection set

Could not verify: Could not verify \_lang\_is\_case\_insensitive.

The verifier did not have enough to check \_lang\_is\_case\_insensitive, 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 `source_file` is annotated `object` — outside the synthesizable primitive/collection set

Could not verify: Could not verify \_load\_graphifyrc.

The verifier did not have enough to check \_load\_graphifyrc, 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 run\_language\_resolvers.

The verifier did not have enough to check run\_language\_resolvers, 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 `paths` is annotated `Sequence` — outside the synthesizable primitive/collection set

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

Comment thread graphify/cache.py
return hashlib.sha256(f"{h}:{salt}".encode("utf-8")).hexdigest()


def load_cached(path: Path, root: Path = Path("."), kind: str = "ast",

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

fans out to 8 callees (efferent coupling); 48 callers depend on it (afferent coupling).

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

Comment thread graphify/cache.py
@@ -1029,9 +1042,13 @@ def load_cached(path: Path, root: Path = Path("."), kind: str = "ast",

def save_cached(path: Path, result: dict, root: Path = Path("."), kind: str = "ast",

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

fans out to 7 callees (efferent coupling); 24 callers depend on it (afferent coupling).

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

Comment thread graphify/detect.py
@@ -1514,6 +1517,9 @@ def _resolves_under_root(path: Path, root: Path) -> bool:

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); 110 callers depend on it (afferent coupling).

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

Comment thread graphify/extract.py
set_language_overrides(language_overrides)


def _extract_single_file(args: tuple) -> tuple[int, 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 regression_extract_single_file()

fans out to 7 callees (efferent coupling).

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

Comment thread graphify/rcfile.py
) from None


def load_graphifyrc(root: 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 regressionload_graphifyrc()

10 callers depend on it (afferent coupling).

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

Comment thread graphify/rcfile.py
return dict(_ACTIVE)


def activate_language_overrides(root: Path) -> dict[str, 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 regressionactivate_language_overrides()

7 callers depend on it (afferent coupling).

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

@andrebrait

Copy link
Copy Markdown

I'll patch this onto my own fork because I cannot wait for it to be ready and released. Let me know if you need help reviewing this.

andrebrait added a commit to pfBlockerNG/pfBlockerNG that referenced this pull request Aug 28, 2026
Graphify's suffix map sends .inc to the Pascal extractor, so this
repository's PHP includes -- pfblockerng.inc among them -- extract as 30
incidental nodes while extraction still reports success. Upstream fixes
that in Graphify-Labs/graphify#3075, which is unreleased, so vendor the
PR's code hunks and apply them to the installed package after every
install: scripts/agent/patch-graphify.sh derives the package directory
from the CLI's own interpreter, no-ops when the installed Graphify
already provides the override API, fails loudly rather than silently when
the patch does not apply, and purges the language-blind AST cache on
first application. All three install paths call it, .graphifyrc declares
language.inc=php, and the refreshed graph carries 767 .inc nodes.
andrebrait added a commit to pfBlockerNG/pfBlockerNG that referenced this pull request Aug 28, 2026
Graphify's suffix map sends .inc to the Pascal extractor, so this
repository's PHP includes -- pfblockerng.inc among them -- extract as 30
incidental nodes while extraction still reports success. Upstream fixes
that in Graphify-Labs/graphify#3075, which is unreleased, so vendor the
PR's code hunks and apply them to the installed package after every
install: scripts/agent/patch-graphify.sh derives the package directory
from the CLI's own interpreter, no-ops when the installed Graphify
already provides the override API, fails loudly rather than silently when
the patch does not apply, and purges the language-blind AST cache on
first application. All three install paths call it, .graphifyrc declares
language.inc=php, and the refreshed graph carries 767 .inc nodes.
andrebrait added a commit to pfBlockerNG/pfBlockerNG that referenced this pull request Aug 28, 2026
Graphify's suffix map sends .inc to the Pascal extractor, so this
repository's PHP includes -- pfblockerng.inc among them -- extract as 30
incidental nodes while extraction still reports success. Upstream fixes
that in Graphify-Labs/graphify#3075, which is unreleased, so vendor the
PR's code hunks and apply them to the installed package after every
install: scripts/agent/patch-graphify.sh derives the package directory
from the CLI's own interpreter, no-ops when the installed Graphify
already provides the override API, fails loudly rather than silently when
the patch does not apply, and purges the language-blind AST cache on
first application. All three install paths call it, .graphifyrc declares
language.inc=php, and the refreshed graph carries 767 .inc nodes.
andrebrait added a commit to pfBlockerNG/pfBlockerNG that referenced this pull request Aug 28, 2026
Graphify's suffix map sends .inc to the Pascal extractor, so this
repository's PHP includes -- pfblockerng.inc among them -- extract as 30
incidental nodes while extraction still reports success. Upstream fixes
that in Graphify-Labs/graphify#3075, which is unreleased, so vendor the
PR's code hunks and apply them to the installed package after every
install: scripts/agent/patch-graphify.sh derives the package directory
from the CLI's own interpreter, no-ops when the installed Graphify
already provides the override API, fails loudly rather than silently when
the patch does not apply, and purges the language-blind AST cache on
first application. All three install paths call it, .graphifyrc declares
language.inc=php, and the refreshed graph carries 767 .inc nodes.
abhay-codes07 and others added 2 commits September 12, 2026 23:14
…Graphify-Labs#2961)

`.inc` is hard-mapped to the Pascal extractor, but it is "include file" in
whatever language a project happens to use: PHP on pfSense, Pascal in a
Delphi tree, SQL or assembly elsewhere. A PHP `.inc` parsed as Pascal does
not fail — it yields a handful of incidental nodes, so the graph looks
populated while the shipped runtime is missing from it (7 nodes instead of
471 on the reporter's file). Hardcoding one more extension (Graphify-Labs#1042's fix)
cannot generalise, because `.inc` has no single correct global meaning.

`.graphifyrc` gains `language.<ext>=<language | .ext>`:

    language.inc=php
    language.tpl=.ts

The declaration reaches every place graphify keys a decision on the suffix:

  * detect.classify_file          — a declared extension counts as code
  * extract._get_extractor        — dispatch, ahead of the .h/.m sniffs
  * _lang_is_case_insensitive /   — cross-file resolution rules
    _lang_family / the PHP pass
  * resolver_registry             — the declared language's resolvers wake
  * cache.load_cached/save_cached — a `salt` folds the target language
    into the key, so the same bytes cached under the old extractor are
    never served for the new one
  * the extraction pool           — an initializer forwards the mapping to
    workers, which start with empty module state under `spawn`

The parser moves from hooks.py into a dependency-free graphify.rcfile so
detect/extract (and the workers) can read it; hooks._load_graphifyrc keeps
its name and behaviour. detect() and extract() activate `<root>/.graphifyrc`
themselves, so the CLI, `update`/`watch`, the hooks, the MCP server, and the
skill runbook all pick it up with no further wiring. A malformed file is
reported once on stderr and the scan continues with the defaults.
…ocess-global (Graphify-Labs#2961)

Review follow-up: the active overrides lived in a module-global dict, so two
extractions running concurrently in one process (an MCP server, a threaded
harness) with different roots would clobber each other — one scan's
activate_language_overrides overwrote the other's while effective_suffix /
classify_file / cache_salt read it, cross-contaminating which extractor an
ambiguous .inc/.m/.h file got. The overrides are now thread-local. The
dominant paths are unchanged: the CLI runs on the main thread, and a spawn
pool worker sets its own copy in _worker_init on the same main thread that
runs _extract_single_file.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JJbLfztSxm2tH5cJwBbe9q
@abhay-codes07
abhay-codes07 force-pushed the feat/graphifyrc-language-overrides branch from 560ca74 to 1ad08a9 Compare September 12, 2026 17:47
@abhay-codes07

Copy link
Copy Markdown
Contributor Author

All five findings converge on one real concern — the active overrides were process-global — and it's fixed in 1ad08a9 (rebased onto 0.9.59). The overrides now live in thread-local storage, so two extractions running concurrently in one process (an MCP server, a threaded harness) with different roots each keep their own map; one scan's activate_language_overrides can no longer clobber the other's while effective_suffix/classify_file/cache_salt read it. The dominant paths are unchanged: the CLI runs on the main thread, and a spawn pool worker sets its own copy in _worker_init on the same main thread that then runs _extract_single_file (spawn workers don't inherit module state, which is why the initializer forwards it explicitly). Two regression tests cover it — concurrent threads with different .inc maps keep their own effective_suffix, and a thread that never activated overrides stays clean while another has them live; both fail on the old module-global dict.

@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 .graphifyrc language.<ext>=<lang> mechanism that lets a project declare what an ambiguous extension (.inc, .h, .m, …) means, which now drives file classification, extractor dispatch, cross-file resolution (family/case-insensitivity), and the AST cache. A project-declared remap takes priority over all built-in sniffing in _get_extractor, and load_cached/save_cached accept a salt (via cache_salt) so a file re-parsed under a different language never reuses the entry produced by the old extractor. Overrides are activated in detect and, for direct library callers, in extract (which prints them), and are propagated to spawn-mode pool workers through _worker_init; a typo is reported once on stderr and the scan falls back to graphify's defaults.

Worth a look

  • Language overrides use process-global mutable state without synchronization across threadsgraphify/extract.py:6462 · Escalate · high
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • Language override activated in detect() but not in extract() cache key pathgraphify/cache.py:1014 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • Salt not applied to legacy-key lookup path in load_cachedgraphify/cache.py:1017 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • detect() leaves project language overrides active for later classify_file() callsgraphify/detect.py:1732 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • Language overrides are process-global during extract and race across concurrent callersgraphify/extract.py:6463 · 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 — 2856 functions depend on the 586 functions this change touches.

Health — this change adds coupling hotspots:

  • new: extract() — 588 callers, 47 callees
  • new: _rebuild_code() — 115 callers, 51 callees
  • new: detect() — 112 callers, 16 callees
  • new: save_semantic_cache() — 58 callers, 9 callees
  • new: _extract_generic() — 18 callers, 26 callees
  • new: save_manifest() — 40 callers, 11 callees
  • new: load_cached() — 48 callers, 8 callees
  • new: extract_files_direct() — 17 callers, 20 callees
  • …and 71 more — each is listed as a finding

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

Test selection

Test selection

274 of 274 test file(s) selected (100%) via static blast radius.

Escalated to a full run for safety — the selection is not trustworthy on its own (see below). CI should run the whole suite.

  • tests/test_affected_cli.py — impact, full-run-safety
  • tests/test_affected_member_seed.py — full-run-safety
  • tests/test_agents_platform.py — impact, full-run-safety
  • tests/test_analyze.py — full-run-safety
  • tests/test_anthropic_custom_endpoint.py — full-run-safety
  • tests/test_antigravity_install.py — full-run-safety
  • tests/test_apm_fallback_version.py — full-run-safety
  • tests/test_architecture_doc.py — full-run-safety
  • tests/test_astro_extraction.py — impact, full-run-safety
  • tests/test_astro_import_ids.py — impact, full-run-safety
  • tests/test_atomic_canvas_export.py — full-run-safety
  • tests/test_atomic_version_stamp.py — full-run-safety
  • tests/test_atomic_writes.py — impact, full-run-safety
  • tests/test_backend_env_isolation.py — full-run-safety
  • tests/test_backend_extras.py — full-run-safety
  • tests/test_benchmark.py — full-run-safety
  • tests/test_benchmark_raw_graph.py — full-run-safety
  • tests/test_build.py — impact, full-run-safety
  • tests/test_build_merge_dedup_scope.py — full-run-safety
  • tests/test_build_merge_hyperedges_and_prune.py — full-run-safety
  • tests/test_build_merge_shrink_guard.py — full-run-safety
  • tests/test_builtin_global_type_refs.py — impact, full-run-safety
  • tests/test_cache.py — impact, full-run-safety
  • tests/test_callflow_html.py — full-run-safety
  • tests/test_cargo_introspect.py — full-run-safety
  • tests/test_carried_hyperedge_remap.py — full-run-safety
  • tests/test_case_sensitive_resolution.py — impact, full-run-safety
  • tests/test_charmap_encoding.py — impact, full-run-safety
  • tests/test_chunking.py — impact, full-run-safety
  • tests/test_cjs_module_extension.py — impact, full-run-safety
  • tests/test_claude_cli_backend.py — impact, full-run-safety
  • tests/test_claude_md.py — full-run-safety
  • tests/test_cli_broken_pipe.py — full-run-safety
  • tests/test_cli_export.py — full-run-safety
  • tests/test_cli_help.py — full-run-safety
  • tests/test_cluster.py — full-run-safety
  • tests/test_codebuddy.py — impact, full-run-safety
  • tests/test_community_hub_labels.py — full-run-safety
  • tests/test_community_labels_skill.py — full-run-safety
  • tests/test_confidence.py — full-run-safety
  • tests/test_corrupt_graph_json.py — full-run-safety
  • tests/test_cpp_nested_and_cli.py — impact, full-run-safety
  • tests/test_cpp_objc_cross_file_calls.py — impact, full-run-safety
  • tests/test_cpp_preprocess.py — full-run-safety
  • tests/test_cross_extension_reexport_self_cycle.py — impact, full-run-safety
  • tests/test_cross_language_call_resolution.py — impact, full-run-safety
  • tests/test_cross_repo_member_calls.py — impact, full-run-safety
  • tests/test_cross_repo_shared_types.py — full-run-safety
  • tests/test_csharp_call_site_generic_args.py — impact, full-run-safety
  • tests/test_csharp_enum_members.py — impact, full-run-safety
  • … and 224 more

non-code file(s) changed (README.md) → running the full suite for safety (a code graph can't see config/fixture/data deps)

changed code file(s) with no mapped test (README.md) — a coverage gap or a missing link — running the full suite rather than only the selected tests

Selection is safe under the controlled-regression assumption; always-run tests + a periodic full run are the backstops. Advisory — it never changes the check verdict.

Formal verification

Could not verify: Could not verify load\_cached.

The verifier did not have enough to check load\_cached, 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 save\_cached.

The verifier did not have enough to check save\_cached, 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 classify\_file.

The verifier did not have enough to check classify\_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: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

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 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\_parallel.

The verifier did not have enough to check \_extract\_parallel, 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 \_extract\_sequential.

The verifier did not have enough to check \_extract\_sequential, 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 \_extract\_single\_file.

The verifier did not have enough to check \_extract\_single\_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: all 8 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly ValueError — names the real obstacle, not a sampling gap)

Could not verify: Could not verify \_get\_extractor.

The verifier did not have enough to check \_get\_extractor, 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 \_lang\_family.

The verifier did not have enough to check \_lang\_family, 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 `source_file` is annotated `object` — outside the synthesizable primitive/collection set

Could not verify: Could not verify \_lang\_is\_case\_insensitive.

The verifier did not have enough to check \_lang\_is\_case\_insensitive, 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 `source_file` is annotated `object` — outside the synthesizable primitive/collection set

Could not verify: Could not verify \_load\_graphifyrc.

The verifier did not have enough to check \_load\_graphifyrc, 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 run\_language\_resolvers.

The verifier did not have enough to check run\_language\_resolvers, 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 `paths` is annotated `Sequence` — outside the synthesizable primitive/collection set

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

Comment thread graphify/cache.py
return hashlib.sha256(f"{h}:{salt}".encode("utf-8")).hexdigest()


def load_cached(path: Path, root: Path = Path("."), kind: str = "ast",

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

fans out to 8 callees (efferent coupling); 48 callers depend on it (afferent coupling).

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

Comment thread graphify/cache.py
@@ -1069,9 +1082,13 @@ def load_cached(path: Path, root: Path = Path("."), kind: str = "ast",

def save_cached(path: Path, result: dict, root: Path = Path("."), kind: str = "ast",

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

fans out to 7 callees (efferent coupling); 24 callers depend on it (afferent coupling).

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

Comment thread graphify/detect.py
@@ -1726,6 +1729,9 @@ def _resolves_under_root(path: Path, root: Path) -> bool:

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); 112 callers depend on it (afferent coupling).

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

Comment thread graphify/extract.py
set_language_overrides(language_overrides)


def _extract_single_file(args: tuple) -> tuple[int, 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 regression_extract_single_file()

fans out to 7 callees (efferent coupling).

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

Comment thread graphify/rcfile.py
) from None


def load_graphifyrc(root: 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 regressionload_graphifyrc()

10 callers depend on it (afferent coupling).

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

Comment thread graphify/rcfile.py
return dict(_active())


def activate_language_overrides(root: Path) -> dict[str, 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 regressionactivate_language_overrides()

7 callers depend on it (afferent coupling).

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

@andrebrait

Copy link
Copy Markdown

@abhay-codes07 I reviewed 1ad08a9 and integrated it on top of 0.9.59 in our maintained fork. The thread-local fix works; the remaining issues were in consumers of the override state.

I recommend adopting or adapting these follow-up commits before merging:

  • 5c8aebc: carries the effective language through extractor grammar, cross-file resolution and cache bypass; adds declared JS/Python import candidates while preserving source paths and Python package scope.
  • 4815743: tracks language identity in incremental manifests, so config-only changes invalidate unchanged source bytes; fixes watcher configuration/source events, relative roots and semantic-work notifications.
  • a59563e: closes the reviewed C# partial/inherited/dispatch gaps, applies language eligibility to inferred native candidates and JS call scope, and processes root configuration events even when the file is excluded from the corpus.

Our earlier 1010f53 also validates explicit target suffixes (for example, .phhp must warn instead of silently disabling extraction) and fixes the README examples to use supported whole-line comments.

The commits include behavior regressions. Final focused suite: 124 passed; full suite: 5,720 passed, 14 existing skips. Fork CI passed on Python 3.10 and 3.12. Native file-format conventions, including Fortran .F90 preprocessing and C/C++ header/implementation pairing, remain unchanged.

The complete tested snapshot is v0.9.59-pfb.1; fork PR #10 contains the review and resolution evidence. The separate Leiden packaging patch in that snapshot is not required for these language-override fixes.

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

Labels

None yet

Projects

None yet

3 participants