deployment
Agentic Planning For Cross Package Refactors Failure Patterns: practical systems guide
Technical field guide on agentic planning for cross package refactors failure patterns and fixes for teams building dependable AI coding workflows.
# Agentic Planning For Cross Package Refactors Failure Patterns: practical systems guide
When an AI agent suggests renaming `UserProfile` to `Profile` across twelve packages, it sounds straightforward. Then the agent opens six parallel PRs, three break integration tests in downstream services that depend on stable interfaces, and two introduce circular imports the compiler only catches after merge. The rename itself was correct—the orchestration failed.
Cross-package refactors expose a specific class of failure: agents excel at local transformations but struggle with dependency-aware sequencing, merge conflicts across concurrent changes, and the "blast radius" problem where a single bad edit in a foundational package cascades through everything that imports it. These aren't shortcomings of the underlying model; they're systems problems that need systems solutions.
The edit-validate gap in multi-package changes
Single-package refactors have natural boundaries. An agent can propose changes, run the test suite, see failures, and retry within a tight loop. Cross-package work breaks this. Consider a typical monorepo with `packages/core`, `packages/api`, and `packages/worker`. An agent decides to:
1. Extract shared types from `api` into `core`
2. Update `api` imports
3. Update `worker` imports
If the agent queues all three edits and validates only after, it discovers circular dependency issues too late. If it validates after each step, `api` and `worker` break between steps one and two because `core` changes aren't atomic with their consumer updates.
The failure pattern: **premature validation boundaries**. Agents treat each package like an independent unit of work when they're actually coupled by import graphs. Goatfied's plan-constrain-edit-validate loop helps here by letting you define validation scopes that match actual dependency boundaries—you can specify "only validate this cluster of packages together" rather than assuming package == validation boundary.
Dependency ordering and false-green builds
A more subtle trap: agents often generate plans in topological order (leaf dependencies first, consumers second) but execute them in whatever order the task queue provides. Here's what happens:
// Agent plan (logical):
// 1. Update core/types.ts
// 2. Update api/handlers.ts (imports from core)
// Actual execution (parallel queue):
// Thread A: api/handlers.ts starts
// Thread B: core/types.ts starts
// Thread A: validation passes (old types still present)
// Thread B: types updated
// Thread A: committed
// Thread B: committed
// Result: api imports stale types, but CI was green at commit time
This is **optimistic parallelism** meeting import-time coupling. The fix isn't just "run sequentially"—that kills throughput for genuinely independent changes. You need the constraint system to model dependencies explicitly. In Goatfied's architecture, you'd declare:
constraints:
- name: type-migrations
depends_on: [core/**/*.ts]
blocks: [api/**/*.ts, worker/**/*.ts]
The agent can parallelize freely until it touches something in `core`, at which point consuming packages wait. The tradeoff: slightly slower execution for correctness guarantees that matter more in production.
The "works locally" integration test problem
Agents often validate changes by running each package's test suite in isolation. This catches unit test failures but misses integration contract breaks. A real example: an agent renamed a REST endpoint path in `api` and updated all call sites in `worker`. Both test suites passed. The staging deploy broke because a third service—external to the monorepo—hit the old path.
The pattern: **isolated validation scope**. The agent's mental model stops at the monorepo boundary. For cross-package refactors, you need:
- Contract tests that verify interface stability (OpenAPI specs, proto definitions, exported type signatures)
- Explicit markers for public API surfaces vs internal implementation
- A staging environment where integration tests run against the composed system
Goatfied's validation gates let you inject custom scripts here—`validate: integration-tests.sh` that spins up all services and runs end-to-end flows. The agent doesn't need to understand your entire architecture; it just needs to know "this validation must pass before merge" and interpret failures.
Merge conflict detection at plan time
Large refactors often spawn multiple concurrent branches. An agent creates PR #1 to migrate `UserService`, while another task creates PR #2 to add new methods to the same class. Both CIs pass individually. Merging the first invalidates the second, but the agent doesn't know until someone tries to land it.
The failure: **no conflict prediction**. Agents work in isolated branches and don't model future merge state. A practical mitigation:
1. Lock files or directories when an agent starts work on them (coarse but effective)
2. Use smaller atomic changes—prefer ten 50-line PRs to one 500-line PR
3. Revalidate at merge time, not just at initial commit
The Goatfied agent loop's `retry` phase helps here: when a PR fails to merge cleanly, the agent sees the conflict diff as a validation failure and can regenerate the edit against the current main branch. This only works if your constraints are clear enough that the agent knows what invariants to preserve across rebases.
Version skew in distributed teams
In larger engineering orgs, different teams consume different versions of shared packages. An agent updates `@company/auth` to v2 with breaking changes, migrates the `web-app` team's code automatically, but the `mobile-app` team is pinned to v1 for stability. The refactor breaks their builds during the next dependency update.
The pattern: **invisible consumer contracts**. The agent sees the monorepo graph but not the external dependency graph. Solutions:
- Maintain a CODEOWNERS or dependency manifest that surfaces which teams consume which packages
- Run cross-team integration CIs before landing breaking changes
- Use feature flags or adapters for gradual migrations (agent adds shim layer, consumers migrate individually)
For self-hosted Goatfied deployments, you can configure the planner with external dependency metadata—even simple JSON mapping package names to Slack channels for approval workflows. The agent then knows to file a tracking issue and pause rather than assuming it can migrate everything.
Practical constraints for safer refactors
The theme across all these failures: **missing or mismatched constraints**. Agents need explicit rules about:
- What must validate together (dependency clusters)
- What can run in parallel (independent subtrees)
- What needs human review (public API changes, version bumps)
- What counts as success (compile + lint + unit tests vs integration tests)
In Goatfied's compile-first model, you define these upfront. The agent's plan phase generates a DAG of edits, the constrain phase prunes invalid paths (circular deps, blocked files), and the validate phase runs the right scope of checks. This doesn't eliminate failures—it shifts them left to plan time where they're cheaper to fix.
Related reading
- [Self-hosted AI coding assistants: a complete Goatfied deployment guide](/blog/self-hosted-ai-coding-assistants-goatfied-deployment-guide)
- [Deployment playbook #2: practical Goatfied tactics for shipping PRs](/blog/goatfied-deployment-playbook-2)
