EnglishРусский Map

Second Brain β€” LLM Wiki Schema

Second Brain β€” LLM Wiki Schema

This is a personal knowledge base maintained by an LLM following the LLM Wiki pattern. The LLM builds and maintains the wiki; the human curates sources and directs exploration.

Directory Structure

second-brain/
β”œβ”€β”€ CLAUDE.md              # this file β€” schema and conventions
β”œβ”€β”€ meta/                  # docs about the wiki itself (pattern, writing style, etc.)
β”œβ”€β”€ sources/               # raw, immutable source documents (never modified by LLM)
β”‚   └── ingested-urls.md   # URLs fetched from terminal that need manual backup
β”œβ”€β”€ inbox/                 # Obsidian web clips landing here for processing
β”œβ”€β”€ inbox-processed/       # clips moved here after ingest (backup, not deleted)
β”œβ”€β”€ inbox-assets/          # staging for clip attachments; drained into sources/assets/
β”œβ”€β”€ toolbox/               # tool/project pages (type: toolbox)
β”œβ”€β”€ books/                 # book pages (type: book)
β”œβ”€β”€ talks/                 # talk and lecture pages (type: talk)
└── wiki/                  # LLM-maintained markdown pages
    β”œβ”€β”€ index.md           # content catalog β€” updated on every ingest
    β”œβ”€β”€ log.md             # activity log: how entries are written, and the list of months
    └── log/YYYY-MM.md     # the entries themselves, one file per month

The meta/ directory holds documents that define how the wiki works β€” the LLM Wiki pattern description, writing style guides, etc. These are not knowledge base content; they shape how content gets written. Read them before writing wiki pages.

