From 86856491963d0ff8ff986a9f2f848af31f2fa511 Mon Sep 17 00:00:00 2001 From: vitali87 Date: Tue, 25 Aug 2026 17:06:20 +0100 Subject: [PATCH 1/8] test: assert the suffix-distinct names, not merely that they differ The suffix collision test checked only distinctness, which is the same predicate the fix establishes: a rule that mangled the stem would still produce two different names and pass. Now asserts the qualified names each file actually receives. Verified it fails against a deliberately mangled stem, which the uniqueness-only form accepted. --- codebase_rag/tests/test_document_tier.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/codebase_rag/tests/test_document_tier.py b/codebase_rag/tests/test_document_tier.py index 45aff3c61..ed0509eba 100644 --- a/codebase_rag/tests/test_document_tier.py +++ b/codebase_rag/tests/test_document_tier.py @@ -321,13 +321,22 @@ def test_same_stem_with_different_suffixes_stays_separate( tmp_path, {"docs/guide.md": "# Alpha\n", "docs/guide.markdown": "# Alpha\n"}, ) - modules = [ - p + # Asserts the qualified names each file actually gets, not merely + # that they differ: a rule that mangled the stem would still produce + # two distinct names and satisfy a uniqueness-only check. + by_path = { + str(p[cs.KEY_PATH]): p[cs.KEY_QUALIFIED_NAME] for p in _nodes(mock, MODULE) if str(p.get(cs.KEY_PATH, "")).startswith("docs/guide") - ] - assert len({p[cs.KEY_QUALIFIED_NAME] for p in modules}) == 2 - assert len(_qns(mock, SECTION)) == 2 + } + assert by_path == { + "docs/guide.md": f"{tmp_path.name}.docs.guide_md", + "docs/guide.markdown": f"{tmp_path.name}.docs.guide_markdown", + } + assert _qns(mock, SECTION) == { + f"{tmp_path.name}.docs.guide_md.Alpha", + f"{tmp_path.name}.docs.guide_markdown.Alpha", + } def test_nested_directory_paths_recorded(self, tmp_path: Path) -> None: mock = _run(tmp_path, {"docs/guide/plan.md": "# Deep\n"}) From ab401ec1a8e6a867fd6aa7fd7353e22c1e7df68f Mon Sep 17 00:00:00 2001 From: vitali87 Date: Tue, 25 Aug 2026 17:11:12 +0100 Subject: [PATCH 2/8] test: constrain whitespace collapsing and the untitled fallback Found by mutation rather than inspection: replacing the whitespace collapse with a plain strip, and dropping the (untitled) fallback, both left all 34 tests passing. Two deliberate documented behaviours that nothing actually held in place. Killing a mutant does not require conceiving of the failure mode first, which is what makes it catch the case where a test was written from the same premise as the code. --- codebase_rag/tests/test_document_tier.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/codebase_rag/tests/test_document_tier.py b/codebase_rag/tests/test_document_tier.py index ed0509eba..c63d29130 100644 --- a/codebase_rag/tests/test_document_tier.py +++ b/codebase_rag/tests/test_document_tier.py @@ -275,6 +275,24 @@ def test_dots_in_a_heading_do_not_create_phantom_levels( # The display name keeps the dots. assert _node_names(mock, SECTION) == {"Release 1.2.3"} + def test_runs_of_whitespace_collapse_in_the_qualified_name( + self, tmp_path: Path + ) -> None: + # So the same heading reflowed across lines keeps one identity. The + # display name keeps the original spacing. + mock = _run(tmp_path, {"w.md": "# Alpha Beta\n"}) + assert _qns(mock, SECTION) == {f"{_module_qn(tmp_path, 'w.md')}.Alpha Beta"} + assert _node_names(mock, SECTION) == {"Alpha Beta"} + + def test_heading_with_no_text_gets_a_placeholder_name( + self, tmp_path: Path + ) -> None: + # A bare "##" has nothing to name a node after; an empty name would + # produce a qualified name ending in a bare separator. + mock = _run(tmp_path, {"e.md": "# Top\n\n##\n"}) + assert "(untitled)" in _node_names(mock, SECTION) + assert f"{_module_qn(tmp_path, 'e.md')}.Top.(untitled)" in _qns(mock, SECTION) + def test_heading_literally_containing_the_marker_stays_distinct( self, tmp_path: Path ) -> None: From 21a7b1791ce303df66e476430b29dffa38d7baf3 Mon Sep 17 00:00:00 2001 From: vitali87 Date: Tue, 25 Aug 2026 17:11:35 +0100 Subject: [PATCH 3/8] style: apply ruff format --- codebase_rag/tests/test_document_tier.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/codebase_rag/tests/test_document_tier.py b/codebase_rag/tests/test_document_tier.py index c63d29130..a0630551f 100644 --- a/codebase_rag/tests/test_document_tier.py +++ b/codebase_rag/tests/test_document_tier.py @@ -284,9 +284,7 @@ def test_runs_of_whitespace_collapse_in_the_qualified_name( assert _qns(mock, SECTION) == {f"{_module_qn(tmp_path, 'w.md')}.Alpha Beta"} assert _node_names(mock, SECTION) == {"Alpha Beta"} - def test_heading_with_no_text_gets_a_placeholder_name( - self, tmp_path: Path - ) -> None: + def test_heading_with_no_text_gets_a_placeholder_name(self, tmp_path: Path) -> None: # A bare "##" has nothing to name a node after; an empty name would # produce a qualified name ending in a bare separator. mock = _run(tmp_path, {"e.md": "# Top\n\n##\n"}) From 4e45c74b1b783ba0b11df08cf95941c11d247df3 Mon Sep 17 00:00:00 2001 From: vitali87 Date: Tue, 25 Aug 2026 17:17:12 +0100 Subject: [PATCH 4/8] test: pin that distinguish_suffix stays opt-in for other tiers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Another mutation survivor: flipping the default to True passed all 45 tests. The opt-in-ness is the whole reason the ast-grep tier's names are unchanged by #1428 — an argument I made in review and nothing enforced. Ruby still yields .app, not app_rb. Changing that is #1429's job, deliberately and with its own review. --- codebase_rag/tests/test_document_tier.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/codebase_rag/tests/test_document_tier.py b/codebase_rag/tests/test_document_tier.py index a0630551f..a87fbcec4 100644 --- a/codebase_rag/tests/test_document_tier.py +++ b/codebase_rag/tests/test_document_tier.py @@ -363,6 +363,15 @@ def test_non_markdown_files_produce_no_sections(self, tmp_path: Path) -> None: mock = _run(tmp_path, {"mod.py": "def f():\n return 1\n"}) assert _nodes(mock, SECTION) == [] + def test_other_tiers_keep_suffixless_module_names(self, tmp_path: Path) -> None: + # `distinguish_suffix` is opt-in precisely so the ast-grep tier's + # qualified names do not change (issue #1429 tracks doing that + # deliberately). Without this, flipping the default to True passes + # every other test in the suite. + mock = _run(tmp_path, {"app.rb": "def hi\n 1\nend\n"}) + assert f"{tmp_path.name}.app" in _qns(mock, MODULE) + assert f"{tmp_path.name}.app_rb" not in _qns(mock, MODULE) + def _export_index(tmp_path: Path, document: str = NESTED): """Index a document through the protobuf sink and read the artifact back.""" From 73c974756e2ffa1643e7430037cd95be5ef2f016 Mon Sep 17 00:00:00 2001 From: vitali87 Date: Tue, 25 Aug 2026 17:18:42 +0100 Subject: [PATCH 5/8] Revert "test: pin that distinguish_suffix stays opt-in for other tiers" This reverts commit 4e45c74b1b783ba0b11df08cf95941c11d247df3. --- codebase_rag/tests/test_document_tier.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/codebase_rag/tests/test_document_tier.py b/codebase_rag/tests/test_document_tier.py index a87fbcec4..a0630551f 100644 --- a/codebase_rag/tests/test_document_tier.py +++ b/codebase_rag/tests/test_document_tier.py @@ -363,15 +363,6 @@ def test_non_markdown_files_produce_no_sections(self, tmp_path: Path) -> None: mock = _run(tmp_path, {"mod.py": "def f():\n return 1\n"}) assert _nodes(mock, SECTION) == [] - def test_other_tiers_keep_suffixless_module_names(self, tmp_path: Path) -> None: - # `distinguish_suffix` is opt-in precisely so the ast-grep tier's - # qualified names do not change (issue #1429 tracks doing that - # deliberately). Without this, flipping the default to True passes - # every other test in the suite. - mock = _run(tmp_path, {"app.rb": "def hi\n 1\nend\n"}) - assert f"{tmp_path.name}.app" in _qns(mock, MODULE) - assert f"{tmp_path.name}.app_rb" not in _qns(mock, MODULE) - def _export_index(tmp_path: Path, document: str = NESTED): """Index a document through the protobuf sink and read the artifact back.""" From 865043e8688b5bb8189875ef24b43869033c2b5e Mon Sep 17 00:00:00 2001 From: vitali87 Date: Tue, 25 Aug 2026 17:50:44 +0100 Subject: [PATCH 6/8] test: exercise a heading whose text really spans lines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The comment claimed the test protected identity for a heading reflowed across lines, but the fixture was one physical line with repeated spaces — it exercised space collapsing, not reflow. A setext heading's text genuinely spans lines, so that case now has its own test: the display name keeps the newline, the qualified name collapses it. --- codebase_rag/tests/test_document_tier.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/codebase_rag/tests/test_document_tier.py b/codebase_rag/tests/test_document_tier.py index a0630551f..9cdbe1981 100644 --- a/codebase_rag/tests/test_document_tier.py +++ b/codebase_rag/tests/test_document_tier.py @@ -278,12 +278,22 @@ def test_dots_in_a_heading_do_not_create_phantom_levels( def test_runs_of_whitespace_collapse_in_the_qualified_name( self, tmp_path: Path ) -> None: - # So the same heading reflowed across lines keeps one identity. The - # display name keeps the original spacing. + # Repeated spaces within one heading line. The display name keeps the + # original spacing; only the qualified name collapses. mock = _run(tmp_path, {"w.md": "# Alpha Beta\n"}) assert _qns(mock, SECTION) == {f"{_module_qn(tmp_path, 'w.md')}.Alpha Beta"} assert _node_names(mock, SECTION) == {"Alpha Beta"} + def test_heading_text_spanning_lines_keeps_one_identity( + self, tmp_path: Path + ) -> None: + # A setext heading's text really can span physical lines, which the + # single-line fixture above does not exercise. The newline must not + # reach the qualified name, or reflowing a heading would rename it. + mock = _run(tmp_path, {"r.md": "Alpha\nBeta\n=====\n"}) + assert _qns(mock, SECTION) == {f"{_module_qn(tmp_path, 'r.md')}.Alpha Beta"} + assert _node_names(mock, SECTION) == {"Alpha\nBeta"} + def test_heading_with_no_text_gets_a_placeholder_name(self, tmp_path: Path) -> None: # A bare "##" has nothing to name a node after; an empty name would # produce a qualified name ending in a bare separator. From 592223472733aa0f96c0fe03501f29719d028d67 Mon Sep 17 00:00:00 2001 From: vitali87 Date: Tue, 25 Aug 2026 23:23:44 +0100 Subject: [PATCH 7/8] test: write fixtures with explicit newlines so CRLF platforms agree --- codebase_rag/tests/test_document_tier.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/codebase_rag/tests/test_document_tier.py b/codebase_rag/tests/test_document_tier.py index 9cdbe1981..26fb73d87 100644 --- a/codebase_rag/tests/test_document_tier.py +++ b/codebase_rag/tests/test_document_tier.py @@ -47,7 +47,12 @@ def _run(tmp_path: Path, files: dict[str, str]) -> MagicMock: for rel, content in files.items(): path = tmp_path / rel path.parent.mkdir(parents=True, exist_ok=True) - path.write_text(content, encoding="utf-8") + # newline="" disables the platform newline translation write_text + # applies by default: on Windows a "\n" in a fixture would reach disk + # as "\r\n", so a heading spanning lines would parse as "Alpha\r\nBeta" + # and any assertion naming the literal text would fail there only. + # Fixtures are byte-for-byte what the test wrote on every platform. + path.write_text(content, encoding="utf-8", newline="") mock = MagicMock() GraphUpdater( ingestor=mock, repo_path=tmp_path, parsers=parsers, queries=queries From a869a64ddcf0d82f41de750af30a2fa97c360464 Mon Sep 17 00:00:00 2001 From: vitali87 Date: Tue, 25 Aug 2026 23:51:51 +0100 Subject: [PATCH 8/8] test: pin the collision suffix to the start line, not mere distinctness --- codebase_rag/tests/test_document_tier.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/codebase_rag/tests/test_document_tier.py b/codebase_rag/tests/test_document_tier.py index 26fb73d87..1f612fc6d 100644 --- a/codebase_rag/tests/test_document_tier.py +++ b/codebase_rag/tests/test_document_tier.py @@ -316,6 +316,18 @@ def test_heading_literally_containing_the_marker_stays_distinct( doc = "# Top\n\n## Notes\n\n## Notes@9\n\nx\n\n## Notes\n" mock = _run(tmp_path, {"marker.md": doc}) qns = _qns(mock, SECTION) + # Assert the NAMES, not merely that they are distinct: distinctness is + # the property the suffixing establishes, so any scheme that separates + # them satisfies it — a counter would pass this test while renaming + # the third section "Notes@1", which points at no line in the file. + # The suffix has to be the start line for the name to stay meaningful. + top = f"{_module_qn(tmp_path, 'marker.md')}.Top" + assert qns == { + top, + f"{top}.Notes", + f"{top}.Notes@9", + f"{top}.Notes@9@9", + }, f"unexpected qualified names: {sorted(qns)}" assert len(qns) == len(_nodes(mock, SECTION)), ( f"qualified names collided, sections merged: {sorted(qns)}" )