My vault template: typed pages, enforced wikilinks, and a linter that keeps the graph honest


Is this project open source? Yes — MIT.
Is this project completely free? Yes.
Is this project vibe-coded beyond the author’s ability to comprehend how it works? No.
Community Directory: N/A — this is a custom vault / workflow showcase, not a plugin or theme.


Sharing the vault setup I have been using, in case the structure is useful to anyone. It is a template repo you can clone.

No plugins required. Graph view, backlinks and Properties do the whole job. Frontmatter is flat YAML (Properties needs it that way), and graph colors,
folder colors and search exclusions come pre-configured in .obsidian/.

The structure:

  • Typed pages, one folder each: sources/, entities/, concepts/, projects/, notes/, meta/. Every type has a skeleton in _templates/.
  • A universal frontmatter schema — type, title, created, updated, tags, status (seed → developing → mature → evergreen), related, sources. Wikilinks in
    YAML are quoted so Properties parses them.
  • The trio: index.md is a master catalog with one line per page, log.md is an append-only journal of every change, hot.md is a short cache of recent
    context.
  • Filenames are Title Case and unique across the vault, so wikilinks always resolve by bare name.
  • Originals are immutable. Raw notes land in inbox/, and once processed they move to inbox/_done/ — never edited, never deleted. Each derived page
    records the hash and path of the file it came from.

Two pieces worth stealing even if you use none of the rest:

A deterministic linter. A stdlib-only Python script that checks dead wikilinks, orphan pages, duplicate filenames, alias collisions, frontmatter gaps
and index drift. Runs in a second, no dependencies, tells you exactly what is broken.

Contradiction callouts. When something new conflicts with an existing page, both sides get flagged instead of one quietly winning:

[!warning] Contradiction with [[Other Page]]
This page claims X; [[Other Page]] claims Y. Needs resolution.

The filing work — turning a messy note into typed, cross-linked pages and updating the index — I do with Claude Code, using skills that live inside the
vault. That part is optional and swappable; the vault is plain markdown and works standalone with any editor. But it is why the structure stays
consistent instead of drifting after two weeks, which is what killed every previous system I tried.

The whole contract is in CLAUDE.md — schema, conventions, read order — so the pattern is readable and forkable without adopting my tooling.

The split you draw between the deterministic linter and the editorial pass is the strongest decision in this template. Structural checks are cheap and they are exactly the checks that need to run every time, so paying an LLM for them makes no sense.

Most vault templates treat cleanup as maintenance — something you do when the vault starts feeling messy. But the content itself does not really decay. What degrades is the organization around it: links that lost their target, pages nothing points to anymore, an index that quietly drifted from what is on disk. That is a bounded problem, and it is the part a linter can actually own.

The one thing I would push on is related:. Checking that a wikilink resolves proves the target exists, it does not say anything about what the connection asserts. A page can come back completely green and still be a list of names sitting next to each other. Your contradiction callouts are the interesting counter-example — they carry a real claim about how two pages stand to each other, but they look hand-authored, so they are also the part still costing tokens.

Does the linter do anything with related: past resolving the target? And are the contradiction callouts written by hand, or does the editorial pass propose them and you accept or reject?

thanks chatgpt