← The ADLC library
Open knowledge · 3

One concept per file: the filesystem as schema

The oldest data structure in computing turns out to be a decent knowledge schema, provided you accept one rule. Getting the cut right is harder than it sounds.

There is a file I have seen in more than one repository. It is called something like CONTEXT.md or ARCHITECTURE.md, it is about four thousand words long, and it contains, in no particular order: the deployment topology, three metric definitions, an explanation of why the team stopped using a message queue, some notes on the staging environment, a list of on-call escalation paths, and a paragraph near the bottom that begins “note that the customer_id column in the events table is actually the account id, for historical reasons.”

That last sentence is the most valuable thing in the file. It is buried in position eighteen of twenty-three, under a heading about something else, in a document that gets loaded in full whenever anybody asks the agent anything.

The Open Knowledge Format’s central structural rule addresses exactly this: one file, one concept. tables/events.md would hold that sentence and nothing else would compete with it. The file path is the concept’s identity, so there is exactly one place the events table is described, and anything that needs to refer to it links there.

This sounds like filing advice. It is actually a schema decision, and it has consequences worth working through.

The filesystem is a schema you already have

Every serious attempt at organisational knowledge before this reached for a database of some kind. Wikis have page trees and databases underneath. Catalogues have entity models. Knowledge graphs have triple stores and query languages. Each of these gives you real power, and each of them charges the same toll: the knowledge now lives inside a system, and getting it out means the system’s export format, on the system’s terms, if the system still exists.

A directory tree is a weaker data structure than any of those. It gives you hierarchy, unique paths, and nothing else. No types beyond a filename, no queries, no referential integrity, no constraints.

But look at what that weakness buys.

Paths are globally unique within a repository, and they are human-readable, and they are stable if you decide they are. Every tool ever written can address one. Every version control system tracks renames and content changes. Every developer already knows how to navigate one, and so does every model, because paths appear constantly in training data. There is no import step, no sync job, no drift between the system of record and a local copy, because there is no local copy.

Knowledge in a system

  • Identity is an internal record id
  • Access needs an API and credentials
  • History is the platform's revision list
  • Local copies drift from the source
  • Portability means an export format

Knowledge in a directory

  • Identity is a path you can read aloud
  • Access is opening a file
  • History is git, with diffs and blame
  • There is one copy, checked out
  • Portability means copying a folder
A directory tree is a much weaker data structure than a graph database. Most of what it gives up was costing more than it was worth.

The trade is real, and I do not want to pretend otherwise. You genuinely lose query power. You cannot ask “show me every metric derived from a table that contains personal data” and get an answer without writing a script that parses front matter and follows links. In a proper graph store that is a query. The bet OKF is making is that you will ask that question rarely, and that the everyday operation (an agent reading three relevant documents before making a change) is worth optimising for instead.

For agentic work I think that bet is correct, and the reason is a little subtle. Agents are not query engines. They are readers. A system optimised for expressive queries over structured facts is optimised for a consumer that does not exist in this workflow. A system optimised for “here are four documents, read them” is optimised for the consumer that does.

Why the granularity rule is the whole thing

If you take away only one rule, take the one about size, because it determines whether everything else works.

A concept file should be the amount of knowledge you would want loaded together, always, and never partially.

That is the operational definition. Not “a page,” not “a topic,” not “500 words.” The question to ask of any candidate document is: if an agent loads this because it needs part of it, is the rest of it also relevant? If yes, the cut is right. If the document contains three ideas and any given task needs one of them, you are paying context for the other two and, worse, you are inviting the model to blend them.

The failure in both directions is instructive.

Too coarse and relevance collapses. The four-thousand-word context file gets loaded for every question. The signal about customer_id is competing with the deployment topology for attention. Retrieval that operates at file granularity cannot distinguish “this file mentions events” from “this file is about events.” And edits collide, because everybody who wants to change anything changes the same file.

Too fine and coherence collapses. If metrics/churn/definition.md, metrics/churn/exclusions.md and metrics/churn/history.md are three files, then no document states what churn is. An agent that reads the definition without the exclusions produces a wrong number with a citation. Splitting a concept into parts means the reader has to reassemble it, and reassembly is where errors live.

Too coarse

The 4,000-word context file. Loaded for everything, relevant to nothing in particular, edited by everyone at once.

Too fine

Churn split across definition, exclusions and history. No single file is true on its own.

Cut by document

Filed the way a person would write a report: by audience and occasion, not by what the thing is.

Cut by concept

One thing in the world, one file. Load it whole or not at all.

The good cut is the one where loading the file partially would never make sense.

A practical test for where the seam goes

