The wiki is a filesystem-first collection of Markdown notes
that gets rendered into a static site.
The source of truth is src/;
the build produces dist/;
and bin/wiki.py is the single tool that does both.
The source tree
src/
*.md -- maintained wiki notes (the source of truth)
sources/ -- immutable evidence artifacts
.obsidian/ -- minimal Obsidian portability layer
Top-level src/*.md files are maintained wiki notes.
Everything else under src/ is either evidence or configuration.
The build: bin/wiki.py
wiki.py is a single-file Python tool
that handles every wiki operation:
creating notes, linting, building, serving, deploying,
querying, searching, renaming, and importing sources.
The build converts src/ into a static site in dist/:
- Parse YAML frontmatter from every
.mdfile. - Convert Markdown to HTML using the
markdownlibrary. - Apply a bounded Obsidian-compatible syntax subset: wikilinks, callouts, tasks, Mermaid fences, Vega-Lite fences, inline and block math.
- Render the graph-marker directives (wiki-graph, tag-graph, tag-cloud) into interactive force-directed graphs.
- Apply a note template with navigation chrome (breadcrumbs, prev/next links, backlinks).
- Write the output to
dist/.
Incremental builds
The build tracks a stamp file.
just build skips the rebuild
when nothing is newer than the last build stamp.
just rebuild forces a full rebuild
by passing --force.
Rendering constraints
Only a bounded Obsidian subset renders. The following break the build and are rejected by lint:
- Embeds (page embeds using the exclamation-mark-bracket syntax)
- Block references
- Dataview queries
- Templater syntax
- Inline tags
- Obsidian comment markers
- Highlights (double-equals syntax)
- Extended task states (custom checkbox symbols beyond the standard three)
The rationale is renderer compatibility: the static build and the Obsidian editor must produce the same output, so the wiki restricts itself to the intersection of what both support.
Math rendering
Inline math (single-dollar delimiters) and block math
(double-dollar delimiters)
are converted to MathML at build time.
The conversion uses latex2mathml and emits a plain MathML element:
there is no MathJax or KaTeX script loaded at view time.
A note with math renders identically
whether served from dist/ or opened from file://.
Obsidian renders the same source
with its own MathJax-based math engine,
so the two environments may differ in layout
for complex multi-row environments.
The build therefore prefers
flat expressions with \text{} labels
over aligned or array environments.
Mermaid and Vega-Lite
Mermaid diagrams render in the browser at view time.
The static build emits <pre><code class="language-mermaid"> blocks;
a Mermaid script in the page template converts them to SVG.
Vega and Vega-Lite charts similarly render client-side.
The build emits <pre><code class="language-vega-lite"> blocks
with inline JSON specifications.
Both are fenced code blocks in the Markdown source:
```mermaid
flowchart LR
A --> B
```
```vega-lite
{"data": {"values": [...]}, "mark": "bar"}
```
The serve command
just serve dynamically renders src/ in a read-only local server.
It applies the same rendering pipeline as build
but serves from memory,
useful for previewing changes before committing.
just preview builds first
and then serves the exact dist/ artifact,
useful for testing the production output locally.
The dist/ directory
dist/ is generated output.
Never edit it by hand
and never treat it as canonical.
The deploy command uploads dist/ to Cloudflare Pages
after copying _redirects and _headers
from share/cloudflare/.
The template
The note template at share/templates/note.md
defines the HTML structure that wraps every rendered note.
It provides:
- A breadcrumb trail from the Index.
- The note’s rendered body.
- A backlinks section showing which other notes link to this one.
- Prev/next navigation links.
The template is applied during the build; editing it changes the structure of every rendered page.
This note cites no sources of its own.
Working out connections…
No sources are recorded.
Working out the neighbourhood…
Model contributions
Measured by git-blame lines per AI model (156 total).
{"width": 320, "height": 320, "data": {"values": [{"model": "MiMo V2.5", "label": "MiMo V2.5 (99%)", "lines": 155, "share": 0.9935897435897436}, {"model": "Claude Opus 5", "label": "Claude Opus 5 (1%)", "lines": 1, "share": 0.00641025641025641}]}, "mark": {"type": "arc"}, "encoding": {"theta": {"field": "lines", "type": "quantitative"}, "color": {"field": "label", "type": "nominal", "legend": {"title": null, "orient": "right"}}, "tooltip": [{"field": "model", "type": "nominal"}, {"field": "lines", "type": "quantitative"}, {"field": "share", "type": "quantitative", "format": ".1%"}], "order": {"field": "lines", "type": "quantitative", "sort": "descending"}}}