Overview
Frontmatter is the spine of a vault. It carries the type, the status, the provenance, and the dates that every query, audit, and agent workflow reads. Frontmatter is never optional and never partially filled. This page covers the schema discipline; the folder each type lives in is set in vault-architecture.
Type every note in frontmatter and file it by type
Every note declares type, and every type declares its folder in the vault spec. Those two facts together make the vault checkable: a note whose type is missing evades every per-type rule, and a note filed outside its type’s folder breaks every folder-scoped query.
---
id: 202608291430 # YYYYMMDDHHmm, generated by the template
title: "Full title as a claim"
type: atom
status: status/seedling
created: 2026-08-29
modified: 2026-08-29
domain: [finance/macro]
sources: ["[[@minto-1987-pyramid-principle]]"]
related: ["[[mece-issue-tree]]"]
confidence: medium
last_verified: 2026-08-29
decision_relevant: false
---Generate this block from a template on note creation rather than typing it. Hand-typed frontmatter is where schema drift starts. See obsidian-templates for the template mechanics.
Give each note type its own required-field set
Required fields are per type, declared in the spec, and enforced by the auditor. A single global required list either under-constrains atoms or over-constrains daily notes.
| Type | Required beyond id, title, type, created |
|---|---|
| atom | status, sources, confidence |
| source | source_type, author, year, atoms_extracted |
| claim | sources (non-empty), confidence, last_verified |
| decision | decision_date, chosen_option, alternatives_considered, key_atoms, key_assumptions, expected_outcome, confidence_at_decision |
| contradiction | atoms_in_tension, resolution_criteria |
| question | status (open, answered, abandoned) |
The load-bearing fields are the ones that would otherwise be quietly skipped. confidence on an atom, sources on a claim, and alternatives_considered on a decision are all fields a hurried author drops, and all three are exactly the fields that make the note worth keeping. Requiring them machine-checks the discipline instead of trusting it. See vault-audit for how missing fields surface.
Write empty arrays, never missing keys
An absent key and an empty list mean different things to a query engine, and the difference silently corrupts results. counter_evidence: [] says “checked, none found.” A missing counter_evidence says nothing at all.
The same rule covers nulls: outcome: null on an unresolved decision is a filled field. Dates are always YYYY-MM-DD and ids are always a minute-precision timestamp such as 202608291430, so string sorting matches chronological sorting everywhere.
Extend the schema per framework rather than per note
When a rigor framework applies, it adds fields to the base type rather than creating a parallel schema. An engineering vault adds ADR fields to its decision atoms; a journalism vault adds source-tier and corroboration fields to its claims.
# Software and engineering (ADR)
adr_status: proposed | accepted | deprecated | superseded
superseded_by: ["[[ADR-...]]"]
blast_radius: small | medium | large
# Investigative research (source credibility)
source_tier: primary-document | on-record | on-background | anonymous
corroboration_count: 2
legal_review_status: not-reviewed | in-review | cleared | held
publishable: falseField groups that gate each other travel together. publishable, corroboration_count, and legal_review_status are one unit: flipping publishable without the other two produces a critical rather than a publishable claim. Framework selection is covered in rigor-frameworks.
Quote values that YAML would coerce. Unquoted publishable: yes parses as boolean true, not the string "yes".
Remove fields that stop being queried
A field that is always empty and never read is schema bloat, and bloat trains authors to skip frontmatter. During the monthly review, drop any field no query touches and no audit checks.
Adding a field is cheap; making it required is a migration. Add it to the required set, run the audit to get the exact backfill list, fill values honestly, then re-run and confirm the count returns to zero. Never backfill a fabricated default: a last_verified set to today on a claim verified two years ago is a lie the schema will now vouch for. The full procedure is in vault-evolution.