diff --git a/graphify/report.py b/graphify/report.py index 673c2f2bb..0f1c5e709 100644 --- a/graphify/report.py +++ b/graphify/report.py @@ -137,6 +137,21 @@ def generate( f"- {detection_result['total_files']} files ยท ~{detection_result['total_words']:,} words", "- Verdict: corpus is large enough that graph structure adds value.", ] + # #3511: files detect() saw but could not classify (no supported + # extension/shebang) were counted nowhere -- a corpus that is mostly + # an unsupported language reported the same "well covered" verdict as + # one that was actually extracted. Surface the count and its biggest + # extensions so a near-total miss (e.g. a Lean/Zig/whatever repo with + # no matching extractor) is visible here instead of silent. + unclassified = detection_result.get("unclassified") or [] + if unclassified: + from collections import Counter as _Counter + ext_counts = _Counter(Path(p).suffix or "(none)" for p in unclassified) + top = ", ".join(f"{ext} {n}" for ext, n in ext_counts.most_common(3)) + lines.append( + f"- Unclassified: {len(unclassified)} file(s) not represented in " + f"the graph (top: {top})" + ) from .analyze import _is_file_node as _ifn diff --git a/tests/test_report.py b/tests/test_report.py index 03bc67acb..98f256907 100644 --- a/tests/test_report.py +++ b/tests/test_report.py @@ -29,6 +29,29 @@ def test_report_contains_corpus_check(): report = generate(G, communities, cohesion, labels, gods, surprises, detection, tokens, "./project") assert "## Corpus Check" in report + +def test_report_surfaces_unclassified_files(): + """#3511: detect() already tracks files it saw but could not classify + (no supported extension), but nothing surfaced them -- a corpus that is + mostly an unsupported language got the same "well covered" verdict as + one that was actually extracted.""" + G, communities, cohesion, labels, gods, surprises, detection, tokens = make_inputs() + detection = { + **detection, + "unclassified": ["Main.lean", "Util.lean", "a.toml", "b.toml", "c.toml", "readme"], + } + report = generate(G, communities, cohesion, labels, gods, surprises, detection, tokens, "./project") + assert "Unclassified: 6 file(s)" in report + assert ".lean 2" in report + assert ".toml 3" in report + + +def test_report_omits_unclassified_line_when_none(): + """Backward compatible: no unclassified files, no new line.""" + G, communities, cohesion, labels, gods, surprises, detection, tokens = make_inputs() + report = generate(G, communities, cohesion, labels, gods, surprises, detection, tokens, "./project") + assert "Unclassified:" not in report + def test_report_contains_god_nodes(): G, communities, cohesion, labels, gods, surprises, detection, tokens = make_inputs() report = generate(G, communities, cohesion, labels, gods, surprises, detection, tokens, "./project")