Skip to content

· 11 min read · Engineering

A Field Is Not a Process

A Field Is Not a Process

Twenty-one years ago I was writing wiki engines. In 2005 I worked at Socialtext, one of the first commercial wikis, on the then-radical idea that a team could keep its shared knowledge on a page anyone could edit. That same year, for a running contest on Ward Cunningham’s original wiki, I got a working wiki down to four lines. So when Andrej Karpathy posted his LLM Wiki idea in April, and Google shipped a spec that formalized a slice of it, I didn’t read it as news. I read it as a thread I’d been pulling on for two decades, with the same knot still tied in it.

The Wiki I Fit in an Email Signature

The Shortest Wiki Contest asked for the smallest source code that still ran a real wiki: automatic linking, editable pages, the whole contract. People got it to a few dozen lines. Building on the work done on FleaWi, PeeWee, and mostly PeWi, I got it to four, and called it SigWik because it fit in an email signature, 80 columns wide. Nick Clark named it, for the four lines. Doug Merritt’s reaction on the page is still the best code review I’ve gotten: Ok, now I am amazed. Here it is, 218 characters of Perl and shell, reproduced from the contest page:

#!/usr/bin/perl
use CGI':all';path_info=~/\w+/;$_=`grep -l $& *`.h1($&).escapeHTML$t=param(t)
||`dd<$&`;open F,">$&";print F$t;s/htt\S+|([A-Z]\w+){2,}/a{href,$&},$&/eg;
print header,pre"$_<form>",submit,textarea t,$t,9,70

I’m not showing this to brag about a stunt (or maybe I am). I’m showing it because it makes the boundary concrete. SigWik is the format: a folder of files, one file per page, links found by pattern. That part is genuinely small, a clever afternoon. Ok, I’ll be honest, several weeks of iterating. What four lines can’t contain is the part where a human comes back next week, sees the page is wrong now, and fixes it. The format is the easy half. It always was.

What Karpathy Proposed, and What Google Kept

Karpathy’s idea was never “a folder of markdown.” Read the gist and it’s the operations that matter: the model incrementally builds and maintains a persistent, interlinked wiki between you and your raw sources. His framing sticks with me: Obsidian is the IDE; the LLM is the programmer; the wiki is the codebase. Knowledge gets compiled once and kept current, instead of re-derived from scratch on every query the way retrieval-augmented generation does it.

But the pitch has a part that isn’t a folder at all. It describes ongoing upkeep, not just a layout. Karpathy spends a section on it: periodically, ask the model to health-check the wiki, hunting contradictions, stale claims a newer source overtook, and orphans nobody links to. He calls it the lint step. It’s the maintenance loop, and it’s what keeps the wiki from becoming a museum of things that used to be true.

Now Google’s Open Knowledge Format. OKF is a real, minimal, well-designed spec, version 0.2 as I write this, and standardizing the container is worth doing: a bundle is a directory, one file is one concept, the file path is the ID, and links are a plain graph. Two reserved files, index.md and log.md, do what they did in the gist. The container Karpathy sketched, Google wrote down.

The spec kept the structure. It left out the recurring check.

A Field Is Not a Process

OKF gives you a modified field. It records a fact: here’s when this concept was last written. Nothing notices that a source published last month contradicts a page written last year. You can have a perfectly valid, fully conformant OKF bundle that’s wrong, or out of date, and the format won’t say a word. Being wrong is not a validation error.

That’s the knot: a field is not a process. A timestamp is inert.

If you’re reaching for a wiki over a vector database, here’s the trap. RAG re-derives its answer from the chunks on every query, so its staleness is bounded by what it just retrieved. A wiki’s staleness is unbounded: it’s whatever the last curation pass left behind, however long ago that was. And a stale wiki page still reads as authoritative, so it rots faster than RAG because nobody suspects the well-formatted page.

I know this because I run a knowledge base that would have rotted if I hadn’t planned for it.

What okfctl Does