The inbox/ directory is where Obsidian web clips land. During inbox processing, each clip gets ingested normally, then moved to inbox-processed/ (never deleted β€” it's our backup).

inbox-assets/ is where the Obsidian web clipper drops images and attachments. It is a staging area, not a resting place: ingest moves anything it finds there into sources/assets/, which is the only directory attachments are linked from. Expect inbox-assets/ to be empty between ingests β€” it has been empty since it was created, because every clip so far has been text.

The sources/ingested-urls.md file tracks URLs that were fetched directly from the terminal during ingest. These exist only as saved markdown in sources/ and should be manually clipped/backed up when convenient.

Writing Style

Wiki pages are meant to be read by a human and parsed by a machine. Write naturally:

  • Use plain, direct language. Avoid AI-isms: "delve", "leverage", "robust", "tapestry", "landscape", "ecosystem", "harness", "streamline", "utilize".
  • Don't inflate stakes or use false drama. Say what something is, not how world-changing it is.
  • No filler transitions ("It's worth noting", "Let's break this down", "Here's the thing").
  • No fractal summaries β€” don't tell the reader what you're about to say, then say it, then say what you said.
  • No bold-first bullet lists. No rhetorical questions answered in the next line.
  • Vary sentence length and structure. Short sentences are fine. So are longer ones that develop an idea.
  • When a page covers a list of items, write about them β€” don't just reformat the source as bullets.
  • Prefer concrete examples over abstract descriptions.

For a full catalog of tropes to avoid, see meta/tropes.md. To check a published page against these tropes, use tropes.fyi/vetter β€” paste a URL and it flags AI-sounding patterns.

Glossary

Use these terms consistently. Don't mix with alternatives.

Use Don't use
LLM large model, foundation model, large language model
RAG Retrieval-Augmented Generation, retrieval augmented generation
Claude Code CC, claude-code, Anthropic's coding tool
frontmatter YAML header, metadata, file header
wikilink bidirectional link, backlink (backlink is the reverse direction)
vault knowledge base (when referring to Obsidian specifically)
ingest import, add, process (for the source→wiki workflow)
Golang Go (ambiguous in search), golang

Conventions

Obsidian Compatibility

This vault is designed to be opened directly in Obsidian. All markdown follows Obsidian conventions:

  • Wikilinks: [[page-name]] (Obsidian resolves by filename across all folders)
  • Aliased links: [[page-name|display text]] when the filename isn't readable enough
  • Tags: use tags: in YAML frontmatter as a list, and #tag inline where natural
  • Embeds: ![[page-name]] to embed another page's content
  • Images: store in sources/assets/, reference as ![[image-name.png]]
  • No # Title heading if it duplicates the frontmatter title: β€” Obsidian shows the title from properties
  • Headings are ATX only (## Heading), never setext (a line underlined with === or ---). CommonMark reads a paragraph followed directly by --- as a setext heading, so a thematic break needs a blank line before it
  • Code is fenced only (```, with a language where there is one), never indented by four spaces. A fence carries the language and survives blank lines inside it; an indented block is indistinguishable from a list continuation and splits on the first blank line when the page is chunked for search

Page Format

Every wiki page uses YAML frontmatter (Obsidian Properties):

---
title: Page Title
type: summary | entity | concept | comparison | analysis | overview | toolbox | catalog | log
summary: One-sentence abstract of what this page covers
parent: "[[parent-page]]"
tags:
  - topic-tag
sources:
  - "[[source-filename]]"
created: YYYY-MM-DD
updated: YYYY-MM-DD
---

Content here. Link to other wiki pages with [[page-name]].
Cite sources with [[source-filename]].

Page types: summary, entity, concept, comparison, analysis, overview, toolbox, book, talk, catalog, log.

The summary: field lets me scan pages without reading full content. Keep it under 160 characters β€” imagine answering "what's this page about?" in one breath; warren lint warns past that. It is also what a stranger sees when the vault is served as a site, so write it as a search-result line rather than a note to self.

Catalog and Log Pages

Two page types are structural rather than content: catalog for the index.md files β€” root, wiki/, toolbox/, books/, talks/ β€” and log for wiki/log.md and the month files under wiki/log/. They link to nearly every other page β€” the catalogs plus the log carry about a third of every wikilink in the vault β€” so counting their links makes backlink counts meaningless and orphan detection impossible. A page listed in wiki/index.md always has an inbound link, whether or not anything actually refers to it.

Link metrics therefore ignore them: when counting inbound links or looking for orphans, skip every page whose type: is catalog or log. Any future catalog page has to carry type: catalog for the same reason.

The aliases: field is optional and natively supported by Obsidian. Use it to keep an old filename resolving after a rename, since the filename stem is also the page's URL when the vault is served. It widens search matching too β€” a page can be found under a name nobody chose as its filename.

aliases:
  - old-page-name
  - Full Product Name

Parent Field

The parent: field is optional. Use it to nest pages under broader topics:

parent: "[[networking]]"

This creates a hierarchy without moving files into folders. Obsidian resolves the wikilink normally. Use parent: when a page clearly belongs under a broader concept or overview page. Don't force it β€” flat pages with cross-references are fine when there's no natural parent.

Updated Date

Always bump updated: to today's date when revising an existing page (adding new information, correcting claims, adding cross-references). Do not bump it for trivial formatting changes. The created: date never changes.

Freshness

updated: is also what the freshness check reads. Pages age at different rates β€” a summary of an article describes a fixed source and never goes stale on its own, a toolbox page describes a moving target, and anything about LLM agents moves faster still β€” so each kind of page has a tier: the longest it may go without an updated: bump.

The tiers are declared once, in the freshness: frontmatter of freshness-tiers, which also explains each one. warren lint --src . reads them from there and reports every page whose updated: is older than its tier allows. A page's base tier comes from its type:, then its section, then default; a tag can shorten a finite base but never lengthen it, and never revives never. So a concept page tagged ai-agents ages in nine months, while a summary page with the same tag does not age at all.

A stale finding is a review request. Re-read the page, check it against what has happened since, then either revise it or confirm it β€” both end with bumping updated:, since a review that changed nothing is still the date the page was last known to be right. Bumping the date without looking defeats the check.

Translations

The vault is written in English. A Russian version of a page lives in a mirror tree: ru/wiki/foo.md is the Russian wiki/foo.md, ru/toolbox/bar.md the Russian toolbox/bar.md. Nothing else about the layout changes, and links are not rewritten β€” inside ru/wiki/ a bare [[bar]] resolves to the Russian bar when it exists and to the English one when it does not, in Obsidian and in warren alike, so a partial translation degrades page by page. Only wiki/ and toolbox/ are translated; sources are immutable third-party text, and books and talks are not mirrored.

A translation is the same page in another language, not a second page. It is served at the original's URL as a language variant (?lang=ru, remembered), it is not listed in indexes, catalogs or the graph, it counts for nothing in link metrics, and its updated: is not a freshness signal. What it does carry, after the original's keys, is where it came from:

lang: ru
translation_of: "[[foo]]"
source_updated: 2026-07-29          # the original's `updated:` at translation time
translated: 2026-09-01
translator: lllm/antigravity/gemini-3.7-flash-medium

title: and summary: are translated; every other frontmatter value β€” tags, sources, dates, parent β€” stays byte-identical to the original. Wikilink targets never change, code blocks and inline code are copied verbatim, and the file structure (headings, lists, tables) is the original's. The Russian follows the same rules as the English prose plus the typography of writing:ru-spec-style: Ρ‘ always, established terms in Latin script declined with an apostrophe (Π² worktree'Π΅, never transliterated), ASCII punctuation β€” a hyphen with spaces as the dash, straight quotes, -> as the arrow.

Translation is a machine job with a checked result: tools/translate/ holds the prompt, the runner and the checker, and the checker diffs what must not change. source_updated: is what keeps a translation honest β€” warren lint reports translation-stale when the original's updated: moves past it, which is the signal to re-run the translation, and translation-orphan when the mirrored page no longer exists. A translation is never edited by hand to catch up; it is regenerated from the current original. The translate skill is the procedure: how a batch is run, checked, committed and logged, and what the model gets wrong.

warren translations --src . is the coverage check: how many pages in wiki/ and toolbox/ have a current Russian version, which translations are stale, and which pages are still missing, most-linked first. Run it before every push to master β€” the pre-push hook (tools/translate/pre-push.sh) does, and a stale translation stops the push, because an English page must not go out with a Russian version that says something else. Missing translations are the backlog and do not block; they are the next batch.

Toolbox Pages

Toolbox pages describe interesting projects/tools for potential future use. Frontmatter includes extra fields:

---
title: tool-name
type: toolbox
summary: One-sentence abstract of what this tool is and what makes it distinct
tags:
  - language
  - domain
repo: https://github.com/owner/repo
language: Go
license: MIT
created: YYYY-MM-DD
updated: YYYY-MM-DD
---

Content should cover:

  • What the tool does (one paragraph)
  • How it works technically (architecture, key design decisions)
  • Basic usage examples with code blocks
  • Limitations or platform constraints
  • Repo link with star count and license at the end

No source file needed β€” the repo URL in frontmatter is the source. Cross-reference related concept or entity pages where relevant.

Book Pages

Book pages cover long-form works β€” books, monographs, multi-part essays. Live in books/. Frontmatter:

---
title: Book Title
type: book
summary: One-sentence abstract of what the book argues or contains
author: Author Name
year: YYYY
isbn: optional
url: https://publisher.example/book   # canonical publisher / free-distribution page
tags:
  - topic-tag
status: reading | finished | want-to-read | abandoned
rating: 1-5 (optional, only after finishing)
sources:
  - "[[source-filename]]"
created: YYYY-MM-DD
updated: YYYY-MM-DD
---

Additional optional fields can be added when relevant β€” e.g., cohort: linking to a reading-group discussion page, or pdf: for a freely-available PDF. Keep them lowercase and snake_case.

Content should cover:

  • One paragraph: what the book argues or contains
  • Key ideas or claims (concept pages get extracted from these over time)
  • Chapter notes β€” only where worth keeping, not a full ToC
  • Quotes worth keeping, with page numbers where available
  • How it relates to other books or wiki concepts (lineage, contrasts)

A book page may be created in want-to-read status with just the front matter and a short why-I-want-this paragraph. Update status: and bump updated: as the read progresses. After finishing, the concept pages that came out of the book are the primary deliverable β€” the book page itself is the index, not the substance.

Add new entries to the appropriate section of books/index.md. No source file is required for want-to-read; for reading and finished, link any reviews or excerpts under sources:.

Talk Pages

Talk pages cover conference talks, lectures, and recorded courses. Live in talks/. Frontmatter:

---
title: Talk Title
type: talk
summary: One-sentence abstract of what the talk argues or demonstrates
speaker: Speaker Name
venue: Conference or course name
url: https://youtu.be/VIDEO_ID
video_id: VIDEO_ID
duration: 35m10s
published: YYYY-MM-DD
tags:
  - topic-tag
status: watching | watched | want-to-watch | abandoned
parent: "[[course-page]]"   # lectures only
sources:
  - "[[sources/transcript-filename]]"
created: YYYY-MM-DD
updated: YYYY-MM-DD
---

video_id: is the dedup key. It is what an ingest checks before fetching, because the same talk arrives as youtube.com/watch?v=…, youtu.be/…, and a timestamped link, and only the id is stable across all three.

Content should cover:

  • One paragraph: what the talk argues or demonstrates
  • The argument as it develops, with [mm:ss](url?t=N) citations to the moments that carry it
  • Quotes worth keeping, each with its timestamp
  • How it relates to other talks, books, or wiki concepts

Cite timestamps, not just the talk. A deep link into a specific minute is the video equivalent of a page number, and the transcript in sources/ already carries one on every paragraph. A talk page that cites nothing but the video URL is missing the thing that makes video ingestable.

Auto-generated captions get names, jargon, and code identifiers wrong. Check any quote against the audio before it goes on a page β€” the transcript's own callout says so, and it is there because a machine transcript reads as authoritative when it isn't.

Courses

A multi-lecture course is a parent page plus one page per lecture. The course page carries status: and indexes its lectures; each lecture page carries parent: "[[course-page]]", its own transcript under sources:, and its own cross-references. Lectures are watched months apart and cover unrelated concepts, so they need to be linkable and datable one at a time.

Only the course page goes in the Courses section of talks/index.md; its lectures are listed on the course page itself, not in the catalog.

Watchlist

toolbox/watchlist.md tracks toolbox entries that aren't yet ready to commit to β€” alpha-stage projects, single-author repos, single-platform tools, anything dependent on outside circumstances. The point is to come back periodically and re-decide: graduated, abandoned, or still waiting.

When a toolbox page meets watchlist criteria:

  1. Add watchlist to its frontmatter tags:
  2. Add a row to the table in toolbox/watchlist.md with: tool wikilink, what we're waiting on, next-check date (default 90 days out), reason
  3. Mention the watchlist in the toolbox page itself (one line, link to [[toolbox/watchlist]])

When re-checking, evaluate: activity in last 90 days, star trajectory, release cadence, author bandwidth, platform breadth, whether the README still says "alpha", competitor consolidation. Move graduated tools to the Graduated section, abandoned ones to Abandoned, and drop the watchlist tag in either case.

Naming

  • All filenames: kebab-case.md (e.g., llm-wiki-pattern.md)
  • Source filenames: preserve original name where possible
  • Short, descriptive names β€” no dates in filenames
  • Prefer names that are unique across all directories β€” Obsidian resolves [[links]] by filename, so a name used twice makes every bare link to it ambiguous

Uniqueness is what lets a page be addressed by its stem alone. [[raft]] only means one thing if exactly one raft.md exists, and the same stem becomes the page's URL when the vault is served. A duplicate name costs a folder prefix on every link; a rename costs every existing link and address pointing at the old one, which is what aliases: is for.

Uniqueness is a goal, not a fact. About 110 names are currently used twice, plus the four index.md. Nearly all of the duplicates are a wiki/ or toolbox/ page and the sources/ document it was written from, which is deliberate and worth keeping β€” the source is named after the page it produced.

Where a name is reused, put the folder in the link: [[sources/lsm-trees-nosql]] for the clipped article, [[lsm-trees-nosql]] for the page written from it.

A bare [[name]] resolves nearest first: a file in the same folder, then the same top-level directory, then the rest of the vault in the order vault root, wiki/, toolbox/, books/, talks/, meta/, sources/. That order also decides URLs when the vault is served β€” the wiki page keeps the short one and the source falls back to sources/<name> β€” so renaming a page changes its address. Non-markdown attachments need their extension in the link ([[paper.pdf]], not [[paper]]).

Cross-References

  • Link between pages with [[page-name]] β€” works across folders automatically
  • When updating a page, check for and add relevant cross-references
  • Every page should have at least one inbound link (no orphans β€” check via Obsidian graph view)

Deliberate Gaps

A wikilink pointing at a page that does not exist is a defect by default. The exception is a link that is correct and simply ahead of the page β€” deleting it would erase the record that something is missing.

Declare those in planned-pages, which carries them in a planned: frontmatter list and explains each one in a table. warren check accepts a dangling wikilink only when some page declares its target, fails on anything else outside sources/, and warns when a declaration starts resolving so the list gets pruned rather than growing into a permanent allowlist. Any page may carry planned:, but keeping them in one place is what makes the gaps reviewable.

Do not write a stub to make a link resolve. An empty page is worse than a declared gap, because it stops looking like one.

Dangling links inside sources/ are expected and are not gated. Clipped articles and READMEs point at paths in the repository they came from, and sources/ is immutable, so nothing can clear them.

The warren binary lives at ~/data/home/warren (Golang; rebuild with just build or go build ./cmd/... there). Run the gate with warren check --src . from the vault root; it exits non-zero only on an undeclared dangling link. Two scope limits worth knowing: it extracts links from page bodies only, not from parent: or sources: frontmatter, so a frontmatter attachment link missing its extension (sources: "[[paper]]" instead of [[paper.pdf]]) breaks in Obsidian's Properties panel but slips past the gate β€” keep those extension-qualified by hand. And planned: is only pruned automatically when a body link points at the declared target; a gap referenced solely from frontmatter won't trigger the resolves-now warning.

index.md

Organized by page type. Each entry is one line:

## Summaries
- [[page-name]] β€” one-line description

## Entities
- [[page-name]] β€” one-line description

## Concepts
- [[page-name]] β€” one-line description

(Same pattern for Comparisons, Analyses, Overviews, Toolbox.)

The log

Append-only, one file per month: an entry goes into wiki/log/YYYY-MM.md for the month it is written in, and the file is created β€” frontmatter with type: log, as shown on log β€” when a month begins. wiki/log.md is the way in: it explains the format and lists the months; it holds no entries of its own. Each entry:

## [YYYY-MM-DD] action | Title
Brief description of what was done. List pages created/updated.

Actions: ingest, query, lint, update. Entries are never edited after the fact and never moved between files. The header form is what warren parses to put every entry, from every month file, on the one timeline at /log.

Workflows

Ingest

  1. Read the source document fully
  2. Discuss key takeaways with the user
  3. Create a summary page in wiki/
  4. Create or update entity/concept pages as needed
  5. Add cross-references to/from existing pages
  6. Update wiki/index.md
  7. Append entry to wiki/log/YYYY-MM.md

A topic that already has a page is not a reason to skip the source, and a source that disagrees with the page is not a reason to overwrite it. When the new source contradicts what a page says, work out which claim holds β€” a version, a date, a third source usually settles it β€” then update the page and say on it what changed and why the newer claim won ("X was true until version Y; since then Z, per …"). When the source merely adds β€” another approach, a counter-example, a failure mode β€” enrich the page. A later reader should be able to see that two sources disagreed and how it was resolved, rather than finding only the winner.

Video Ingest

Videos come in through tools/ytfetch, not webfetch β€” a video page is a player shell with no article in it, and webfetch refuses video hosts for that reason.

  1. ./ytfetch <url> --metadata to see the title, runtime, chapter count, and whether captions exist
  2. Check video_id against the vault before fetching: grep -rl "<VIDEO_ID>" --include='*.md' .
  3. ./ytfetch <url> --name <slug> -o ../../sources -v writes the transcript
  4. Read the transcript and write the talks/ page, citing timestamps
  5. Update talks/index.md, append to wiki/log/YYYY-MM.md, and record the URL in sources/ingested-urls.md

A talk with no captions on any platform falls back to a local whisper run, which is slow but unattended. A talk that yields no transcript at all gets recorded in sources/ingested-urls.md with the real reason and no page β€” never a page written from the video's description alone.

Query

  1. Read wiki/index.md to find relevant pages
  2. Read relevant wiki pages
  3. Synthesize an answer with citations
  4. If the answer is substantial and reusable, offer to save it as an analysis page

Inbox Processing

  1. List files in inbox/
  2. For each clip, read it and ingest normally (summary + entity/concept pages)
  3. After successful ingest, move the clip from inbox/ to inbox-processed/
  4. Never delete inbox files β€” inbox-processed/ is the backup
  5. Update wiki/index.md and the month's log file as usual

URL Backup

When ingesting from a URL in the terminal (not from inbox/), append the URL to sources/ingested-urls.md. Mark status as pending β€” the user will manually back these up and mark them done.

Lint

  1. Run warren lint --src . from the vault root. It reports what needs no judgment: orphans (no inbound link from any page outside catalogs and the log), pages their section's index.md does not list, frontmatter that is missing a required key, carries one twice, has an unknown type: or a date that is not YYYY-MM-DD, or does not parse at all, a setext heading or an indented code block in an authored section (heading-setext, code-indented β€” see the conventions above), a summary: over the limit, and pages older than their freshness tier (see freshness-tiers). It exits non-zero on the schema defects; long summaries and stale pages are warnings. Fix the errors before reading anything.
  2. Read wiki/index.md and the pages the report and recent ingests point at
  3. Report what a machine cannot see:
    • Contradictions between pages
    • Stale claims superseded by newer sources β€” start from the stale findings
    • Concepts mentioned but lacking their own page
    • Missing cross-references
    • Content gaps worth investigating
  4. Fix issues with user approval
  5. Append lint entry to wiki/log/YYYY-MM.md