wiki: escape brackets in raw article labels (#3547) - #3550
ayushcodes10 wants to merge 4 commits into
Conversation
There was a problem hiding this comment.
Graphify reviewed this change.
Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).
Formal verification. 1 change(s) alter behavior, breaking input(s) attached.
Behavior changes: \_community\_article changes behavior, here is the input that shows it.
The verifier found a concrete input on which \_community\_article behaves differently before and after the change. If that change is intended, ship it; if not, this is your bug.
Guarantee: This difference was REPRODUCED, the verifier actually ran both versions on that input and saw them disagree. It is real, not an artifact.
Evidence: On input \{"G":"\(lambda \_g: \(\_g\.add\_nodes\_from\(\[\(1, \{\}\), \(2, \{\}\), \(3, \{\}\)\]\), \_g\.add\_edges\_from\(\[\(1, 2, \{\}\), \(1, 3, \{\}\), \(2, 3, \{\}\)\]\), \_g\)\[\-1\]\)\(\_\_import\_\_\('networkx'\)\.Graph\(\)\)","cid":"''","nodes":"\[1, 2, 3\]","label":"'h\\u00e9llo w\\u00f6rld'","labels":"\{'a': 1, 'b': 2\}","cohesion":"100\.0","node\_community":"\{'a': None\}","resolver":"\{'a':…, the old code produced '\# héllo wörld\\n\\n\> 3 nodes · cohesion 100\.00\\n\\n\#\# Key Concepts\\n\\n\- \*\*1\*\* \(2 connections\)\\n\- \*\*2\*\* \(2 connections\)\\n\- \*\*3\*\* \(2 connections\)\\n\\n\#\# Relationships… but the new code produces raises AttributeError. Paste that input straight into a regression test.
Graphify review — findings
Escapes [/] in every label printed directly into a generated wiki article — community titles, "Key Concepts" node listings, and god node titles — so source content that literally contains [[...]] renders as plain text instead of a dead wikilink. Factors the escaping already inlined in _md_link into a shared _escape_md_brackets helper and routes the three previously-unescaped print sites through it. Adds regression tests covering each site plus an end-to-end export check that no page retains a literal [[...]].
No blocking issues surfaced. 2 lower-confidence candidates did not survive cross-model review.
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 249 functions depend on the 23 functions this change touches.
Health — this change adds coupling hotspots:
- new:
to_wiki()— 43 callers, 7 callees - new:
dispatch_command()— 2 callers, 124 callees - new:
_god_node_article()— 8 callers, 3 callees - new:
_community_article()— 5 callers, 3 callees - new:
test_poisoned_manifest_is_healed()— 0 callers, 6 callees
Verification — 249 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 75 function(s) in the blast radius were not formally verified this run
Test selection
Test selection
5 of 276 test file(s) selected (2%) via static blast radius.
tests/test_export_path_length.py— impacttests/test_wiki.py— impacttests/test_wiki_label_bracket_escaping.py— impact, changed-testtests/test_wiki_link_filename_parity.py— impacttests/test_wiki_truncation_indicator.py— impact
Selection is safe under the controlled-regression assumption; always-run tests + a periodic full run are the backstops. Advisory — it never changes the check verdict.
Formal verification
Behavior changes: \_community\_article changes behavior, here is the input that shows it.
The verifier found a concrete input on which \_community\_article behaves differently before and after the change. If that change is intended, ship it; if not, this is your bug.
Guarantee: This difference was REPRODUCED, the verifier actually ran both versions on that input and saw them disagree. It is real, not an artifact.
Evidence: On input \{"G":"\(lambda \_g: \(\_g\.add\_nodes\_from\(\[\(1, \{\}\), \(2, \{\}\), \(3, \{\}\)\]\), \_g\.add\_edges\_from\(\[\(1, 2, \{\}\), \(1, 3, \{\}\), \(2, 3, \{\}\)\]\), \_g\)\[\-1\]\)\(\_\_import\_\_\('networkx'\)\.Graph\(\)\)","cid":"''","nodes":"\[1, 2, 3\]","label":"'h\\u00e9llo w\\u00f6rld'","labels":"\{'a': 1, 'b': 2\}","cohesion":"100\.0","node\_community":"\{'a': None\}","resolver":"\{'a':…, the old code produced '\# héllo wörld\\n\\n\> 3 nodes · cohesion 100\.00\\n\\n\#\# Key Concepts\\n\\n\- \*\*1\*\* \(2 connections\)\\n\- \*\*2\*\* \(2 connections\)\\n\- \*\*3\*\* \(2 connections\)\\n\\n\#\# Relationships… but the new code produces raises AttributeError. Paste that input straight into a regression test.
Could not verify: Could not verify \_god\_node\_article.
The verifier did not have enough to check \_god\_node\_article, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: non-vacuity: domain too small (only 2 distinct inputs exercised, need 3) — 'no divergence' would be near-vacuous
No difference found (not proven): No behavior difference found in \_md\_link (not a proof).
The verifier ran both versions of \_md\_link on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.
Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.
Note: An input the sampler did not try could still differ.
· 1 grounded finding(s) anchored inline below; 4 more finding(s) on lines outside this diff (see the check run).
| @@ -185,7 +199,7 @@ def _community_article( | |||
| def _god_node_article(G: nx.Graph, nid: str, labels: dict[int, str], node_community: dict[str, int] | None = None, resolver: dict[str, str] | None = None) -> str: | |||
There was a problem hiding this comment.
_god_node_article()
8 callers depend on it (afferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
|
Thanks for the catch — the formal verifier's finding on Fixed in 5d79e51 by coercing with |
There was a problem hiding this comment.
Graphify reviewed this change.
Worth a look — the grounded gate found no coupling regressions or blocking issues, but 1 advisory finding(s) below merit a look before merge.
Formal verification. 1 change(s) alter behavior, breaking input(s) attached.
Behavior changes: \_community\_article changes behavior, here is the input that shows it.
The verifier found a concrete input on which \_community\_article behaves differently before and after the change. If that change is intended, ship it; if not, this is your bug.
Guarantee: This difference was REPRODUCED, the verifier actually ran both versions on that input and saw them disagree. It is real, not an artifact.
Evidence: On input \{"G":"\(lambda \_g: \(\_g\.add\_nodes\_from\(\[\(1, \{\}\), \(2, \{\}\), \(3, \{\}\)\]\), \_g\.add\_edges\_from\(\[\(1, 2, \{\}\), \(1, 3, \{\}\), \(2, 3, \{\}\)\]\), \_g\)\[\-1\]\)\(\_\_import\_\_\('networkx'\)\.Graph\(\)\)","cid":"'/'","nodes":"\[3, 1, 2\]","label":"'\['","labels":"\{'k': 'v'\}","cohesion":"1\.0","node\_community":"\{'a': None\}","resolver":"\{'a': 1, 'b': 2\}"\}, the old code produced '\# \[\\n\\n\> 3 nodes · cohesion 1\.00\\n\\n\#\# Key Concepts\\n\\n\- \*\*3\*\* \(2 connections\)\\n\- \*\*1\*\* \(2 connections\)\\n\- \*\*2\*\* \(2 connections\)\\n\\n\#\# Relationships\\n\\n\- No… but the new code produces '\# \\\\\[\\n\\n\> 3 nodes · cohesion 1\.00\\n\\n\#\# Key Concepts\\n\\n\- \*\*3\*\* \(2 connections\)\\n\- \*\*1\*\* \(2 connections\)\\n\- \*\*2\*\* \(2 connections\)\\n\\n\#\# Relationships\\n\\n\…. Paste that input straight into a regression test.
Graphify review — findings
Escapes [/] in every label printed directly into a generated wiki article — community titles, "Key Concepts" node listings, and god-node titles — via a new _escape_md_brackets helper, so a label whose source content contains literal [[...]] renders as plain text rather than a dead Obsidian wikilink (#3547). The helper stringifies its input first, so the id-fallback path used when a node has no label handles non-string node ids (ints, tuples) without raising. Adds regression tests covering each site plus an end-to-end export check.
Worth a look
- Backslash can bypass bracket escaping in Markdown links —
graphify/wiki.py:102· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 251 functions depend on the 25 functions this change touches.
Health — this change adds coupling hotspots:
- new:
to_wiki()— 43 callers, 7 callees - new:
dispatch_command()— 2 callers, 124 callees - new:
_god_node_article()— 8 callers, 3 callees - new:
_community_article()— 6 callers, 3 callees - new:
test_poisoned_manifest_is_healed()— 0 callers, 6 callees
Verification — 251 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 77 function(s) in the blast radius were not formally verified this run
Test selection
Test selection
5 of 276 test file(s) selected (2%) via static blast radius.
tests/test_export_path_length.py— impacttests/test_wiki.py— impacttests/test_wiki_label_bracket_escaping.py— impact, changed-testtests/test_wiki_link_filename_parity.py— impacttests/test_wiki_truncation_indicator.py— impact
Selection is safe under the controlled-regression assumption; always-run tests + a periodic full run are the backstops. Advisory — it never changes the check verdict.
Formal verification
Behavior changes: \_community\_article changes behavior, here is the input that shows it.
The verifier found a concrete input on which \_community\_article behaves differently before and after the change. If that change is intended, ship it; if not, this is your bug.
Guarantee: This difference was REPRODUCED, the verifier actually ran both versions on that input and saw them disagree. It is real, not an artifact.
Evidence: On input \{"G":"\(lambda \_g: \(\_g\.add\_nodes\_from\(\[\(1, \{\}\), \(2, \{\}\), \(3, \{\}\)\]\), \_g\.add\_edges\_from\(\[\(1, 2, \{\}\), \(1, 3, \{\}\), \(2, 3, \{\}\)\]\), \_g\)\[\-1\]\)\(\_\_import\_\_\('networkx'\)\.Graph\(\)\)","cid":"'/'","nodes":"\[3, 1, 2\]","label":"'\['","labels":"\{'k': 'v'\}","cohesion":"1\.0","node\_community":"\{'a': None\}","resolver":"\{'a': 1, 'b': 2\}"\}, the old code produced '\# \[\\n\\n\> 3 nodes · cohesion 1\.00\\n\\n\#\# Key Concepts\\n\\n\- \*\*3\*\* \(2 connections\)\\n\- \*\*1\*\* \(2 connections\)\\n\- \*\*2\*\* \(2 connections\)\\n\\n\#\# Relationships\\n\\n\- No… but the new code produces '\# \\\\\[\\n\\n\> 3 nodes · cohesion 1\.00\\n\\n\#\# Key Concepts\\n\\n\- \*\*3\*\* \(2 connections\)\\n\- \*\*1\*\* \(2 connections\)\\n\- \*\*2\*\* \(2 connections\)\\n\\n\#\# Relationships\\n\\n\…. Paste that input straight into a regression test.
Could not verify: Could not verify \_god\_node\_article.
The verifier did not have enough to check \_god\_node\_article, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: non-vacuity: domain too small (only 2 distinct inputs exercised, need 3) — 'no divergence' would be near-vacuous
No difference found (not proven): No behavior difference found in \_md\_link (not a proof).
The verifier ran both versions of \_md\_link on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.
Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.
Note: An input the sampler did not try could still differ.
· 1 grounded finding(s) anchored inline below; 4 more finding(s) on lines outside this diff (see the check run).
| @@ -185,7 +204,7 @@ def _community_article( | |||
| def _god_node_article(G: nx.Graph, nid: str, labels: dict[int, str], node_community: dict[str, int] | None = None, resolver: dict[str, str] | None = None) -> str: | |||
There was a problem hiding this comment.
_god_node_article()
8 callers depend on it (afferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
|
Addressed the advisory finding on backslash bypassing the escaping in 64bfc3a. Source content can already contain a literal backslash right before a bracket (a doc excerpt showing a regex character class, Added a regression test with a small simulator of CommonMark's own left-to-right backslash-escape unwinding, so it checks the actual syntactic property (no bracket reachable as bare/unescaped) rather than just comparing output strings. |
There was a problem hiding this comment.
Graphify reviewed this change.
Worth a look — the grounded gate found no coupling regressions or blocking issues, but 1 advisory finding(s) below merit a look before merge.
Formal verification. 1 change(s) alter behavior, breaking input(s) attached.
Behavior changes: \_community\_article changes behavior, here is the input that shows it.
The verifier found a concrete input on which \_community\_article behaves differently before and after the change. If that change is intended, ship it; if not, this is your bug.
Guarantee: This difference was REPRODUCED, the verifier actually ran both versions on that input and saw them disagree. It is real, not an artifact.
Evidence: On input \{"G":"\(lambda \_g: \(\_g\.add\_nodes\_from\(\[\(1, \{\}\), \(2, \{\}\), \(3, \{\}\)\]\), \_g\.add\_edges\_from\(\[\(1, 2, \{\}\), \(1, 3, \{\}\), \(2, 3, \{\}\)\]\), \_g\)\[\-1\]\)\(\_\_import\_\_\('networkx'\)\.Graph\(\)\)","cid":"'/'","nodes":"\[3, 1, 2\]","label":"'\['","labels":"\{'k': 'v'\}","cohesion":"1\.0","node\_community":"\{'a': None\}","resolver":"\{'a': 1, 'b': 2\}"\}, the old code produced '\# \[\\n\\n\> 3 nodes · cohesion 1\.00\\n\\n\#\# Key Concepts\\n\\n\- \*\*3\*\* \(2 connections\)\\n\- \*\*1\*\* \(2 connections\)\\n\- \*\*2\*\* \(2 connections\)\\n\\n\#\# Relationships\\n\\n\- No… but the new code produces '\# \\\\\[\\n\\n\> 3 nodes · cohesion 1\.00\\n\\n\#\# Key Concepts\\n\\n\- \*\*3\*\* \(2 connections\)\\n\- \*\*1\*\* \(2 connections\)\\n\- \*\*2\*\* \(2 connections\)\\n\\n\#\# Relationships\\n\\n\…. Paste that input straight into a regression test.
Graphify review — findings
Escapes [/] in node labels printed directly into generated wiki articles — community titles, "Key Concepts" node listings, and god-node titles — via a new _escape_md_brackets helper, so a label whose source content literally contains [[...]] renders as plain text rather than a dead wikilink. The helper escapes backslashes before brackets (so a pre-existing \] can't collapse the bracket's escape under CommonMark) and stringifies its input first, covering the non-string node ids callers fall back to when a node has no label.
Worth a look
- Backslash doubling can reactivate escaped Markdown punctuation —
graphify/wiki.py:82· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 254 functions depend on the 28 functions this change touches.
Health — this change adds coupling hotspots:
- new:
to_wiki()— 43 callers, 7 callees - new:
dispatch_command()— 2 callers, 124 callees - new:
_god_node_article()— 8 callers, 3 callees - new:
_community_article()— 6 callers, 3 callees - new:
test_poisoned_manifest_is_healed()— 0 callers, 6 callees
Verification — 254 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 80 function(s) in the blast radius were not formally verified this run
Test selection
Test selection
5 of 276 test file(s) selected (2%) via static blast radius.
tests/test_export_path_length.py— impacttests/test_wiki.py— impacttests/test_wiki_label_bracket_escaping.py— impact, changed-testtests/test_wiki_link_filename_parity.py— impacttests/test_wiki_truncation_indicator.py— impact
Selection is safe under the controlled-regression assumption; always-run tests + a periodic full run are the backstops. Advisory — it never changes the check verdict.
Formal verification
Behavior changes: \_community\_article changes behavior, here is the input that shows it.
The verifier found a concrete input on which \_community\_article behaves differently before and after the change. If that change is intended, ship it; if not, this is your bug.
Guarantee: This difference was REPRODUCED, the verifier actually ran both versions on that input and saw them disagree. It is real, not an artifact.
Evidence: On input \{"G":"\(lambda \_g: \(\_g\.add\_nodes\_from\(\[\(1, \{\}\), \(2, \{\}\), \(3, \{\}\)\]\), \_g\.add\_edges\_from\(\[\(1, 2, \{\}\), \(1, 3, \{\}\), \(2, 3, \{\}\)\]\), \_g\)\[\-1\]\)\(\_\_import\_\_\('networkx'\)\.Graph\(\)\)","cid":"'/'","nodes":"\[3, 1, 2\]","label":"'\['","labels":"\{'k': 'v'\}","cohesion":"1\.0","node\_community":"\{'a': None\}","resolver":"\{'a': 1, 'b': 2\}"\}, the old code produced '\# \[\\n\\n\> 3 nodes · cohesion 1\.00\\n\\n\#\# Key Concepts\\n\\n\- \*\*3\*\* \(2 connections\)\\n\- \*\*1\*\* \(2 connections\)\\n\- \*\*2\*\* \(2 connections\)\\n\\n\#\# Relationships\\n\\n\- No… but the new code produces '\# \\\\\[\\n\\n\> 3 nodes · cohesion 1\.00\\n\\n\#\# Key Concepts\\n\\n\- \*\*3\*\* \(2 connections\)\\n\- \*\*1\*\* \(2 connections\)\\n\- \*\*2\*\* \(2 connections\)\\n\\n\#\# Relationships\\n\\n\…. Paste that input straight into a regression test.
Could not verify: Could not verify \_god\_node\_article.
The verifier did not have enough to check \_god\_node\_article, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: non-vacuity: domain too small (only 2 distinct inputs exercised, need 3) — 'no divergence' would be near-vacuous
No difference found (not proven): No behavior difference found in \_md\_link (not a proof).
The verifier ran both versions of \_md\_link on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.
Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.
Note: An input the sampler did not try could still differ.
· 1 grounded finding(s) anchored inline below; 4 more finding(s) on lines outside this diff (see the check run).
| @@ -185,7 +218,7 @@ def _community_article( | |||
| def _god_node_article(G: nx.Graph, nid: str, labels: dict[int, str], node_community: dict[str, int] | None = None, resolver: dict[str, str] | None = None) -> str: | |||
There was a problem hiding this comment.
_god_node_article()
8 callers depend on it (afferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
|
Following up on the newest review pass's advisory ("Backslash doubling can reactivate escaped Markdown punctuation") — this is unverified/no reproducing input was given, unlike the earlier findings which did include a concrete counterexample. I checked it directly: the fix escapes every backslash in the source text first (single -> double) before escaping brackets, which is provably correct by induction — doubling always leaves an EVEN run of backslashes immediately before any bracket (0, 2, 4, ...), so the bracket-escaping step's one added backslash always makes an odd/pairing count that resolves as an escaped, inert bracket, regardless of how many backslashes preceded it or what surrounds them. Verified empirically against several adversarial cases (runs of 1-4 backslashes before/after/between brackets, mixed content) with a script that simulates CommonMark's left-to-right backslash-escape pairing — no case leaves a bare, live bracket. I believe this is the same concern the earlier finding already flagged and that 64bfc3a already fixed, not a new distinct gap; happy to add more test cases if a specific failing input turns up. |
_md_link already escapes a label before it ever becomes link text, because a node label is extracted source content and can literally contain a bracketed substring, a doc that discusses or demonstrates wikilink syntax being the obvious source. Three other places print a label straight into an article body without going through that escaping at all, a community's own title, its Key Concepts node listing, and a god node's own title, so a label like double bracket wikilink double bracket rendered as a real, and always dead since the wiki export never writes that link form, wikilink instead of the plain text it actually is. Pulls the escaping out of _md_link into a small shared helper and applies it at all three sites. Fixes the first of two bugs in Graphify-Labs#3547. The second bug the report describes, a wikilink target left percent encoded while the file written for it is not, was already fixed on this branch's base by number 2597, confirmed directly: _safe_filename strips everything that would need encoding from a slug before it is ever used as either the link target or the filename, so raw emission and the on disk name are the same string by construction. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
Covers the helper directly, each of the three fixed print sites, and an end to end export using the exact placeholder text the report listed, double bracket link double bracket and double bracket new underscore stem double bracket among them. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
Addresses review feedback on PR 3550: the graphify review bot's
formal verifier found a concrete input on which _community_article
started raising AttributeError after this PR's escaping change.
_escape_md_brackets called replace directly on its argument. Every
caller passes d.get("label", nid), so a node whose dict has no
label attribute falls back to its own networkx node id, which is not
always a string (int and tuple ids are legal). The previous code
interpolated that fallback into an f string, which stringifies
anything; wrapping it in _escape_md_brackets turned that implicit
coercion into a hard requirement the fallback does not always meet.
Coercing with str() first restores the old behavior for a non string
fallback while keeping the escaping for genuine string labels.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
Addresses a review finding on PR 3550: the graphify review bot flagged that a pre existing backslash right before a bracket in source content can bypass the bracket escaping. _escape_md_brackets escaped only the two bracket characters. Source content occasionally already contains a literal backslash right before a bracket, a doc excerpt showing a regex character class like backslash close bracket plus, for example. Escaping the bracket alone turned that into two backslashes then a bare bracket, and CommonMark reads a doubled backslash as one literal backslash, which consumes the pair and leaves the following bracket unescaped and live as link syntax again. Escaping every backslash in the source text first, before the brackets, keeps the bracket's own escape intact regardless of what already preceded it. Added a regression test with a small simulator of CommonMark's own left to right backslash escape unwinding, so the assertion checks the actual syntactic property (no bracket reachable as bare, unescaped) rather than just comparing strings. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
64bfc3a to
85d2122
Compare
There was a problem hiding this comment.
Graphify reviewed this change.
Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).
Formal verification. 1 change(s) alter behavior, breaking input(s) attached.
Behavior changes: \_community\_article changes behavior, here is the input that shows it.
The verifier found a concrete input on which \_community\_article behaves differently before and after the change. If that change is intended, ship it; if not, this is your bug.
Guarantee: This difference was REPRODUCED, the verifier actually ran both versions on that input and saw them disagree. It is real, not an artifact.
Evidence: On input \{"G":"\(lambda \_g: \(\_g\.add\_nodes\_from\(\[\(1, \{\}\), \(2, \{\}\), \(3, \{\}\)\]\), \_g\.add\_edges\_from\(\[\(1, 2, \{\}\), \(1, 3, \{\}\), \(2, 3, \{\}\)\]\), \_g\)\[\-1\]\)\(\_\_import\_\_\('networkx'\)\.Graph\(\)\)","cid":"'/'","nodes":"\[3, 1, 2\]","label":"'\['","labels":"\{'k': 'v'\}","cohesion":"1\.0","node\_community":"\{'a': None\}","resolver":"\{'a': 1, 'b': 2\}"\}, the old code produced '\# \[\\n\\n\> 3 nodes · cohesion 1\.00\\n\\n\#\# Key Concepts\\n\\n\- \*\*3\*\* \(2 connections\)\\n\- \*\*1\*\* \(2 connections\)\\n\- \*\*2\*\* \(2 connections\)\\n\\n\#\# Relationships\\n\\n\- No… but the new code produces '\# \\\\\[\\n\\n\> 3 nodes · cohesion 1\.00\\n\\n\#\# Key Concepts\\n\\n\- \*\*3\*\* \(2 connections\)\\n\- \*\*1\*\* \(2 connections\)\\n\- \*\*2\*\* \(2 connections\)\\n\\n\#\# Relationships\\n\\n\…. Paste that input straight into a regression test.
Graphify review — findings
Escapes [/] (and backslashes ahead of them) in every label printed directly into a generated wiki article — community titles, "Key Concepts" node listings, and god-node titles — via a new _escape_md_brackets, so source content that literally contains [[...]] renders as plain text rather than a dead Obsidian wikilink (#3547). Backslashes are escaped before brackets so a pre-existing \] in source doesn't un-escape back into live link syntax, and non-string node ids used as label fallbacks are stringified rather than raised on. _md_link now routes its own escaping through the same helper.
No blocking issues surfaced. 1 lower-confidence candidate did not survive cross-model review.
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 254 functions depend on the 28 functions this change touches.
Health — this change adds coupling hotspots:
- new:
to_wiki()— 43 callers, 7 callees - new:
dispatch_command()— 2 callers, 125 callees - new:
_god_node_article()— 8 callers, 3 callees - new:
_community_article()— 6 callers, 3 callees - new:
test_poisoned_manifest_is_healed()— 0 callers, 6 callees
Verification — 254 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 80 function(s) in the blast radius were not formally verified this run
Test selection
Test selection
5 of 284 test file(s) selected (2%) via static blast radius.
tests/test_export_path_length.py— impacttests/test_wiki.py— impacttests/test_wiki_label_bracket_escaping.py— impact, changed-testtests/test_wiki_link_filename_parity.py— impacttests/test_wiki_truncation_indicator.py— impact
Selection is safe under the controlled-regression assumption; always-run tests + a periodic full run are the backstops. Advisory — it never changes the check verdict.
Formal verification
Behavior changes: \_community\_article changes behavior, here is the input that shows it.
The verifier found a concrete input on which \_community\_article behaves differently before and after the change. If that change is intended, ship it; if not, this is your bug.
Guarantee: This difference was REPRODUCED, the verifier actually ran both versions on that input and saw them disagree. It is real, not an artifact.
Evidence: On input \{"G":"\(lambda \_g: \(\_g\.add\_nodes\_from\(\[\(1, \{\}\), \(2, \{\}\), \(3, \{\}\)\]\), \_g\.add\_edges\_from\(\[\(1, 2, \{\}\), \(1, 3, \{\}\), \(2, 3, \{\}\)\]\), \_g\)\[\-1\]\)\(\_\_import\_\_\('networkx'\)\.Graph\(\)\)","cid":"'/'","nodes":"\[3, 1, 2\]","label":"'\['","labels":"\{'k': 'v'\}","cohesion":"1\.0","node\_community":"\{'a': None\}","resolver":"\{'a': 1, 'b': 2\}"\}, the old code produced '\# \[\\n\\n\> 3 nodes · cohesion 1\.00\\n\\n\#\# Key Concepts\\n\\n\- \*\*3\*\* \(2 connections\)\\n\- \*\*1\*\* \(2 connections\)\\n\- \*\*2\*\* \(2 connections\)\\n\\n\#\# Relationships\\n\\n\- No… but the new code produces '\# \\\\\[\\n\\n\> 3 nodes · cohesion 1\.00\\n\\n\#\# Key Concepts\\n\\n\- \*\*3\*\* \(2 connections\)\\n\- \*\*1\*\* \(2 connections\)\\n\- \*\*2\*\* \(2 connections\)\\n\\n\#\# Relationships\\n\\n\…. Paste that input straight into a regression test.
Could not verify: Could not verify \_god\_node\_article.
The verifier did not have enough to check \_god\_node\_article, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: no capturable inputs from the test suite; property tier: non-vacuity: domain too small (only 2 distinct inputs exercised, need 3) — 'no divergence' would be near-vacuous
No difference found (not proven): No behavior difference found in \_md\_link (not a proof).
The verifier ran both versions of \_md\_link on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.
Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.
Note: An input the sampler did not try could still differ.
· 1 grounded finding(s) anchored inline below; 4 more finding(s) on lines outside this diff (see the check run).
| @@ -185,7 +218,7 @@ def _community_article( | |||
| def _god_node_article(G: nx.Graph, nid: str, labels: dict[int, str], node_community: dict[str, int] | None = None, resolver: dict[str, str] | None = None) -> str: | |||
There was a problem hiding this comment.
_god_node_article()
8 callers depend on it (afferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
Summary
Fixes the first of the two bugs in #3547 (literal placeholder/mis-encoded
[[wikilinks]]in generatedCommunity_N.mdpages).Bug 1 (literal placeholder wikilinks) — fixed here.
_md_linkalready escapes[/]before a label becomes link text, because a node label is extracted source content and can literally contain a bracketed substring — most plausibly from a doc that discusses or demonstrates wikilink syntax. Three other sites print a label straight into an article body without going through that escaping at all: a community's own title, its "Key Concepts" node listing, and a god node's own title. Printed unescaped, a label like[[wikilink]]renders as a real (and always dead — the wiki export never writes bracket-style links) wikilink instead of the plain text it actually is. Pulled the escaping logic out of_md_linkinto a small shared helper (_escape_md_brackets) and applied it at all three sites.Bug 2 (percent-encoded target vs. raw filename) — already fixed on
v8. Traced this directly:_md_link's own docstring already documents removingquote()-based percent-encoding as issue #2597, and confirmed_safe_filenamestrips every character that would need encoding from a slug before it's used as either the link target or the on-disk filename — so raw emission and the actual filename are the same string by construction. Verified with the issue's own exact example (CMDS.md_(System Philosophy & Context Guide).md): no%28/%29/%26anywhere in the current output, andtests/test_wiki_link_filename_parity.py(already in the suite, all passing) pins exactly this invariant for the same character classes the issue reports ((,),&, em dash,%, CJK).Test plan
tests/test_wiki_label_bracket_escaping.py: the helper directly, each of the three fixed print sites individually, and an end-to-endto_wiki()export using the issue's own example placeholder text ([[link]],[[new_stem]],[[]]) confirmed absent from every generated page.test_wiki.py,test_wiki_truncation_indicator.py,test_wiki_link_filename_parity.py) — 49 tests, all pass, no regressions.python3 -m pytest -q— 5491 passed, only the pre-existing unrelated failures (test_ollama_retry_cap.pymissingopenaiin this env, one flaky timing assertion intest_ts_import_type_arguments.py).python3 -m tools.skillgen --check— OK.🤖 Generated with Claude Code
https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh