plans
Plan — Vault-Aware Linking in Both Directions
Generated notes arrived orphaned, and the templates forbade linking rather than risk a model inventing a path.
Plan — Vault-Aware Linking in Both Directions
Written retroactively. This feature shipped in
dbe6b25before it had a plan document, unlike the four drafted on 2026-09-10. Recorded here so it is tracked like its siblings, and because the inbound/outbound distinction is exactly the kind of thing that gets re-conflated six months later.
Why care
Two problems that look unrelated and are the same problem.
A generated note arrived orphaned. Perplexed would produce an excellent
profile of a company that linked to nothing in the vault it was written into.
The standing instruction in market-map-profile.md and toolkit-profile.md was
literally "Do NOT invent [[wikilink]] syntax" — with the curator expected to
promote names to wikilinks by hand during a later pass. That rule was correct
given what the model knew: a model guessing vault paths produces broken links.
But the conclusion it forced — forbid linking entirely — meant every note had to
be hand-wired into the graph afterward.
Existing notes were full of unlinked mentions. Obsidian surfaces these in its "Unlinked mentions" pane and has never offered to apply them. With ~1,180 notes in the vault and hundreds already drafted, the backlog is the larger half of the problem, and it is not addressed by anything done at generation time.
The fix for the first is to stop withholding the paths. The fix for the second is a bulk-convert with review.
The distinction that matters
These are opposite operations and conflating them cost a real detour during the session — a scan was built for the outbound direction while the request was for the inbound one, and the fact that Obsidian calls the inbound pane "Linked mentions" did not help.
| Direction | Source of truth | What it does | |
include-backlinks: | inbound | metadataCache.resolvedLinks | Notes that link to this one, fed into generation as real paths |
| "Link back to vault notes" | outbound | the note's own prose | Titles of other notes found in this one, offered for linking |
The load-bearing consequence: an inbound backlink usually does not appear as
text in the target note at all. Four notes linking to ThoughtSpot contained
zero occurrences of their own titles in ThoughtSpot's body. So inbound links can
never be handled by text replacement — they are generation context, not a
find-and-replace. That is why the two halves are different mechanisms rather
than one shared one.
Findings
Established while building, verified against the live vault.
resolvedLinks over getBacklinksForFile()
The "Linked mentions" pane uses app.metadataCache.getBacklinksForFile(), which
is absent from obsidian.d.ts (checked against obsidian 1.12.3). Community-
plugin review flags undocumented API use, and this plugin is being prepped for
submission — see the submission-blocker plans.
resolvedLinks is documented and typed: Record<source, Record<target, count>>.
Backlinks are its inversion, which is O(files) over a map already held in memory.
No reason to reach for the internal API.
The link format is load-bearing, and it is not the default
Links must be emitted vault-root-absolute with a display alias:
[[Tooling/Software Development/Programming Languages/Go|Go]] This is Obsidian's "New link format: Absolute path in vault" setting, and the published web renderings depend on it — a shortened link resolves in Obsidian and breaks in production.
fileManager.generateMarkdownLink() honours that setting, so it would produce
the right output today. The links are built explicitly anyway, because output
that feeds published sites must not silently change shape if a vault preference
is toggled.
Confidence tiers are not polish
A scan of a real note against 1,155 indexed vault notes returned eleven
candidates: seven correct (Gartner Magic Quadrant, General Catalyst,
Geodesic Capital, Metabase) and four noise (backed, pivot, slide,
data infrastructure).
backed is a genuine vault note and an ordinary English verb. So are Hex,
Layer, Element and Go — all real companies, all common words. Auto-linking
on title match alone would quietly corrupt prose at a rate that is invisible
until someone reads a sentence closely.
Hence: high confidence (exact case, and either multi-word or ≥6 characters) starts checked; everything else starts unchecked. Clicking "Select all" then Apply still cannot do damage, because the dangerous rows were never pre-selected.
Regions that must never be linkified
Code fences, inline code, existing wikilinks, markdown links, bare URLs,
footnote and reference definitions, headings (linking one breaks the TOC), and
everything from the generated # Sources heading down. Longest candidate wins,
so Agentic Analytics claims its span before Analytics can.
The instruction for weaving, not listing
The first version of the inbound prompt produced a tidy list of every backlink at the bottom of the note — the thing nobody reads. The instruction that produced usable output names a count and bans the list shape:
Work ONE OR TWO of them into your prose where they genuinely fit — inline, as part of a sentence you were going to write anyway. … Do NOT add a "Related notes" section, a bulleted list of links, or a See-also block. … Linking nothing is better than linking something irrelevant.
Result on a live Sintra run: "…the same general space referenced in market-map
notes like [[…|Agentic AI in Fintech]]" — a link in the middle of a sentence,
where a human would have put it.
What shipped
src/services/linkBackService.ts— outbound scan (findLinkCandidates,applyLinkCandidates), inbound lookup (findBacklinks,renderBacklinksBlock), and the sharedbuildWikilink.src/modals/LinkBackModal.ts+ styles — the review table: checkbox, matched text, target note with full path, surrounding context, select-all/none, and a live count on the apply button.include-backlinks:/backlinks-limit:cft keys, wired intoapplyTemplateand enabled ontoolkit-profile.The "Link back to vault notes" command.
Remaining work
Aliases are read but untested.
aliasesFor()handlesaliases:andalias:frontmatter, and candidates are taggedvia: 'alias'in the table, but no vault note with aliases was exercised during the session.excludeRootsis hardcoded to['zz-cf-lib']inDEFAULT_SCAN_OPTIONS. It should be a setting — a vault with a large archive or daily-notes folder will want them excluded, andminLength/includeLowConfidencelikewise.No batch mode. The outbound command runs on the active file only. The backlog it exists to clear is hundreds of notes, so a folder-scoped run with one review pass is the obvious next step.
The scan is synchronous over every markdown file. Fine at 1,155 notes; unmeasured above that.
The templates still say "Do NOT invent
[[wikilink]]syntax". That rule is now wrong wheninclude-backlinks:is on, and the prose inmarket-map-profile.mdandtoolkit-profile.mdhas not been updated to say so. Doing it needs care: the rule is still correct for notes the model was not handed a path for.include-backlinks:is enabled ontoolkit-profileonly.market-map-profileis the template that would benefit most — its whole Adjacent Concepts section exists because the curator has to wikilink by hand.
Open items
Should the outbound scan offer to create a note for a high-confidence unresolved mention — a term used repeatedly with no vault note behind it? That is closer to vault authoring than to linking; probably its own tool.
Whether
include-backlinks:should also surface unresolved links (metadataCache.unresolvedLinks) — notes that tried to link to this subject before the note existed. Cheap to add, unclear whether useful.Whether the one-or-two-links instruction should scale with document length. A 6,000-word market map can carry more than a 900-word toolkit profile without reading as link spam.