My own corpus is currently 254 curated concepts, the research and method notes my agents read before they do anything, stored as an OKF bundle. For a while it was a bundle with no process behind it, aging one commit at a time.

So I built the check the spec left out. okfctl is an open-source CLI, Apache-2.0, on GitHub. It’s a single static Go binary you install and point at a folder, and the whole thing is designed around one refusal: the core stays pure Go, offline, and model-free, so every command below produces the same answer on your laptop that it produces in my CI. What follows is that binary run against my real 254-node bundle. Every number is its actual output.

Conformance and Health Are Two Different Questions

Start with the most important pair. validate asks whether the bundle is a legal OKF bundle. lint asks whether it’s holding together as a body of knowledge. On my corpus, both come back clean:

$ okfctl validate ./bundles/knowledge
OK: bundle conforms to the OKF spec floor
$ okfctl lint ./bundles/knowledge
OK: no lint findings

Two green checks, two different floors. validate measures the bundle against the OKF specification. lint measures orphans, broken cross-links, and coverage gaps, the curation hygiene. A perfectly conformant folder can still be quietly rotting, and only lint catches it.

analyze: Make the Rot Visible

lint gives a pass/fail gate. analyze is the microscope. It walks the whole bundle and reports where the knowledge is weak, not where it’s malformed:

$ okfctl analyze ./bundles/knowledge
# OKF Corpus Analysis
254 node(s), 4123 internal link(s). Stale threshold: 180d.
...
## Coverage gaps — thin nodes (need expansion)
- casey/agentic-efficiency.md (9 lines)
- design/spacing-rhythm-and-density.md (13 lines)
...
## Structure — near-duplicate slugs (rename/merge candidates)
- security/authz/abac.md ≈ security/authz/rbac.md
...
435 actionable signal(s) across all dimensions.

Four hundred thirty-five signals on a corpus that just passed both gates. A nine-line concept that pretends to be a concept. Nine nodes with no citation behind them. Two access-control pages, abac.md and rbac.md, similar enough that one of them is probably swallowing the other’s traffic. None of that’s a spec violation. analyze is the timestamp’s missing conscience: it does the noticing the frontmatter field can’t.

eval: A Machine Gate on Trust

This is the command I’m proudest of. eval decomposes a node’s trustworthiness along four dimensions borrowed from the TACA framing: Transparency, Accuracy, Calibration, and Alignment. Only one of those can be checked without a model.

eval transparency is deterministic. It checks that a node’s provenance is actually there, that it carries a grade, that its internal citations resolve. Run against my corpus, it found two real problems:

$ okfctl eval transparency ./bundles/knowledge
grade-vocabulary: design/spacing-rhythm-and-density.md has authority: "DEPRECATED", an off-vocabulary value carried by only 1 node(s) (likely a typo/drift)
grade-vocabulary: design/ux-psychology/deceptive-patterns.md has authority: "high", an off-vocabulary value carried by only 1 node(s) (likely a typo/drift)
2 transparency finding(s)

Two nodes carrying authority grades that aren’t in the vocabulary, DEPRECATED and high, each the only node in 254 to use its value. That’s drift, the kind of slow schema erosion that no one notices until a query trusts the wrong page. okfctl’s own help text calls this “the first machine gate that touches trust rather than format,” and that’s the right claim, precisely because it’s a narrow one.

The other three dimensions need a model or the network to judge. Instead of guessing whether a claim matches its source, eval sample scaffolds an eval-set, one row per node with every extractable field pre-filled and the judgment slots left empty, and hands it to a human or an out-of-band LLM judge to complete. The tool computes no truth verdict it can’t verify on its own.

search: Query the Bundle With No Model and No Index

Search runs against the bundle with no embedding model and no prebuilt index, which matters if you want a curation check you can run on every commit without a GPU in the loop. Lexical mode matches title, tag, type, or body. The mode I reach for more is --neighbors, which walks the link graph instead of the text:

