diff --git a/graphify/report.py b/graphify/report.py index 5bac06d05..248bce9a1 100644 --- a/graphify/report.py +++ b/graphify/report.py @@ -82,6 +82,7 @@ def generate( min_community_size: int = 3, built_at_commit: str | None = None, learning: dict | None = None, + obsidian: bool = False, ) -> str: today = date.today().isoformat() @@ -140,14 +141,21 @@ def generate( "- Run `graphify update .` after code changes (no API cost).", ] - # Community hub navigation - links to _COMMUNITY_*.md files in the Obsidian vault. - # Without these, GRAPH_REPORT.md is a dead-end and the vault splits into disconnected components. + # Community hub navigation. The `_COMMUNITY_*.md` notes these wikilinks target + # are only created by the opt-in `--obsidian` export, and the report is written + # at build time (before any export runs), so emitting wikilinks by default left + # every link dangling — polluting an Obsidian vault's graph view and rendering as + # literal brackets everywhere else (#1712). Emit wikilinks only when the caller + # signals Obsidian output; otherwise a plain list, which navigates nowhere-to-break. if non_empty: lines += ["", "## Community Hubs (Navigation)"] for cid in non_empty: label = community_labels.get(cid, f"Community {cid}") - safe = _safe_community_name(label) - lines.append(f"- [[_COMMUNITY_{safe}|{label}]]") + if obsidian: + safe = _safe_community_name(label) + lines.append(f"- [[_COMMUNITY_{safe}|{label}]]") + else: + lines.append(f"- {label}") lines += [ "", diff --git a/tests/test_report.py b/tests/test_report.py index 00be0f36d..767e2ba34 100644 --- a/tests/test_report.py +++ b/tests/test_report.py @@ -135,3 +135,22 @@ def test_import_cycles_section_absent_for_documents_only_corpus(): tokens = {"input": 0, "output": 0} report = generate(G, communities, cohesion, labels, gods, surprises, detection, tokens, "./project") assert "## Import Cycles" not in report + + +def test_report_hubs_are_plain_text_by_default(): + # #1712: without --obsidian the _COMMUNITY_*.md notes don't exist, so wikilinks + # would dangle (and pollute an Obsidian vault's graph view). Default to plain text. + G, communities, cohesion, labels, gods, surprises, detection, tokens = make_inputs() + labels = {cid: f"Widget {cid}" for cid in communities} + report = generate(G, communities, cohesion, labels, gods, surprises, detection, tokens, "./project", min_community_size=1) + assert "## Community Hubs (Navigation)" in report + assert "[[_COMMUNITY_" not in report, "must not emit dangling Obsidian wikilinks by default (#1712)" + assert any(f"- Widget {cid}" in report for cid in communities) + + +def test_report_hubs_use_wikilinks_when_obsidian(): + # The opt-in path keeps the vault-navigable wikilink form. + G, communities, cohesion, labels, gods, surprises, detection, tokens = make_inputs() + labels = {cid: f"Widget {cid}" for cid in communities} + report = generate(G, communities, cohesion, labels, gods, surprises, detection, tokens, "./project", min_community_size=1, obsidian=True) + assert "[[_COMMUNITY_" in report