From 900b376d39446ef79dbc60c528c985847fc69389 Mon Sep 17 00:00:00 2001 From: ayushcodes10 Date: Sat, 12 Sep 2026 23:23:36 +0530 Subject: [PATCH] Exempt Office/Workspace sidecars from the ignore check detect() converts Office files and Google Workspace shortcuts into markdown sidecars under a converted subdirectory of the output dir, then ran the same scan ignore check on the sidecar's own path. The documented gitignore advice puts the whole output dir inside a gitignored tree, so that check rejected the tool's own output for the same reason a user would gitignore it, and the source document vanished from the corpus with nothing recorded about why. The check exists to keep user files out of the scan, not to filter output this same pass just produced from an already admitted source, so a sidecar under the converted directory is now exempt from it. Fixes #3504. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh --- graphify/detect.py | 15 +++++++++++-- tests/test_detect.py | 52 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 2 deletions(-) diff --git a/graphify/detect.py b/graphify/detect.py index 340b59576..287b0f431 100644 --- a/graphify/detect.py +++ b/graphify/detect.py @@ -1968,7 +1968,15 @@ def detect(root: Path, *, follow_symlinks: bool | None = None, google_workspace: skipped_sensitive.append(str(p) + f" [Google Workspace export failed: {exc}]") continue if md_path: - if _ignored_for_scan(md_path): + # #3504: the sidecar lands under converted_dir, which the + # documented .gitignore advice puts inside a gitignored + # graphify-out/ -- an ignore check here would reject the + # tool's own output for the same reason it should be + # gitignored in the first place, silently dropping the + # source document from the corpus. The ignore check exists + # to keep USER files out of the scan, not to filter output + # this same pass just produced from an already-admitted file. + if _ignored_for_scan(md_path) and not md_path.is_relative_to(converted_dir): continue files[ftype].append(str(md_path)) total_words += _wc(md_path) @@ -1979,7 +1987,10 @@ def detect(root: Path, *, follow_symlinks: bool | None = None, google_workspace: if p.suffix.lower() in OFFICE_EXTENSIONS: md_path = convert_office_file(p, converted_dir, root=root) if md_path: - if _ignored_for_scan(md_path): + # #3504: see the matching comment in the Google Workspace + # branch above -- same sidecar-under-a-gitignored-output-dir + # trap, same exemption. + if _ignored_for_scan(md_path) and not md_path.is_relative_to(converted_dir): continue files[ftype].append(str(md_path)) total_words += _wc(md_path) diff --git a/tests/test_detect.py b/tests/test_detect.py index 1bf6b056b..3a2635769 100644 --- a/tests/test_detect.py +++ b/tests/test_detect.py @@ -872,6 +872,58 @@ def test_detect_converts_google_workspace_shortcuts_when_enabled(tmp_path, monke assert result["total_words"] > 0 +def test_detect_office_sidecar_survives_a_gitignored_output_dir(tmp_path, monkeypatch): + """#3504: the documented .gitignore advice puts graphify-out/ (and so + graphify-out/converted/, where Office sidecars land) inside a gitignored + tree. The ignore check exists to keep USER files out of the scan, not to + filter output this same pass just produced from an already-admitted + source file -- so a sidecar landing under converted/ must survive it, + or every .docx/.xlsx silently vanishes from the corpus the moment a repo + follows that advice.""" + (tmp_path / ".gitignore").write_text("graphify-out/\n", encoding="utf-8") + src = tmp_path / "report.docx" + src.write_text("placeholder", encoding="utf-8") + + def fake_convert(path, out_dir, root=None): + out_dir.mkdir(parents=True, exist_ok=True) + out = out_dir / "report_converted.md" + out.write_text("# Report\n\nConverted content.", encoding="utf-8") + return out + + monkeypatch.setattr("graphify.detect.convert_office_file", fake_convert) + + result = detect(tmp_path) + + assert len(result["files"]["document"]) == 1, ( + "the Office sidecar was dropped by the gitignore check on the tool's own output dir" + ) + assert result["files"]["document"][0].endswith("report_converted.md") + assert result["total_words"] > 0 + + +def test_detect_google_workspace_sidecar_survives_a_gitignored_output_dir(tmp_path, monkeypatch): + """Same trap as the Office sidecar case (#3504), for the Google Workspace + conversion branch, which writes into the same converted/ directory.""" + (tmp_path / ".gitignore").write_text("graphify-out/\n", encoding="utf-8") + shortcut = tmp_path / "notes.gdoc" + shortcut.write_text('{"doc_id":"doc-1"}', encoding="utf-8") + + def fake_convert(path, out_dir, *, xlsx_to_markdown=None, root=None): + out_dir.mkdir(parents=True, exist_ok=True) + out = out_dir / "notes_converted.md" + out.write_text("# Notes\n\nA converted Google Doc.", encoding="utf-8") + return out + + monkeypatch.setattr("graphify.detect.convert_google_workspace_file", fake_convert) + + result = detect(tmp_path, google_workspace=True) + + assert len(result["files"]["document"]) == 1, ( + "the Google Workspace sidecar was dropped by the gitignore check on the tool's own output dir" + ) + assert result["files"]["document"][0].endswith("notes_converted.md") + + def test_detect_includes_video_key(tmp_path): """detect() result always includes a 'video' key even with no video files.""" (tmp_path / "main.py").write_text("x = 1")