AI Coding in Monorepos: Scoping Tasks Across Package Boundaries
How to give AI coding agents the right context in a monorepo — scoping tasks to packages, sharing type contracts between sessions, and preventing cross-package regressions.

AI coding agents thrive on focused context. Monorepos actively work against that. A typical monorepo has dozens of packages with shared type contracts, coordinated versioning, and implicit coupling that only becomes visible when something breaks downstream. Handing an agent “add this feature to the API package” in that environment often produces code that compiles in isolation but silently breaks three consumers.
The discipline isn’t complicated, but it requires intentionality. Here’s how to work effectively with AI agents across package boundaries.
The core problem: agents default to incomplete scope
When you ask an AI agent to implement a feature in one package, it reads the files you point it at, generates code, and stops. If that feature changes a shared type, adds an exported function, or modifies a configuration that other packages consume, the agent has no reason to know about those downstream effects unless you tell it.
This creates a failure mode subtler than a build error: the code works in isolation, passes unit tests in that package, and only breaks downstream when someone integrates. Catching this late costs far more than preventing it early.
The fix is to explicitly tell the agent the blast radius of every change before it starts.
Scoping a task with an explicit dependency map
Before handing any cross-package change to an agent, write a short dependency map:
## Scope for this change
Package being modified: packages/api
Type being changed: UserRecord (packages/shared/src/types.ts)
Direct consumers of UserRecord:
- packages/api (primary change site)
- packages/web/src/components/UserProfile.tsx
- packages/cli/src/commands/users.ts
The change: add an optional `displayName` field to UserRecord.
Required outcomes:
1. Add the field to UserRecord in packages/shared
2. Update packages/api to populate displayName from the DB
3. Check packages/web and packages/cli for rendering sites
that need updating — list them, do not modify them
The last instruction matters. Ask the agent to enumerate downstream effects rather than implement them in the same task. One task that touches three packages is three opportunities for drift. Three tasks, each with focused context and a verification step, is three chances to catch mistakes early.
This is the same decomposition discipline from Task Decomposition for AI Coding Agents, applied to the package layer.
Feeding the right context per package session
A monorepo agent session should start with just the relevant package’s source — not the whole repo. When working in packages/api, point the agent at that package’s directory, not the root. Feed it only the files it actually needs.
For a new feature task:
Working in: packages/api (src/ directory only)
Context:
- Database client: src/db/client.ts
- Existing user queries: src/db/users.ts
- UserRecord type (from packages/shared):
{ id: string; email: string; createdAt: Date; displayName?: string }
Task: add getUser(id: string) to src/db/users.ts that fetches a user
by ID and returns null if not found. Use the existing db client pattern.
By pasting the relevant type definition inline instead of asking the agent to find it, you control exactly which version of the contract it’s working against. You also prevent the agent from wandering into shared code and accidentally modifying it, and you prevent it from inventing a type that’s slightly off from the canonical one.
Managing type contracts across packages
The riskiest class of monorepo change is a type modification in a shared package. These cascade silently — TypeScript catches them at compile time, but only if you build downstream packages too.
When you change a shared type, run the full workspace typecheck before writing any consumers:
# pnpm workspaces
pnpm --filter "..." tsc --noEmit
# Turborepo
turbo typecheck
If you can’t run the workspace typecheck in your AI session, structure the task to avoid the dependency: define the type change as its own task with its own verification step, then tackle each consumer in separate sessions.
A pattern that works well: paste the before/after diff of the type change into the consumer package’s session, not just the final type:
The shared UserRecord type was just updated. Before/after:
Before: { id: string; email: string; createdAt: Date }
After: { id: string; email: string; createdAt: Date; displayName?: string }
Find all places in packages/web/src/ that render user data and add
displayName rendering where appropriate. Do not modify anything outside
packages/web/src/.
The before/after diff gives the agent something to reason about. It can recognize “this field didn’t exist before, so any code written previously wouldn’t reference it” rather than having to infer that from the broader context.
Workspace-level instruction files
Most monorepos have conventions that span packages: how to import from shared packages, where types live, which packages own which concerns. These belong in a root-level instruction file that every session loads automatically.
For Claude Code, that’s CLAUDE.md in the monorepo root. For other tools, find the equivalent project configuration.
A useful structure for a monorepo CLAUDE.md:
## Package layout
- packages/shared — all shared types and utilities; no business logic
- packages/api — REST API, reads the DB, exposes JSON
- packages/web — React app, consumes packages/api over HTTP only
- packages/cli — CLI tool, shares types with packages/api
## Key rules
- Never import from packages/api directly in packages/web; use the HTTP API
- All shared types live in packages/shared/src/types.ts
- Never add business logic to packages/shared
## Build
- pnpm workspaces; run `pnpm build` from root to build in dependency order
- CI runs `turbo typecheck lint test` — all must pass before merge
This prevents the most common monorepo mistake: the agent importing from packages/api directly into packages/web because the type it needed was “right there” and the path of least resistance led straight through the package boundary.
On writing explicit rules — 0degrees Team: The “Never import packages/api in packages/web” rule seems obvious once it’s written down. Before we added it to our CLAUDE.md, an agent broke that boundary twice in the same week — not intentionally, just because the type was available and nothing said it was off-limits. Explicit negative constraints in the instruction file are cheap to write and eliminate an entire class of mistake.
Stating the right exit condition
The exit condition for a monorepo task needs to name the right verification scope. “The tests pass” is ambiguous — which tests?
Exit condition:
- `pnpm --filter packages/api test` passes
- `pnpm --filter packages/api tsc --noEmit` is clean
- No changes exist outside packages/api/src/
The scope constraint in the third line serves double duty: it tells the agent when to stop, and it makes it immediately visible if the agent wandered outside the expected boundary. If you look at the diff and see a change in packages/shared when the task was scoped to packages/api/src/, you know something went wrong before running a single test.
One session per package, one task per session
Monorepo agent work maps cleanly onto a session structure: one session per package, one task per session. Each session gets a clean context loaded with exactly the files and type contracts it needs. Each task produces output that becomes the explicit context for the downstream session.

The sequence for a cross-package feature looks like this:
- Session 1 — Modify
packages/shared/src/types.ts. Verify: workspace typecheck passes. - Session 2 — Update
packages/apiagainst the new type. Paste the updated type inline. Verify: package typecheck and unit tests pass. - Session 3 — Update each consumer, one at a time. Paste the before/after type diff. Verify: package typecheck passes per consumer.
This is slower than asking an agent to “just handle the whole thing.” It’s also consistently more reliable, because each session stays coherent and each step is independently verified before the next builds on it.
Monorepos reward the discipline
Agents in monorepos don’t fail because the models are bad at code. They fail because the model can’t see what it doesn’t know — it doesn’t know the type it’s editing is consumed by three other packages, or that the import path it chose crosses a package boundary that matters. It hasn’t seen the rule.
The discipline of scoped sessions, explicit dependency maps, inline type contracts, and workspace-level instruction files is the same discipline that makes monorepos maintainable by humans. The difference is that a human developer eventually learns the implicit rules through experience. An AI agent needs them stated every time — or written down once in a place that every session loads automatically.
For more on the instruction-file side of this, see Writing Effective Project Instruction Files. The context management patterns in Managing Context in Long AI Coding Sessions apply here too, especially the checkpoint format for carrying type contracts from one session to the next without re-opening the whole history.