When you are unsure whether something is one concept or two, three questions settle it almost every time.

Would you ever cite one part without the other? If somebody would reasonably link to the exclusions rule without linking to the definition, they are separate concepts with a relationship. If citing one without the other would be misleading, they are one concept.

Do they change on different schedules? A table’s columns change when the schema migrates. The runbook for that table’s ingestion job changes when the operational reality changes. Different clocks, different files. Two things that always change together are usually one thing.

Do they have different owners? Not formally, necessarily, but in the sense of who would be annoyed if it were wrong. Ownership boundaries make good file boundaries because they match the boundaries of who will maintain the file, which is what actually determines whether it stays true.

That third one is the most useful and the least discussed. The maintenance question should drive the structure question, because a beautifully cut knowledge base that nobody owns section-by-section will rot uniformly, whereas an awkwardly cut one where every file has an obvious owner will mostly survive.

Path as identity has teeth

The rule that the path is the concept’s identity is easy to nod along to and surprisingly demanding in practice, because it means renaming a file is a semantic act, not a tidying act.

If metrics/churn-rate.md becomes metrics/customer-churn-rate.md, then in a strict reading you have destroyed one concept and created another, and every link into the old path is now a reference to something that does not exist. Markdown links do not fail loudly. Nothing throws. The agent simply finds no document and proceeds with whatever it can infer.

Teams that take this seriously end up doing three things, none of which the spec mandates but all of which follow from taking identity seriously.

Choose paths as though they are public APIBecause within your organisation they are. Name for what the thing is, not where it currently sits in the org chart.
Check links in CIA dead internal link is a broken reference, and the only way you will find out is by looking for it deliberately.
Treat a rename as a migrationUpdate the referring files in the same commit, or leave a stub that points at the new path and says so.
None of this is in the spec. All of it follows from the moment you agree that the path is the identity.

The link-checking one is worth flagging as the highest-value thing you can automate early. It is trivial to implement, it needs no models, and it catches the single most common way a knowledge base decays: not wrong content, but a graph that has quietly come apart while every individual file still looks fine.

Where this breaks down

Not everything is a concept. The one-concept-per-file rule assumes the world decomposes into nameable, separable things. A lot of institutional knowledge does not. “How we handle a customer who is technically out of contract but strategically important” is real, load-bearing knowledge that resists having a path. You can force it into a file called policies/strategic-exceptions.md and produce something so hedged it says nothing. Formats have a gravitational pull toward the knowledge they represent well, and the risk is that the knowledge base ends up full of the tractable half of what your team knows while the important half stays in heads, and everybody believes the base is complete.

The filesystem has no integrity constraints, and you will feel it. Nothing stops two files defining the same concept. Nothing stops a link pointing at a document of the wrong type. Nothing enforces that every metric names its source table. In a graph database, several of those are schema violations caught at write time. Here they are just things that are true about your folder until somebody writes a check. If your knowledge base is going to be load-bearing, you will end up building a small pile of validation, and you should budget for that rather than being surprised by it.

Granularity is a judgement call the spec cannot make for you, and it is v0.1. There is no guidance here that resolves the coarse-versus-fine argument in a specific domain, and reasonable people will cut differently. That is tolerable within one team and becomes a real friction across teams, which undercuts some of the interoperability story. Expect this to be an area where conventions accumulate on top of the spec rather than inside it.

Restructuring is expensive, so early mistakes persist. Because paths are identity, a bad early taxonomy is not free to fix. It costs a coordinated rename plus every referring link. Which means, in practice, teams live with the structure they invented in week one while they still understood the domain least. This is the same dynamic as a database schema chosen too early, and it deserves the same wariness: start with fewer, larger, more obviously-correct concepts and split them when the seam becomes evident, rather than designing a fifty-directory ontology up front.

And the maintenance cost compounds with the file count. Every cut you make creates another document somebody has to keep true. Fine granularity is better for retrieval and worse for upkeep, and upkeep is the thing that actually kills knowledge bases. A stale file with a confident path is more dangerous than a missing one, because the path implies somebody decided this was the answer.

The takeaway

One concept per file is a schema decision dressed as filing advice. It trades query power for portability, tool-independence and a structure every reader already understands, which is the right trade when your primary reader is an agent that reads rather than queries.

The cut that matters is the one where loading half the file would never make sense. Get that wrong toward coarse and relevance dies; wrong toward fine and coherence dies. Use the citation test, the change-schedule test and the ownership test, and let ownership win ties, because ownership predicts survival.

The next piece looks at the only thing the spec actually requires: the YAML front matter, why type is the field it chose, and how a couple of lines at the top of a document become the index an agent navigates by.