KognitaKognita.

Blog

The CLAUDE.md Hierarchy Falls Apart in a Monorepo

10 min read

Claude Code lets you nest CLAUDE.md files: one at the repo root, one in your home directory, and one in any subdirectory that needs its own context. In a small project this is tidy. In a real monorepo it becomes a sprawl of partially-overlapping files at a dozen levels of staleness, where you cannot tell which one loaded, which one is wrong, or whether the right one was even consulted.

The feature is sold as "scoped context." What you actually get at scale is fragmentation — the same codebase described in eight different files by eight different people at eight different points in time.

What the hierarchy looks like at scale

Picture a normal monorepo — a couple of apps, a few services, some shared packages — translated into the CLAUDE.md hierarchy:

One monorepo, eight context files
A real monorepo, in CLAUDE.md terms

  ~/.claude/CLAUDE.md            # user-level, your personal prefs
  /CLAUDE.md                     # root: "we use pnpm, turborepo"
  /apps/web/CLAUDE.md            # "Next.js, app router, RSC"
  /apps/admin/CLAUDE.md          # "legacy CRA, do not copy patterns"
  /services/payments/CLAUDE.md   # "Python, async SQLAlchemy"
  /services/auth/CLAUDE.md       # last edited 8 months ago
  /packages/ui/CLAUDE.md         # "Tailwind tokens live in..."
  /packages/sdk/CLAUDE.md        # ...does this even exist?

  8 files. 8 owners (in theory). 8 staleness clocks.

Each file was a good idea when someone created it. Collectively they are a maintenance surface nobody owns. The root file knows nothing about the packages; the package files know nothing about each other; and the auth service's file has not been touched since before the last rewrite. This is the team drift problem multiplied by the number of directories.

The questions the hierarchy can't answer

A monorepo exists precisely because the parts depend on each other. That is the one thing per-directory files are structurally incapable of describing:

Cross-package reality falls between the files
The questions the hierarchy can't answer

  -> Which CLAUDE.md actually loaded for THIS edit?
  -> When root says "async everywhere" and a package
     predates that rule, which wins?
  -> A change in packages/ui breaks apps/web — whose
     CLAUDE.md describes that relationship? (none of them)
  -> Who reviews 8 markdown files for accuracy on a PR? (no one)

The most important facts in a monorepo are the relationships between packages — and those live in the gaps between the files, owned by none of them. When a change in packages/ui ripples into three apps, there is no CLAUDE.md that captures the ripple, because each file only describes its own folder.

You don't know which file the model used

Even setting accuracy aside, the loading behavior is opaque in practice. Depending on where the agent is working and how deep the tree goes, you do not have a clear, auditable view of which files were pulled into context and which were not. When the model gets something wrong, you cannot easily tell whether it read a stale file, missed the relevant one, or saw two that contradicted each other. Debugging the grounding becomes its own task.

Conflicts make this worse. When the root file states a rule the team adopted last quarter and a package predates it, both statements are "true" in their own file, and there is no precedence the model reliably honors. You are left guessing which version of reality won.

A monorepo needs one representation, not many files

The fix is not better discipline across eight files — it is a single representation that spans the whole tree and understands the dependencies between its parts:

N partial views vs. one connected index
What a monorepo actually needs

  Per-file CLAUDE.md:
    -> describes one package, in isolation
    -> blind to cross-package dependencies
    -> fragments the picture into N partial views

  Cross-package semantic index:
    -> one representation spanning every package
    -> knows apps/web imports packages/ui imports ...
    -> answers "what breaks if I change this" across the tree

A cross-package index does not care about directory boundaries. It knows that apps/web imports packages/ui, that services/payments calls services/auth, and can answer the question that no per-folder file can: what breaks if I change this. That is the same cross-repo capability described in /init vs. a semantic index, applied within a single monorepo.

Where Kognita fits

Kognita indexes the whole monorepo as one continuously updated semantic representation, spanning every app, service, and package, and including the dependencies between them. There is no hierarchy of files to maintain, no ambiguity about which one loaded, and no blind spot at the package boundaries — because the index does not have boundaries, it has the actual import and call graph. The agent retrieves the relevant cross-package context per query, current as of the last merge, instead of assembling a guess from a scatter of folder-local markdown.

Final take

The nested CLAUDE.md model promises scoped context and delivers fragmentation: many files, many owners, many staleness clocks, and a structural blindness to the cross-package relationships that are the whole reason you have a monorepo.

A monorepo is defined by how its parts connect. You cannot describe that with a file per folder — you need one index that spans the tree and knows what depends on what.