Your Repo Is Public. Your Thinking Isn't.
Why the private half of a project's context needs a home outside the repo, and the tool I built to keep it there.
Every project has two bodies of context.
There’s the public kind: the code, the README, the docs you’d show anyone.
And there’s the private kind: half-formed ideas, decisions and the reasoning behind them, roadmaps you’re not ready to commit to, and the operational notes that only make sense from inside the work.
If you work with coding agents, that private context suddenly becomes load-bearing. An agent is only as good as the context you feed it. Too little, and it guesses. Too much, too early, and you’re cleaning up noise it didn’t need.
But private context has no natural home. Put it in the repo and you’ve published your rough thinking. Keep it out, and every session starts by re-explaining decisions you’ve already made.
Documented discipline is a blog post. Enforced discipline is a tool.
I could write down a rule for where private notes live and follow it for months. Then, the first time I was moving fast, I’d break it. A rule that only lives in memory has no teeth.
Dotbrain is what happened when I got tired of being the discipline myself.
The private half of a project
Dotbrain is a small CLI and a convention.
All the private thinking behind a project lives in one private repo of its own, outside every code repo: notes, decisions, in-progress plans, agent config, and the real issue tracker. Dotbrain calls each project’s slice of that home a brain.
Each project then gets wired. Run dotbrain wire from the repo root, and it drops a few gitignored symlinks into the code repo that point back into the brain:
myproject/
├── src/
├── README.md
├── .brain -> ~/dotbrain/brainspaces/myproject/.brain
├── .beads -> ~/dotbrain/brainspaces/myproject/.beads
└── .claude -> ~/dotbrain/brainspaces/myproject/.claude
.brain holds the notes, vocabulary, and decision records. .beads is the private issue tracker. .claude is the agent workspace, and every coding agent gets its own link; a Codex setup adds a .codex beside it.
From inside the project, all of it sits right there in the tree, where any agent or editor can read it. From git’s point of view, none of it exists: git status stays clean, and deleting the links leaves an ordinary repo behind.
The boundary isn’t a rule I follow. It’s the shape of the filesystem. Private state can’t be committed into the public repo by accident, because it was never inside the repo to begin with.
Symlinks guard the files, though, not the words. Nothing about the filesystem stops an agent from reading a private decision and paraphrasing it into a public commit message.
So the boundary ships its own rulebook. Every brain carries a DOTBRAIN.md spelling out what never crosses over: no brain paths, no decision-record identifiers, no mirrored content. Agents read it at the start of every session.
That half of the boundary is still a rule. But it lives in the tree, next to the content it protects, instead of in my memory or the agent’s.
What sits behind those links is everything that isn’t ready for the public repo, or was never meant for it.
Vocabulary, so an agent and I mean the same thing by the same words. Decision records, so the reasoning survives longer than the conversation that produced it. Rough ideas that need time to mature before they become code, docs, or public commitments.
Most ideas start nowhere near ready. If you let an agent work against them too early, the noise doesn’t stay contained. It leaks into commit history, and cleaning up the idea starts to mean cleaning up the repo too.
Two trackers, one direction
The issue tracker is where the boundary gets tested hardest, because a tracker is your planning made visible: milestones, half-formed epics, priorities you’ll change your mind about next week.
File all of that into GitHub Issues on a public repo and you’ve published a roadmap you never agreed to keep. Every abandoned issue reads like a broken promise, and every rough plan invites scrutiny it wasn’t shaped for.
So Dotbrain splits the job in two. GitHub Issues stays what it’s good at: public intake. Bug reports, feature requests, questions from people who use the thing.
The execution graph lives in the private tracker behind .beads: epics, dependencies, and the order I actually intend to do things in. That’s where agents pick up work, and where I plan without an audience.
Work flows in one direction. Public reports get triaged, and the ones worth doing are promoted into the private tracker. What comes back out is deliberate: a fix, a release note, a closed issue. Never the planning behind it.
Gitignore can hide context, but it can’t carry it
The obvious lightweight version of this is a gitignored notes folder. I ran some variant of that for years.
Its flaw is simple: gitignore can hide private thinking, but it can’t carry it. Untracked files don’t follow the work.
Spin up a fresh worktree for an agent task and it starts blind. Clone the repo on another machine and it starts blind. Move between a laptop, WSL, and a remote agent VM, and suddenly the most important context is wherever you last remembered to copy it.
Before Dotbrain, that left three bad options: copy context by hand, keep files synced across environments yourself, or skip the notes and let the agent infer decisions from the code.
The third one is the most expensive. You pay in tokens, time, and wrong guesses for context the agent should have just had.
With Dotbrain, the private home is a repo like any other. Clone it, run one wire command, and every project’s context surfaces in place: same files, same paths, whether it’s a worktree that lives for an hour or a machine you set up yesterday.
Agent config has the same problem
It isn’t just notes.
Agent configuration has the same homelessness problem: skills, subagents, per-tool settings, and the small bits of operating context that make one agent session behave like the last one.
I run more than one coding agent, Claude Code and Codex among them, and for a long time their configs lived nowhere in common. Every skill I wrote had to be linked into each tool, on each machine, by hand: a quiet tax, paid constantly.
In Dotbrain, that’s just more wiring. Skills and agent workspaces live in the same private home, and the .claude and .codex links carry them into each project, so whichever agent picks up the work finds its config already in place.
The boundary is the product
If you follow agent tooling, you’re probably already reaching for a category to file this under.
Dotbrain sits next to three crowded ones: persistent agent memory, execution trackers, and rule-file managers. Measured inside any of those categories, it loses on purpose.
It doesn’t build a richer memory store. It wires in the context you actually authored. It doesn’t build its own tracker. It depends on one you plug in. It doesn’t compete on rule-file cleverness. That’s table stakes, and it leaves that job to the agent runtimes themselves.
Give up all three fights and what’s left is the job none of those tools treats as the product: keeping the boundary. Private knowledge and execution live outside the code repo, wired in cleanly, and nothing crosses back over by accident.
The disciplined path has to be the lazy one
The bar Dotbrain has to clear is brutally simple.
Anyone reading this could hand-roll the same setup: a private repo, a handful of symlinks, a gitignore entry. A tool for it only makes sense if the disciplined path is also the lazy one.
The day cloning, wiring, and refreshing feels like more friction than rolling your own links and hoping you remember, Dotbrain stops earning its keep. That isn’t a caveat; it’s the design constraint the whole thing lives under.
The honest costs
Dotbrain isn’t hosted. It’s a home repo you own and manage: real infrastructure rather than a switch you flip. If that sounds like a burden, it might be one.
It also ships opinionated. The default issue tracker is beads, chosen because it’s purpose-built for agent-driven work and needs almost no setup, not because Dotbrain depends on it.
The engine is swappable. Point a project at a different tracker and the agent uses that tool’s CLI instead. The swappability is the tell: if the tracker can be replaced without the thing falling apart, the value was never the tracker. It was the wiring.
The tool itself is an ordinary public repo: code, docs, tests, nothing withheld. Everything it operates on lives in a separate private repo that the public one never mirrors: every project’s brain, tracker, and skill set.
That’s the same shape Dotbrain builds for every project: two bodies of context, one boundary between them, drawn around itself first.
If the problem sounds familiar, that’s the half I’m comfortable publishing: github.com/arminzou/dotbrain.