test(watch): assert update writes community_name (#1855 followup); changelog

Adds the regression test the #1855 fix was missing: _rebuild_code must
produce graph.json whose clustered nodes carry community_name, guarding
against the label-stripping regression recurring. Also records #1847 and
#1855 in the 0.9.15 changelog.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
safishamsi
2026-07-13 18:49:25 +01:00
co-authored by Claude Opus 4.8
parent 6eb5923404
commit 961b78e57a
2 changed files with 32 additions and 0 deletions
+4
View File
@@ -4,6 +4,10 @@ Full release notes with details on each version: [GitHub Releases](https://githu
## 0.9.15 (2026-07-13)
- Fix: detection now honors nested `.gitignore`/`.graphifyignore` files below the scan root, not just those at the scan root and above (#1847, thanks @Mohak-Agrawal). git applies a `.gitignore` to everything under its own directory, but graphify only loaded ignore files from the VCS-root-down-to-scan-root chain — so a `vendor/sub/.gitignore` deeper in the tree was never read and its exclusions leaked into the graph. Each directory's own ignore files are now read during the walk and anchored to that directory, preserving last-match-wins precedence (nearer files win, including over `.git/info/exclude`) and the parent-exclusion rule.
- Fix: `graphify update` now writes human-readable `community_name` labels, not just numeric community ids (#1808 / #1855, thanks @latreon). The incremental rebuild in `_rebuild_code` called `to_json` without `community_labels`, so every `update`/hook rebuild stripped the labels a `cluster-only` pass had written; a follow-up `cluster-only` restored them and the next rebuild stripped them again. The rebuild now forwards the labels it already computes (hub-derived for a code-only pass) and strips `community_name` from the topology fast-path comparison so the new field doesn't force a needless re-cluster.
- Security: fix a stored XSS and broken neighbor links in the exported `graph.html` (#1838, thanks @edgestack-ai). The report's neighbor "focus" links dropped an unescaped `JSON.stringify(nid)` into a double-quoted inline `onclick`. Because the stringified value carries its own quotes, the attribute was truncated on every node — so the links never worked — and a node `id`/`label` containing a double-quote broke out of the attribute and injected live handlers. AST node ids are `[a-z0-9_]`-safe, but ids/labels from documents or from titles scraped via `graphify add <url>` are not, so a hostile source could plant an executable handler into a report opened locally. The id is now carried in an HTML-escaped `data-nid` attribute and dispatched through a single delegated listener, closing the injection and repairing the links.
## 0.9.14 (2026-07-12)
+28
View File
@@ -160,6 +160,34 @@ def test_graphify_root_preserves_relative_when_invoked_with_relative_path(tmp_pa
)
def test_rebuild_code_writes_community_name(tmp_path):
"""#1808: `graphify update` / _rebuild_code must forward community_labels to
to_json, so graph.json nodes carry a human-readable community_name (hub-derived
for a code-only rebuild) — not just a numeric community id. Before the fix,
_rebuild_code called to_json without community_labels, so the labels a
cluster-only pass writes were stripped again on every incremental rebuild."""
import json
from graphify.watch import _rebuild_code
corpus = tmp_path / "corpus"
corpus.mkdir()
(corpus / "a.py").write_text(
"def alpha():\n return beta()\n\ndef beta():\n return 1\n", encoding="utf-8"
)
(corpus / "b.py").write_text(
"import a\n\ndef gamma():\n return a.alpha()\n", encoding="utf-8"
)
assert _rebuild_code(corpus, acquire_lock=False) is True
graph = json.loads((corpus / "graphify-out" / "graph.json").read_text(encoding="utf-8"))
clustered = [n for n in graph["nodes"] if n.get("community") is not None]
assert clustered, "expected clustered nodes in the rebuilt graph"
assert all(n.get("community_name") for n in clustered), (
"clustered nodes missing community_name — the update rebuild stripped the "
"labels that cluster-only writes (#1808)"
)
def test_graphify_root_preserves_absolute_when_user_supplied(tmp_path):
"""When the caller supplies an absolute path, ``.graphify_root`` stores
that absolute form verbatim — preserving explicit-absolute intent."""