An agent cannot use a note it cannot find. Giving it every note at once solves that problem by creating another one: now the useful context is buried among material that has nothing to do with the task.
This is the practical problem behind a personal knowledge base for agents. I already use Obsidian to organise research, decisions, and drafts. This series began as notes in that vault. Yet a directory full of Markdown does not become agent memory merely because an agent can read it. It needs an information architecture: a small entry point, explicit routes, and a boundary around what may be loaded or changed.
In the previous chapter, I argued that context engineering is about designing the environment around the instruction. A personal “Brain OS” is that idea applied to everything outside a single codebase.
An Operating System Made of Files
Muratcan Koylan’s Personal Brain OS is an unusually complete example. He describes a system of more than 80 files, using Markdown, YAML, and JSONL for material such as goals, contacts, research, decisions, failures, and writing guidance. It lives in Git and can be opened directly in Cursor or Claude Code. There is no application server or vector database between the agent and the information.
The number of files is eye-catching, but it is not the interesting part. Copying all 80 would be a good way to acquire someone else’s filing habits. The transferable idea is that access and loading are separate concerns. The agent may have access to the repository, but it only loads the part relevant to the current job.
The Router Is the Front Door
The router is the one file small enough to load for every task. It does not answer the request or summarise the vault. Its job is to recognise the kind of work, point the agent towards the next instruction file, and state any obvious exclusions.
The public example’s routing file describes three levels of progressive disclosure:
- A small routing file is available at the start. It maps a request to a domain.
- The selected domain provides its own instructions and file inventory.
- Data files are read only when the task needs them.
In a repository-backed system, the root AGENTS.md can play that role:
# Routes
- For writing or editing, read `writing/AGENTS.md`. Do not load `personal/` or `finance/`.
- For project planning, read `projects/AGENTS.md` and `projects/current-project.md`.
- For a question about a past technical choice, search `memory/decisions.jsonl` for active records matching the project and subject.
## Context loading protocol
1. Before opening routed data, create `.context/<task-id>.yaml` with the task, selected route, proposed paths, and excluded domains.
2. Check the proposed paths against the permitted roots. Ask for approval before adding a sensitive domain.
3. Open the accepted paths. After each read, add the exact file or record ID to `loaded`.
4. Set `status: compiled` and keep the manifest beside the task output.
These routes do not all end at the same kind of file, and that is fine. A writing request hands control to another set of instructions. A planning request needs both instructions and current state. A question about an old decision needs records rather than another handbook. The router only needs to identify the next bounded step.
A writing task can load the voice and research modules without also reading contact history. Preparing for a meeting can load contacts and previous interactions without dragging content templates into the context window. The intended lookup is short: route to the module, then open the data.
At runtime, the agent starts with the root router, matches the request to a domain, reads that domain’s instructions, and follows the more specific routes it finds there. If a task crosses domains, it can propose two routes instead of searching the entire vault. That proposal becomes the input to context compilation later in the workflow.
This is a better mental model than “memory”. The system is not recalling everything it knows. It is resolving a path through a small dependency graph. The router can describe an access boundary, but it cannot enforce one if the agent can still read every folder. Tool or filesystem permissions must do that job.
Progressive Disclosure Is an Attention Decision
There is a reason to keep that path short. The Lost in the Middle study found that, on its multi-document question answering and key-value retrieval experiments, model performance often fell when relevant information appeared in the middle of a long context. The result is not a universal law about every model and task, but it is enough to reject a naive assumption: fitting text into the context window does not guarantee that the model will use it reliably.
Progressive disclosure helps in two ways. It spends fewer tokens, and it reduces competition between relevant and irrelevant instructions. The router should therefore contain routing information, not compressed copies of every module. If the “short” entry point grows into a handbook, the hierarchy has collapsed back into one large prompt.
A minimal version could look like this:
brain/
├── AGENTS.md # routes tasks to domains
├── projects/
│ ├── AGENTS.md # how project context is organised
│ └── current-project.md
├── writing/
│ ├── AGENTS.md # voice and review workflow
│ └── style.md
└── memory/
├── decisions.jsonl
└── failures.jsonl
The root file does not explain how to write, plan a project, or interpret a past decision. It tells the agent where those explanations live and under which conditions to load them. Module files do the same for their narrower domain.
There is a cost. Retrieval becomes part of the system, so broken routes and vague filenames can hide useful information just as effectively as a missing note. A route such as “read relevant files under knowledge/” delegates the hard decision back to the agent. “For a dependency choice, read memory/decisions.jsonl and the project’s constraints” is more useful because it names both the trigger and the source.
Match the Format to the Job
Koylan’s implementation uses a sensible format-function mapping: Markdown for narrative, YAML for configuration, and JSONL for logs. I would treat that as a starting point rather than a rule.
Markdown should be the default for prose because humans can edit it, agents can read it, links are cheap, and Git produces understandable diffs. Project descriptions, working agreements, research notes, and explanations of past decisions all fit naturally.
YAML works for small configuration documents where hierarchy and comments matter. It becomes unpleasant when deeply nested, and small indentation mistakes can change meaning. If the information is mostly an explanation with a handful of fields, Markdown frontmatter is often simpler than turning the entire document into configuration.
JSONL is useful for a stream of independent records. One decision or failure per line can be appended, filtered, and reviewed without parsing one enormous JSON array. Koylan uses a schema record at the start of each log and marks deletions as archived, preserving history.
But JSONL is not append-only by magic. An agent with write access can still replace the file. Append-only behaviour needs to be enforced by the tool, a validation hook, or file permissions. Diff review can catch a replacement, and Git can recover committed history; neither prevents the overwrite.
An illustrative JSONL decision record occupies one line:
{"id":"decision-017","date":"2026-04-20","subject":"event-store","decision":"retain-postgres","reason":"current volume does not justify another datastore","revisit_when":"write latency exceeds agreed SLO","status":"active"}
The important field is not the chosen technology. It is the reasoning and the condition under which the decision should be reconsidered. “We use PostgreSQL” is a fact. “We kept PostgreSQL because the added operational cost was not justified, and we will revisit after this threshold” contains judgement the agent can apply to a similar question.
Memory Needs Provenance
Facts without provenance become surprisingly dangerous once an agent starts acting on them. A note may be stale, speculative, copied from an unreliable source, or true only for one project. The system needs enough metadata to tell those cases apart.
For durable records, I would include:
- who made or approved the entry;
- when it was written and last reviewed;
- which project or domain it applies to;
- the evidence or source behind it;
- whether it is a fact, decision, hypothesis, or generated suggestion;
- when it should be revisited.
This is also where human and agent writing should diverge. In Vin’s Obsidian and Claude Code walkthrough, he explains why he wants the vault to remain human-written: agent output stays outside until he decides what belongs in it. That preserves the vault as a record of his thinking rather than allowing generated summaries to become sources for later generated summaries.
I find the boundary more important than the exact rule. Some workflows need agents to update task state or append session logs. That can be safe if generated material is clearly labelled and isolated. What should be avoided is silent provenance loss: after a few editing cycles, nobody can tell whether a “decision” came from a person, an agent, or a discarded brainstorm.
A practical arrangement is to keep approved knowledge and generated inbox material separate:
brain/
├── knowledge/ # human-authored or explicitly approved
├── agent-inbox/ # generated candidates, never trusted implicitly
└── activity/ # operational logs with a retention policy
Promotion from agent-inbox/ to knowledge/ is then a review action, not an automatic side effect of running a command.
Why Obsidian Helps, and Where It Does Not
None of this requires Obsidian. A Git repository and a text editor are enough. Obsidian is useful because links and backlinks make relationships between Markdown notes visible, while its command-line interface exposes files, links, unresolved references, orphans, and dead ends to tools.
Those relationships can improve retrieval. A project note can link to its architecture decisions, people, and research; an agent can follow those edges instead of searching the whole vault. Orphan and dead-end reports can also expose information that has become difficult to reach.
Links do not remove the need for routing, though. A large graph is still a large graph. Backlinks show that two notes are connected, not whether the connection matters for today’s task. They help the agent traverse context after the system has selected a useful starting point.
There is also a less exciting concern: a personal vault may contain financial, medical, employment, or relationship information. Giving an agent access to the whole vault enlarges the blast radius of a bad command, a compromised tool, or malicious content copied into a note. Domain separation should therefore be an access boundary as well as a token optimisation. Separate folders do not enforce that boundary; the tools or filesystem permissions must restrict access. The writing agent probably does not need the finance folder at all.
Compile Context for the Task
I find “compilation” a useful way to describe the final step. The vault is storage; the context presented to the model is a temporary artefact built for one task. A compiler does not copy every source file into every binary. In the same way, a context loader should follow the route, select the relevant records, and leave an inspectable manifest of what it included.
There is no hidden Brain OS process generating the YAML in the example. In the smallest implementation, the context-loading protocol in the root router tells the agent to create and maintain it. For review-api-design, the agent matches the request to the project route, reads projects/AGENTS.md, and writes an initial file like this before opening the proposed data:
task: review-api-design
status: proposed
route: projects/AGENTS.md
proposed:
- projects/payments/constraints.md
- projects/payments/api-contract.yaml
- memory/decisions.jsonl#decision-017
excluded_domains:
- personal
- finance
loaded: []
A human or a wrapper can now check those paths against the permitted roots, with a separate approval step for sensitive material. The agent then opens only the accepted paths and adds each successful read to loaded. The contents returned by those file reads become part of the working context; the YAML does not.
Once loading finishes, the same file becomes the audit manifest:
task: review-api-design
status: compiled
route: projects/AGENTS.md
proposed:
- projects/payments/constraints.md
- projects/payments/api-contract.yaml
- memory/decisions.jsonl#decision-017
loaded:
- projects/payments/constraints.md
- projects/payments/api-contract.yaml
- memory/decisions.jsonl#decision-017
excluded_domains:
- personal
- finance
The manifest can live beside the task output or in a task log. If the agent loads another file later, that read should be added too. The #decision-017 suffix above is an application convention, not a feature of JSONL: a loader must scan the records for that ID, or the agent must perform the equivalent search and record which line it used.
This minimal audit is self-reported. It is useful for debugging, but it is not independent evidence that the agent avoided every other file. If that distinction matters, the file-reading tools need to sit behind a wrapper which records successful reads and generates the loaded list from tool events. The router still chooses the route; the wrapper supplies the trustworthy audit trail.
Now a poor answer can be diagnosed. Perhaps the route omitted a decision, the loader selected the wrong record, the decision was stale, or an irrelevant module displaced useful material. Without the manifest, “the model forgot” becomes the explanation for every retrieval failure, which tells us nothing we can fix.
Context compilation also provides a natural place to apply policy: remove secrets, cap large tool outputs, reject untrusted instructions, and require approval before loading a sensitive domain. Those checks are easier to reason about before content enters the conversation than after an agent has already acted on it.
The manifest records those decisions; it does not enforce them. An excluded_domains entry is trustworthy only when the loader refuses those paths or the agent never receives access to them in the first place.
Start With the Smallest Useful Brain
Koylan’s public implementation is valuable because it shows how far the pattern can go. It would be a poor starting checklist.
My recommendation is to begin with one router, two or three domains, and one decision log. Use real tasks for a few weeks. Record where the agent searches unnecessarily, loads too much, misses a route, or applies stale guidance. Only add structure in response to an observed retrieval problem.
The first version should answer four questions:
- What does this agent need to know every time?
- What should it load only for a particular kind of task?
- Which records contain reasoning worth reusing?
- What is the agent allowed to write back?
If those answers are explicit, the files can remain delightfully boring. A personal Brain OS is not valuable because it resembles an operating system or because it contains a heroic number of notes. It is valuable when an agent can retrieve one relevant piece of judgement, know why it should trust it, and leave everything else alone.