to_wiki emitted Obsidian `[[Title]]` wikilinks. Outside Obsidian `[[Domain Data
Models]]` resolves to a literal "Domain Data Models.md", but the article file is
the slugged "Domain_Data_Models.md", so nearly every community/god-node link
opened an empty page; god-node articles also linked node-level neighbors that
never get an article file, so those were dead even inside Obsidian.
Links are now standard `[display](slug.md)` with the target URL-encoded (spaces,
&, parentheses, # survive in CommonMark and Obsidian alike); a link whose target
has no article is rendered as plain text instead of left dangling. A label->slug
resolver is built up front so a link points at the real on-disk filename incl. the
case-fold collision suffix.
Ported from PR #1465 by @TPAteeq. Maintainer edit: trimmed the CHANGELOG wording
that overstated the guarantee ("always matches ... collision suffix and all") —
two articles sharing a byte-identical label still keep the first slug (pre-existing
behavior, same as the old [[label]]), so the claim is scoped to the case-fold case.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
to_obsidian / to_canvas / to_wiki keyed filename dedup on the exact-case name,
so two labels differing only by case (e.g. `References` vs `references`) counted
as non-colliding and the second write clobbered the first on case-insensitive
filesystems (macOS/APFS, Windows/NTFS) — silently, no suffix, no warning.
Dedup now folds case (keyed on the lowercased name) while emitting the
original-case filename, so any pair that would collide on disk gets a numeric
suffix. The obsidian/canvas dedup is one shared helper (`_dedup_node_filenames`)
so they can't drift; wiki's slug dedup gets the matching fix; the `_COMMUNITY_*`
overview notes (which had no dedup at all) are covered; and a generated `base_1`
is re-checked so it can't overwrite a node literally labelled `base_1`.
Ported from PR #1457 by @TPAteeq onto current v8. Verified with a rigorous
edge-case battery (case-only collision, base_1 literal re-check -> base_1_1,
community-label case fold, determinism) plus the PR's tests; full suite 2404
passed, ruff + skillgen clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Security: _is_sensitive now flags underscore-prefixed names (api_token.txt, oauth_token.json) by replacing \b with lookarounds; adds _SENSITIVE_DIRS check on parent path components (parts[:-1]) so .ssh/, secrets/, .aws/ directories are always skipped; aligns both patterns to (?![a-zA-Z]) for consistent underscore-after-keyword behavior (#920)
Fix: --wiki Relationships section always empty because _cross_community_links read community from node attrs (always None) instead of the communities dict; _god_node_article had the same bug and never linked to the owning community; fixed by building a node->community map in to_wiki() and threading it through (#925)
Fix: --watch now respects .graphifyignore; patterns loaded once at startup, handler checks _is_ignored before extension filter so node_modules/, .venv/, build/ churn no longer triggers rebuilds (#928)
Fix: C++ struct inheritance edges via base_class_clause; initialize base="" per iteration to prevent stale carryover (#915)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>