From cd8552242c2c26b8711bf2aa7daa7780ffada3be Mon Sep 17 00:00:00 2001 From: ayushcodes10 Date: Sat, 12 Sep 2026 18:59:04 +0530 Subject: [PATCH] Report unclassified files from graphify update too graphify extract has printed this since #1692, but the update and watch rebuild path never did, so a corpus with no extractor for its language, Lean 4 in the reported case, rebuilt successfully with those files completely absent and nothing said about it. Prints the same wording from the extract command, and threads the count into the local detection summary the rebuild path already builds for GRAPH_REPORT.md, which previously dropped it. Fixes #3511. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh (cherry picked from commit 120405673f7b35840158028d14bb54a71a89e763) --- graphify/watch.py | 15 +++++++++++++++ tests/test_watch.py | 19 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/graphify/watch.py b/graphify/watch.py index 58028eae0..5d8c07080 100644 --- a/graphify/watch.py +++ b/graphify/watch.py @@ -1428,6 +1428,20 @@ def _rebuild_code( ) code_files = [Path(f) for f in detected['files']['code']] + # #3511: `graphify extract` has surfaced files it saw but could not + # classify since #1692; this update/watch rebuild path never did, + # so a corpus in a language with no extractor (no supported + # extension or shebang) rebuilt "successfully" with those files + # silently absent from the graph. Same wording as the extract path. + _unclassified = detected.get("unclassified", []) if isinstance(detected, dict) else [] + if _unclassified: + _names = ", ".join(sorted({Path(p).name for p in _unclassified})[:6]) + _more = f" (+{len(_unclassified) - 6} more)" if len(_unclassified) > 6 else "" + print( + f"[graphify watch] {len(_unclassified)} file(s) not classified " + f"(no supported extension or shebang), skipped: {_names}{_more}" + ) + # #2495: hand reconcile the same ignore decisions the detect() call # above made, so a newly-ignored file that still exists on disk is # purged from the graph instead of preserved forever by the fail-closed @@ -1876,6 +1890,7 @@ def _rebuild_code( "files": {"code": [str(f) for f in code_files], "document": [], "paper": [], "image": []}, "total_files": len(code_files), "total_words": detected.get("total_words", 0), + "unclassified": detected.get("unclassified", []), } # Inherit the existing graph's directed flag (#2342) so `graphify diff --git a/tests/test_watch.py b/tests/test_watch.py index da7d05933..412edb218 100644 --- a/tests/test_watch.py +++ b/tests/test_watch.py @@ -133,6 +133,25 @@ def test_doc_only_deletion_full_rebuild_evicts_md_nodes(tmp_path): assert "run()" in labels +def test_rebuild_code_reports_unclassified_files(tmp_path, capsys): + """#3511: `graphify extract` has surfaced files it saw but could not + classify (no supported extension/shebang) since #1692; the update/watch + rebuild path never did, so a corpus mostly in an unsupported language + (e.g. Lean, per the report) rebuilt "successfully" with those files + silently absent and nothing said about it.""" + corpus = tmp_path / "corpus" + corpus.mkdir() + (corpus / "app.py").write_text("def run(): pass\n", encoding="utf-8") + (corpus / "Main.lean").write_text("def main := 0\n", encoding="utf-8") + (corpus / "Util.lean").write_text("def util := 1\n", encoding="utf-8") + + assert _rebuild_code(corpus, acquire_lock=False) is True + out = capsys.readouterr().out + assert "2 file(s) not classified" in out + assert "Main.lean" in out + assert "Util.lean" in out + + # --- watch() import error without watchdog --- def test_check_update_no_flag_returns_true(tmp_path):