2.2 The Archives Standard
The single source of truth for how these archives think, speak, and breathe.
When in doubt — come here first.
Quick Reference
Standard Frontmatter Block :
---
aliases: []
created: 2026-00-00 00:00
modified: 2026-00-00 00:00
mode: read
publish: false
snippets: []
tags: []
type: note
---
Optional: url, theme
Field at a Glance :
| Field | Values | Always? |
|---|---|---|
aliases |
list | yes |
created |
datetime | yes |
modified |
datetime | yes |
mode |
read / edit / raw | omit if read |
publish |
false / public / unlisted / private | yes |
snippets |
list of snippet names | omit if empty |
tags |
list | yes (even []) |
type |
note / snippet / font / reference / log | yes |
url |
URL string | omit if absent |
theme |
theme name | omit if default |
Tag Vocabulary :
Maturity — describes the epistemic state of a note’s content. How developed is the idea?
maturity/seed — raw capture, early thinking, not yet developed
maturity/sprout — being actively developed, revisited, evolving
maturity/tree — stable, considered, complete, foundational
Use when: you want to track how mature a thought is. Opt-in only — dailies and logs carry no maturity tag.
Status — describes the operational state. What’s the action status?
status/working — actively in progress right now
status/active — ongoing, alive, in circulation
status/done — finished, complete, shipped
status/pending — waiting, blocked, on hold
status/idea — captured but not yet started
status/archived — closed out, no longer active
Use when: you want to track what you’re doing with something. Opt-in only — most notes are neither.
Example combinations :
maturity/seedling + status/working → new idea you're developing
maturity/evergreen + status/active → established knowledge you keep returning to
maturity/evergreen + status/archived → solid knowledge, no longer relevant
The rule :
Tags carry signal, not noise. A note with tags: [] is complete and correct. Only add tags when the tag actually tells you something useful.
The Rule :
Pipeline-critical → always explicit.
Optional/decorative → omit when empty.
Filename = title.type= behavior.publish= one field, four states.
Philosophy
This vault is not a productivity system. It is a personal canon — a living document of a mind at work. Every decision made here serves one principle :
Slow, intentional craft over automated noise.
Fields exist because they carry signal. Conventions exist because they reduce friction. Nothing is here for aesthetics alone.
Frontmatter Schema
The Standard Block
Every note born in this vault carries this shape :
---
aliases: []
created: 2026-00-00 00:00
modified: 2026-00-00 00:00
mode: read
publish: false
snippets: []
tags: []
type: note
---
Optional fields (omit if not needed):
url:— external source (Web Clipper fills)theme:— override vault defaultsnippets:— if empty, omit entirely
Field Reference
| Field | Type | Strategy | Purpose |
|---|---|---|---|
aliases |
multitext |
Always explicit (even []) |
Alt names for search & links |
created |
datetime |
Always explicit | Templater fills on creation |
modified |
datetime |
Always explicit | Linter updates on save |
mode |
text |
Omit if read | edit, raw override default reading mode |
publish |
text |
Always explicit | false, public, unlisted, private — publication state |
snippets |
multitext |
Omit if empty | CSS snippets to apply to this note |
tags |
tags |
Always explicit (even []) |
Content taxonomy + semantic meaning |
theme |
text |
Omit if default | Override vault-wide theme for this note |
type |
text |
Always explicit | Behavior-driven — plugin consumes it |
url |
text |
Omit if absent | External source URL — Web Clipper fills |
The Golden Rule
If your pipeline or queries depend on it — make it explicit.
If it’s decorative or contextual — omit when empty.
With Templater + Linter as enforcers, the cost of explicit empty fields is zero.
types.json
Located at .obsidian/types.json — this file tells Obsidian how to render each property in the Properties panel. Defines the native UI widget (date picker, checkbox, etc.).
{
"types": {
"aliases": "multitext",
"created": "datetime",
"modified": "datetime",
"mode": "text",
"publish": "text",
"snippets": "multitext",
"tags": "tags",
"theme": "text",
"type": "text",
"url": "text"
}
}
Available native types :
| Value | Widget |
|---|---|
text |
Plain text input |
multitext |
Multiple text values |
number |
Numeric input |
checkbox |
Boolean toggle |
date |
Date picker (YYYY-MM-DD) |
datetime |
Date + time picker |
tags |
Tag input with autocomplete |
To add a new field to the system — define it here first, then add it to the Templater default template.
Tag Taxonomy
Tags carry semantic and epistemic meaning. They are never used as operational pipeline flags — that is the role of properties.
Maturity
Describes the epistemic state of a note’s content. Opt-in only — never forced. Dailies, logs, and operational notes carry no maturity tag. Silence is correct.
maturity/seedling → raw capture, early thinking
maturity/growing → being developed, revisited
maturity/evergreen → stable, considered, complete
Status
Describes the operational state of a task, project, or idea.
status/working → actively in progress
status/active → ongoing, alive
status/done
status/pending
status/idea
status/archived
Type Hints (when type property is insufficient)
type/journal
type/project
type/reference
type/person
type/log
The Rule
publish,visibility,modeare properties — they are machine-read flags.
maturity,statusare tags — they are human-read signals.
Never conflate the two.
Note Types
Defined via the type property.
| Type | Description |
|---|---|
note |
Default. A thinking document. |
map |
Navigation hub. Organizes and links to other notes thematically. |
log |
Timestamped daily entry. |
reference |
External knowledge captured. |
project |
Active work with a goal. |
person |
A human being in the network. |
snippet |
CSS to load + documentation. |
font |
Font metadata + documentation. |
Naming Conventions
YYMMDD → daily logs (260609)
YYMMDD-HHMM → time-stamped entry (260609-0822)
Title Case → evergreen notes (Basement Door Catch Latch)
lowercase → never
The date-first format ensures chronological sort in the file system without any plugin dependency.
publish Vs Tags — The Decision
publish: true/false stays as a boolean property. It is a pipeline flag read by the build system (Astro, Cloudflare). It is not a content descriptor.
Reasons it will never become a tag :
- Absence of a tag is ambiguous (forgotten vs. intentionally unpublished)
- Build pipelines look for
publish: trueas a property, not a tag presence - Boolean clarity — the state is always explicit, never inferred
Automation Stack
| Tool | Role |
|---|---|
| Templater | Births every note with the correct schema |
| Linter | Enforces schema on every save — updates modified, normalizes fields |
| types.json | Ensures the Properties panel renders correctly |
| Dataview | Queries rely on field types being correct — types.json makes this stable |
These three form a closed loop. A note created by Templater, linted on save, and typed in
types.jsonis structurally trustworthy from birth to query.
This document is the ground truth. All structural decisions are final.
Working Session — 2026-06-09
Live decisions made during a working session. Raw thinking, not yet fully resolved.
title Field — keep or drop?
Context : inline title display is disabled. All notes now use a proper # H1 heading. The title property has lost its UI purpose.
Current thinking :
- For published notes going through Astro —
titlestays. Pipeline needs it for<title>, OG tags, RSS. - For everything else (logs, dailies, private notes) — it’s a third redundant copy of the same string. Dead weight.
Direction : two templates with different intents.
Templates/note.md→ notitlefieldTemplates/published.md→ hastitlefield
Decision : ✅ title is dropped from frontmatter entirely.
Rationale :
- The filename is the canonical title
- The filename is the permalink (slug)
- Astro derives
<title>, OG tags, and RSS from the filename — no frontmatter needed - A
# H1exists in every note for human reading - Three copies of the same string is noise — now there is one
Astro — filename → title conversion
When Obsidian saves a file, the filename is the source of truth. Astro reads it and converts to a clean title :
// Derive title from filename in content collection
const title = entry.id
.split('/').pop() // grab filename
.replace(/\.md$/, '') // strip extension
.replace(/-/g, ' ') // hyphens → spaces
.replace(/\b\w/g, c => c.toUpperCase()) // Title Case
Slug = filename. Title = filename humanized. One source, two outputs.
Atelier Plugin — Filename Sanitizer
Problem : some files have slipped through with bad characters in their name. Obsidian allows saving them but they break Astro routing, URLs, and filesystem portability.
Solution : build a sanitization feature directly into the Atelier plugin.
Behavior :
- On file rename or creation — validate the filename immediately
- If bad characters detected → block the save + show a popup
- Popup offers a one-click fix : auto-sanitized suggested name, user confirms or edits
- No file with bad characters can persist in the vault
Bad characters to catch :
# % & { } \ < > * ? / $ ! ' " : @ + ` | = , ; space(leading/trailing)
Suggested sanitization rules :
spaces → hyphens
accents/diacritics → stripped or transliterated (é→e, ç→c)
uppercase → preserved (Title Case filenames are valid)
special chars → removed
double hyphens → collapsed to single
- [ ] Build filename sanitizer in
apps/obsidian-atelier/src/features/filename-sanitizer/ - [ ] Popup UI with suggested fix + confirm/edit flow
- [ ] Audit existing vault for files with bad characters — one-time cleanup pass
aliases — ✅ Locked. Foundation field. Always explicit, always there.
created & modified — ✅ Locked. Untouchable.
Fully automated — Templater writes created once, Linter rewrites modified on every save. The timestamps are part of the record. No cognitive load, pure signal.
published (Date) — ❌ Skipped.
No current use case. created covers the pipeline needs. Revisit only if Astro needs to distinguish creation date from publication date.
cssclasses → snippets
Decision : rename cssclasses to snippets.
Rationale : cssclasses is a native Obsidian reserved word with a specific hardcoded behavior — it injects class names directly onto the .markdown-preview-view container. Renaming it to snippets means Obsidian no longer reads it natively. That bridge is cut.
Consequence : the Atelier plugin must take ownership. On note open/render, it reads snippets, resolves each value to its corresponding CSS snippet file from the vault library, and applies it. This is actually cleaner — the plugin controls the behavior, not Obsidian’s internals.
Workflow :
snippets: [full-width, sidebar-hidden, reading-mode]
→ Atelier reads the list → loads matching snippets from the vault library → applies to the note view.
- [ ] Build snippet resolver in Atelier plugin — reads
snippetsfield, maps to vault CSS library - [ ] Decide: does Atelier inject as
<style>tags or as body classes? - [ ] Migration pass — rename
cssclasses→snippetsacross all notes (Linter rule or one-time script)
cssclasses → snippets — Implementation
The Atelier plugin already does this exact pattern for theme: — reads frontmatter, sets data-theme on document.body. The snippets field will follow the same path as the existing cssclasses handler.
Current code (to adapt) :
const cssclasses = frontmatter.cssclasses || frontmatter.cssClasses;
// → prefixes each value → body.classList.add("cssclass-" + cls)
New code :
const snippets = frontmatter.snippets;
if (snippets) {
const list = Array.isArray(snippets) ? snippets : [snippets];
list.forEach((s) => {
newClasses.add("snippet-" + s.trim().replace(/\s+/g, "-"));
});
}
CSS side :
body.snippet-full-width .markdown-preview-view { … }
- [ ] Swap
cssclasseshandler forsnippetsinmain.js - [ ] Update class prefix from
cssclass-tosnippet- - [ ] Update all vault CSS snippet files to target
body.snippet-* - [ ] Vault-wide migration : rename
cssclasses→snippetsin all frontmatter
link → url — ✅ Renamed
Decision : link becomes url. Omit if absent.
Rationale :
- The official Obsidian Web Clipper writes
{{url}}to frontmatter natively — zero friction urlis semantically unambiguous.linkcould mean a wikilink, an anchor, a citation —urlmeans exactly one thing- No manual copy-paste from the browser — the clipper fills it automatically on every clip
url: "https://stephango.com/understand"
mode — ✅ Optional, three values
Decision : mode omit by default. Only declare when overriding read.
Vocabulary :
| Value | Behavior |
|---|---|
read (or omit) |
Reading view, normal line-width constraint (~700px), prose-optimized |
edit |
Opens in live edit mode |
source |
Source mode |
The raw mode vision :
Not a CSS addon. A proper first-class mode in the software. When you open a note with mode: raw, the Atelier plugin treats it as a distinct experience :
- Full viewport width — no line-width cap
- Monospace rendering friendly — code blocks, tables, token files breathe
- IDE-like contrast — structured data readable at a glance
- Paired with
snippet: full-widthor dedicatedrawbody classes for CSS control
This is a real opportunity to make the raw view feel like Zed — your vault as an IDE, not just a note app. The plugin owns the behavior, not CSS.
- [ ] Add
mode: rawhandling to Atelier plugin - [ ] Define the CSS for
body[data-mode="raw"]— full width, no padding romance, monospace-friendly typography - [ ] Test on wide tables, token files, structured data
- [ ] Decide: monospace base font for raw, or keep proportional with adjusted spacing?
type — ✅ Always explicit. Behavior-driven.
Decision : type is required. It determines plugin behavior, not just taxonomy.
Vocabulary :
| Value | Plugin Behavior | Content |
|---|---|---|
note |
None — regular note rendering | Knowledge document |
snippet |
Extract first css code block → load as CSS. Rest is docs |
CSS snippet + documentation |
font |
Extract first data block → load font metadata. Rest is docs | Font metadata + documentation |
reference |
Read-only, no extraction | External knowledge captured |
log |
Render as timestamped entry | Daily log / capture |
Key insight : These are not just categories. Each type has plugin logic attached. When you write type: snippet, the Atelier plugin knows :
- Look at the first
cssfence - Extract and load it
- Render the rest as documentation
This is behavior, not metadata. That’s why it’s never optional.
Open questions :
- [ ] Does
type: configneed special parsing for YAML/JSON blocks? - [ ] Should
type: snippetsupport multiple code blocks (first loads, rest are examples)? - [ ] How does
type: fontintegrate with the font-note system (69 fonts)? - [ ] Should there be a
type: moc(Map of Content) for navigation hubs? - [ ] Dataview — do queries filter by
type? Which types should appear inWHERE type = "note"?
visibility — TBD
Pending decision.
tags — ✅ Locked. Always explicit (even []).
Decision : tags is always written, even when empty.
Rationale :
- Carries semantic meaning —
maturity/seedling,status/active, etc. - Dataview relies on it existing for clean queries
- Empty list is not ambiguous — it’s explicit silence
- Linter maintains it, cost is zero
tags: []
tags: [maturity/evergreen, status/active]
theme — ✅ Optional. Omit if absent.
Decision : theme only written when overriding the vault default.
Rationale :
- Purely a UI preference, not structural
- 95% of notes use the vault-wide default theme
- Silence = use the default. Correct behavior, not ambiguity
- Only write when deliberately choosing a different curated theme
# Most notes: omit theme entirely
# Special case, override default:
theme: humanist
The Atelier plugin reads it and sets data-theme on body. Falls back to vault default when absent.
Brainstorm Session — type field semantics
Context : discovered that “snippet” notes have dual purpose: the first CSS code block is system-loaded, the rest is documentation. This blurs the line between “system asset” and “knowledge note.”
Realization : type is not just taxonomy. It’s behavior-driven metadata. The plugin consumes it to decide how to parse and load the note.
Example workflow :
User creates a snippet note:
type: snippet
title: "Full Width Layout"
Content:
## Description
A CSS snippet that removes line-width constraints...
```css
body.snippet-full-width .markdown-preview-view {
max-width: 100vw;
}
```
## How to use
Add `snippets: [full-width]` to your frontmatter...
Plugin reads type: snippet → extracts first css block → loads it → renders docs.
Same pattern could apply to :
- Font files (first block = metadata, rest = docs)
- Config files (first block = YAML/JSON, rest = explanation)
- References (no extraction, just read-only knowledge)
Decision made : type is required, never omitted. It’s not optional metadata.
Still open :
- How many types do we define? Just the critical ones (
note,snippet,font) or be comprehensive? - Do all types get full frontmatter, or do
type: snippetnotes have a lighter schema? - How does
type: snippetinteract with the snippets library system? Are they the source of truth, or generated? - Should
type: mocexist as a first-class type for Maps of Content (navigation hubs)?
publish — ✅ Locked. Single enum field, all states.
Decision : publish becomes a single field with four values. No separate visibility field.
Vocabulary :
| Value | Meaning |
|---|---|
false |
Unpublished. Doesn’t exist on web. |
public |
Published, fully discoverable. Listed in archives, feeds, search. |
unlisted |
Published, URL-only. Hidden from archives, feeds, search. |
private |
Published, access-controlled. Requires authentication. Excluded from search. |
Pipeline logic :
IF publish = false
→ don't build, don't publish
IF publish = "public"
→ build with full discovery (RSS, archives, SEO)
IF publish = "unlisted"
→ build but hide from discovery (URL-only access)
IF publish = "private"
→ build with auth gate (logged-in users only)
Schema :
publish: false # default — not on web
publish: public # fully public
publish: unlisted # hidden but accessible
publish: private # access-controlled
Advantages :
- One field, one source of truth — no impossible states
- No dependent logic —
visibilityfield eliminated - Cleaner Dataview :
WHERE publish = "public"instead of two-field conditionals - Safer default : every note defaults to
false(safest assumption)
Feature: data-stnd-mode attribute
Goal : the Atelier plugin reads the mode: property and sets a corresponding data-stnd-mode attribute on the document body, enabling clear CSS targeting.
Behavior :
On note open, Atelier reads frontmatter mode value and applies :
body.setAttribute('data-stnd-mode', modeValue); // 'read', 'edit', 'raw'
CSS targeting :
body[data-stnd-mode="raw"] .cm-editor {
max-width: 100% !important; /* full width */
}
body[data-stnd-mode="raw"] .markdown-source-view {
max-width: 100% !important;
}
body[data-stnd-mode="edit"] .cm-editor {
/* edit-specific styling */
}
body[data-stnd-mode="read"] .markdown-preview-view {
/* reading-specific styling */
}
Implementation :
- [ ] Add
modeto frontmatter reader in Atelier plugin - [ ] Set
data-stnd-modeattribute on body during note load - [ ] Update on mode change (if user toggles between edit/read)
- [ ] Document CSS patterns in Standard Manual
AI Architecture
The vault has a dedicated layer for AI interactions. Context is organized, versioned, and bootstrappable.
Location : Kernel/AI/ directory
Purpose : Provide AI agents with complete, current context about the vault, its structure, and its conventions so conversations start informed and coherent.
Main Context File
Kernel/AI/Context.md — the single source of truth for AI context.
This file is the bootstrap document. When starting any conversation with an AI agent about the vault :
- Start the conversation
- Provide the contents of
Kernel/AI/Context.mdas initial context - The AI understands the vault’s architecture, conventions, and current state
What goes in Context.md :
- Vault philosophy and principles
- Current schema (frontmatter, tags, types)
- Key decisions and their rationale
- File structure and organization
- Active projects and their status
- Conventions for documentation, naming, automation
- Known limitations and workarounds
Example opening message to AI :
“Here’s the current vault context. Use this as the source of truth for all questions about structure, naming, or conventions. Ask clarifying questions if anything is ambiguous.”
Then paste the content of Kernel/AI/Context.md.
How the Architecture Works
- Standard Manual (
Kernel/Standard Manual.md) — the schema and decision log - AI Context (
Kernel/AI/Context.md) — distilled for AI agents - Conversation — AI operates from Context.md, references Standard Manual for details
The AI layer is async-friendly. You can hand off work to an agent, come back later, give them fresh context, and they resume coherently.
Keeping Context Fresh
Every time a major decision is made or the vault structure changes :
- Update
Kernel/Standard Manual.mdfirst - Then distill changes into
Kernel/AI/Context.md - Version control both
The AI always operates from what’s actually true, not from stale context or memory.
- [ ] Create
Kernel/AI/Context.mdwith current vault state - [ ] Document what should be included/updated in Context
- [ ] Establish update protocol (when to refresh)
Meta Note
This manual is evolving. Right now it documents the technical schema, decisions, and brainstorm. But it’s becoming something larger — a complete portrait of the vault, the author, and how they work together.
Eventually, this manual will have a “Vault” section that encompasses philosophy, author context, architecture, and living decisions. Everything needed to understand not just how the vault works, but why and for whom.
When you sit down to review or expand the manual, remember : you’re not just documenting a system. You’re documenting a mind. Keep that in focus.
- [ ] Expand the manual with a “Vault” section — author, philosophy, architecture, principles (not just technical schema)
- [ ] Ensure AI Context.md stays a distilled version of the manual (not the other way around)
- [ ] Revisit this manual annually — it should grow as the vault grows