Skip to main content

The Poor Man's Monorepo

The system tier of the context hierarchy. A real monorepo is a months-long migration most teams never finish. But the thing you actually want from one — agents that can see and change your whole system at once — you can get in an afternoon. Put every repo under one parent directory and give that directory an .agents/ of its own.

Point an agent at auth-service and it can only see auth-service. It doesn't know that web-app consumes the endpoint it's about to change, or that gateway enforces the contract. So it ships a change that is locally correct and globally broken.

1
parent directory
N
repos, untouched
1
system-level .agents/
0
migration

The polyrepo blind spot

Most systems are not one repo; they are a dozen services, each in its own. That is fine for humans, who hold the system in their heads, but it blinds agents to everything outside the repo they're pointed at. Cognition's Don't Build Multi-Agents names the underlying principle: every action carries implicit decisions, and agents working from fragmented context make conflicting ones — which is exactly what per-repo silos guarantee. A real monorepo fixes this by putting everything in one tree — but it is an expensive, invasive migration: build systems, CI, tooling, and years of muscle memory all have to change. You can skip all of that.


The setup

Make a parent directory. Clone each service repo into it. They stay exactly what they were — independent git repos with their own history, CI, and their own .agents/. Then add one new thing at the top level: a .agents/ that describes the system rather than any single service.

A system as a directorythree repos, one shared briefing
~/system/
├─ .agents/
├─ ARCHITECTURE.md
├─ API_CONTRACTS.md
├─ SYSTEM_MAP.md
├─ gateway/
├─ .agents/ · .git/
├─ auth-service/
├─ .agents/ · .git/
├─ web-app/
├─ .agents/ · .git/
Every service repo stays its own git repo. The parent directory adds one thing: a top-level .agents/ that describes the whole system.

The whole setup is a handful of commands — no build-system surgery, no rewrite:

terminal
mkdir ~/system && cd ~/system
git clone git@github.com:acme/gateway.git
git clone git@github.com:acme/auth-service.git
git clone git@github.com:acme/web-app.git

# add the system-level briefing
mkdir .agents # then write SYSTEM_MAP.md, API_CONTRACTS.md, ARCHITECTURE.md

The parent .agents/ holds what no individual repo can: the architecture across services, the contracts between them, and a system map of who calls whom.

.agents/SYSTEM_MAP.md
# System Map

> Agents: Planner · Implementer · Reviewer · Risk

## Services
| Repo | Owns | Talks to |
|--------------|---------------------------|----------------|
| gateway | edge routing, rate limits | auth, web-app |
| auth-service | login, sessions, tokens | gateway |
| web-app | the customer UI | gateway |

## Cross-cutting contracts
- All service-to-service calls go through gateway; no direct calls.
- Auth is a JWT in an HTTP-only cookie, minted by auth-service.
- Rate limits live in gateway and are surfaced to users by web-app.

Why it works

With the system context in place, cross-repo work stops being guesswork. An agent given "add a per-IP rate limit and surface it in the UI" reads the parent briefing, learns the limit lives in gateway and the surface in web-app, edits both, and opens a coordinated PR in each repo.

Without the shared briefing, the web-app agent wouldn't know the gateway change was coming or which status code to handle. With it, the contract is explicit and the two PRs are built to meet in the middle. The same grounding makes a cross-repo Reviewer possible: it sees that a gateway response shape changed and checks whether web-app still matches.


Two levels that layer, not compete

Each service keeps its own repo-level .agents/ describing that service in detail — its style guide, its database, its security rules. The parent directory adds only what no single repo can know: the boundaries between services and the contracts across them. An agent working in gateway reads gateway's own briefing for local detail and the parent briefing for system shape, and the two compose without duplicating each other.

That separation is what keeps it maintainable. When a service's internals change, its own .agents/ changes and the parent doesn't care. The parent only changes when the relationships change — a new service, a new contract, a call path that didn't exist before. Local churn stays local; the system map moves only when the system actually does.


What you give up

This is not a real monorepo and does not pretend to be. There is no atomic commit across repos, no unified build graph, no single CI run. What you get instead is most of the benefit — agents and humans reasoning about the system as a whole — for none of the migration cost.

PolyrepoPoor man'sReal monorepo
Cross-repo agent contextnoyesyes
Setup costnonean afternoonmonths
Atomic cross-repo commitnonoyes
Unified CI / build graphnonoyes
Existing tooling untouchedyesyesno
The poor man's version buys the one thing agents actually need — system-wide context — without the migration a real monorepo demands.

When to graduate

This is a stepping stone, and it is honest about it. You outgrow it at the point where the coordination you're doing by hand starts to hurt: when changes routinely have to land atomically across repos, when you want one CI run that understands the whole build graph, or when keeping every repo cloned and current becomes its own chore. That's the signal to invest in a real monorepo — and by then you'll have a living system map to migrate from.

There's one more tier above this one. When the system map itself outgrows what fits in a file — dozens of services, knowledge scattered across teams and years of incidents — you stop authoring and start indexing: the org-wide context layer, RAG over everything, retrieved on demand. Same idea, one level wider. That's the top of the context hierarchy.