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):