$ okfctl search --neighbors research/a-field-is-not-a-process.md ./bundles/knowledge
casey/usermd-anchor-restructure-decision.md Decision Memo depth=1
method/node-worthiness-graph-richness.md Research Brief depth=1
research/agentic-harness.md Research Brief depth=1
research/breadth-access-capability-layer.md Concept depth=1
research/core-instruction-files-and-memory-layering.md Research Brief depth=1
research/go-vs-rust-okfctl-cli-spike.md Research Brief depth=1
research/llm-wiki.md Concept depth=1
research/okf-write-time-curation.md Research Brief depth=1
research/rag-vs-write-time-curation.md Concept depth=1
research/semantic-recall-store-design.md Research Brief depth=1
research/the-invisible-moat.md Concept depth=1

The bundle is a graph, so I can ask it graph questions: what sits one hop from this concept? The answer is the neighborhood this very post grew out of, right down to the spike where I argued myself into building the thing in Go. Semantic vector search exists too, but as a separate plugin, deliberately outside the core.

migrate: Two Phases So It Never Learns to Guess

When OKF went from v0.1 to v0.2, two keys got renamed. migrate upgrades a bundle across that break in two phases. Phase one is pure read: it computes every mechanical edit and enumerates every edit that needs a judgment call, then writes a plan file and touches nothing else. Phase two applies only the mechanical edits, order-preserving and additive, and re-validates. The judgment items, a prose citation with no resource behind it, a rename with no recorded actor, are never guessed. They stay in the plan for a person to resolve.

graph export and serve: The Bundle Is a Graph, So Treat It Like One

graph export hands you the whole link structure as JSON or Graphviz DOT, so the 254 nodes and 4,123 edges become something you can pipe into other tooling. serve renders the same graph as an interactive page, assets baked into the binary. A knowledge base is a network of concepts. It can help to visualize it.

Here’s mine. graph export produces the DOT; a short script colors each node and its outbound edges by top-level folder and sizes nodes by degree, then Graphviz lays it out:

Terminal window
$ okfctl graph export --format dot ./bundles/knowledge > kb.dot
$ ./scripts/color-graph.py kb.dot | sfdp -Tpng > graph.png

The 254 nodes and 4,123 edges of my knowledge base, laid out by force-directed
graph, colored by top-level folder. Labels are stripped.

Every dot is a concept (node) and every line is a link (edge). That picture is also the argument: nothing in the folder format keeps a network that dense honest as it grows, which is what the rest of these commands are for.

One More Refusal: Plugins

Everything above is core, and core is pure Go and offline on purpose. The things that need a model or the network, semantic search, an HTTP API, live outside as okfctl-<name> plugins that the CLI discovers on your PATH. You can also write your own plugins.

Wire It In, or It Won’t Run

A command you have to remember to run is a command that eventually doesn’t. So the check has to sit inside the loop:

flowchart LR
    A[Write or edit a concept] --> B[Commit]
    B --> C{"okfctl lint --strict"}
    C -->|"orphan · stale link · contradiction"| D[Fail the build]
    D --> A
    C -->|clean| E[Merge]
    E --> F[Corpus stays true]
    F --> A

I run it at two points. A pre-commit hook stops a bad concept before it lands, and CI runs it as a hard gate that fails the whole build on any curation error. The modified field is still inert. The hook and the gate are what make it mean something, because now something runs when the folder changes.

There’s a measurable payoff, not just a hygienic one. When I moved the corpus to passage-level search indexing, so a hit points at the paragraph instead of the file, retrieval quality on the real corpus went from 0.545 to 0.909. A maintained bundle earns better answers than a pile of valid-but-unkept files.

Choose the Whole Idea, Not Half of It

If you’re choosing a wiki over RAG for your agents because you read the same gist I did, choose the whole idea. The folder is the part you get for free. The recurring, build-failing check that keeps it true is the part you have to build, or borrow, or you won’t have the thing you actually want. OKF is a good container. Use it, then add the tools and process so your information doesn’t go stale. okfctl is one static binary, and it’s the difference between a knowledge base and a graveyard that validates.