fix(report): don't emit dangling [[_COMMUNITY_*]] wikilinks by default (#1712)

GRAPH_REPORT.md rendered the Community Hubs section as Obsidian wikilinks, but
the `_COMMUNITY_*.md` notes they target are only created by the opt-in
`--obsidian` export — and the report is written at build time, before any export
runs. So on a default run every link dangled: inside an Obsidian vault they
spawned phantom `_COMMUNITY_*` nodes in the graph view, and outside Obsidian they
rendered as literal brackets that navigate nowhere.

`generate()` now takes `obsidian: bool = False`; by default the hubs render as a
plain list, and the wikilink form is emitted only when a caller opts in. The
Obsidian export's own community notes already cross-link each other, so the vault
stays navigable without the report's links. Mirrors the #1444/#1465 portability
fix that was applied to `export wiki`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
safishamsi
2026-07-07 10:57:52 +01:00
co-authored by Claude Opus 4.8
parent f5d50adbd0
commit f84a0af1bf
2 changed files with 31 additions and 4 deletions
+12 -4
View File
@@ -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 += [
"",
+19
View File
@@ -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