Multi-Agent Refactoring Failure: Architecture Drift, Test Boundary Violations, and Missing Engineering Guardrails
This article reviews a three-week multi-agent refactoring effort that ultimately had to be discarded. A product manager with development and AI research experience used two coding agents in alternating turns to evolve a personal content-processing tool into a more complex agent system. The system appeared to run, but the architecture did not match the specification, the skill registry contained placeholder “fake skills,” unit tests connected to production inference endpoints, and token consumption reached about five times the normal budget. The article analyzes the root causes—non-executable specifications, missing shared contracts across agents, weak environment isolation, flawed test strategy, delayed budget observability, and insufficient human review—and presents eight engineering guardrails covering executable contracts, multi-agent boundaries, startup self-checks, verifiable skill registration, test layering, budget controls, review cadence, and incremental rollback.
Case Review: Why a Three-Week “Automated Refactor” Was Thrown Away
A product manager with a development and AI research background planned to evolve a personal content-processing tool into a more complex agent system. The initial requirements were not complicated: download audio, transcribe audio, and generate summaries. The system then expanded to include social content scraping and summarization, scheduled orchestration, an email entry point, a personal knowledge base, and further workflows such as deep research, daily reflection on coding assistant usage records, deriving new skills from those reflections, and automatically proposing instruction optimization suggestions.
To make the system truly orchestratable, he wrote a set of specifications intended to implement an agent runtime, an agent registry, independent agent scheduling, and a unified access layer. During implementation, he did not write code line by line. Instead, he had two coding agents take over in alternating turns and refactor continuously for about three weeks. During this period, roughly 90% of his time relied on coding tools, and in his personal projects he let the two assistants advance the work in alternation. After three weeks, when he read the code carefully, the problems surfaced in a concentrated way:
- The architecture was not implemented according to the specification. The registry, scheduling boundaries, and runtime state management existed only as surface-level structures. Critical paths still relied on hardcoding and local patches.
- The skill registry looked complete but actually contained “fake skills.” Entries existed, but they could not be truly discovered, scheduled, observed, or rolled back by the runtime. They were mostly placeholder implementations.
- Unit tests connected directly to production inference endpoints. When the tests ran, they made real calls to production model services. In this case, there were about 30 rounds of calls in a single day, and token consumption was about five times the regular budget.
- Because he had focused only on runtime results for a long time and did not review code diffs, architecture drift and test boundary violations were not intercepted in time. In the end, the only option was to start over.
The key lesson from this case is not that coding agents are unusable. Rather, agents are good at local generation and rapid patching, but they do not automatically take responsibility for system architecture, environment isolation, test authenticity, or budget governance. Giving a specification to an agent does not mean the specification has been executed. Making tests pass does not mean the tests have stayed within their boundaries.
Root Cause Analysis: The Problem Was Not Generation Speed but Missing Engineering Guardrails
First, the specification was not executable. If a natural-language specification lacks domain models, interface contracts, state machines, acceptance cases, and non-functional constraints, an agent can only guess based on local context. It may generate directories and class names that “look like a registry” without implementing scheduling semantics. A specification must be transformable into checkable contracts, not merely a document for humans to read.
Second, multiple agents taking over in alternating turns lacked a unified contract. When two coding agents modify the system alternately, inconsistencies in naming style, error handling, state boundaries, and dependency direction are likely. Without shared interface definitions, change logs, code ownership, and conflict resolution mechanisms, locally correct changes can accumulate into globally incorrect results.
Third, environment isolation was not implemented as a hard gate. Tests accidentally connecting to production inference endpoints is usually not a single-point mistake. It reflects the simultaneous absence of configuration layering, key permissions, network policies, and startup self-checks. The test environment should use in-memory doubles or sandbox models by default, while production endpoints must require explicit approval and independent credentials.
Fourth, the test strategy mistook “it runs” for “it validates correctness.” Unit tests should use deterministic doubles; integration tests should enter a sandbox environment; and production environments should allow only controlled read-only smoke tests or shadow validation. If production endpoint resolution appears in test code, the process should fail directly at startup.
Fifth, budget and observability lagged behind. In this case, about 30 rounds of calls in a single day and a budget of about five times normal indicated a lack of real-time dashboards and circuit breakers for call volume, token consumption, latency, error rate, and abnormal retries. The more automated the agents are, the earlier budget gates must be placed.
Sixth, the human review cadence was missing. Not examining the code carefully for three weeks was equivalent to handing architecture decision-making entirely to the generation process. A reasonable approach is to review change summaries daily, conduct architecture reviews weekly, require human confirmation for critical interfaces, and maintain architecture decision records.
Engineering Guardrails: Move from Post-Hoc Detection to Pre-Execution Blocking
- Turn specifications into executable contracts. Before each refactoring task, define domain objects, inputs and outputs, error semantics, state transitions, and acceptance cases. Keep task granularity small and change only one boundary at a time. For example:
Task: Implement the agent registryContract:- register(agent_spec) must validate unique names and complete capability declarations- discover(capability) must return a callable handle, not a placeholder object- invoke(handle, input) must log calls, budget, and errorsAcceptance:- Empty skills cannot be registered- Duplicate registration of a same-name skill must fail- Unavailable skills must not enter the scheduling queue
Multi-agent orchestration needs boundaries. Assign explicit module ownership to each agent, freeze shared interfaces before implementation, put all changes into a unified change log, and require contract tests for cross-module modifications. Do not let two agents modify the same core interface without constraints during the same period.
Make environment isolation a startup self-check. When the test process starts, it should resolve endpoints and exit immediately if a production marker is found. Example code:
def resolve_model_endpoint(env: str):if env == "test":return InMemoryFakeModel()if env == "staging":return SandboxModel()if env == "prod":require_manual_approval("prod-model")return ProdModel()raise ValueError("unknown environment")def assert_safe_test_env(env, endpoint):if env == "test" and endpoint.is_production:raise RuntimeError("Tests must not connect to production inference endpoints")
Skill registration must be discoverable, callable, observable, and rollback-capable. A registry is not a decorative directory. It needs health checks, capability contract tests, call logs, budget limits, and disable switches. Any skill that cannot pass contract tests must not enter the runtime.
Use test layering and a doubles strategy. Unit tests should use in-memory doubles and fixed responses; contract tests should validate interface semantics; integration tests should enter a sandbox; and production validation should be limited to controlled smoke tests. Test code must not hold production keys, and network policies should deny test access to production inference endpoints by default.
Put budget and observability first. Set call-round limits, token budgets, timeouts, and retry limits for each agent run. Abnormal retries should use exponential backoff and trigger alerts. Output daily consumption reports by task. When a budget threshold is exceeded, trigger a circuit breaker instead of waiting for the monthly bill to expose the problem.
Make human review rhythmic. Review change summaries and risk flags daily, and conduct architecture reviews weekly. Core interfaces, data migrations, and permission changes must require human approval. The review focus should not be line-by-line syntax picking, but dependency direction, state boundaries, error handling, and test authenticity.
Refactor incrementally and keep rollback capability. Small-batch changes, feature flags, shadow runs, dual-write validation, and fast rollback should be designed together. Refactoring generated by agents must be rollback-capable by module, avoiding one-time large changes that make it impossible to locate problems.
Implementation Workflow: Let Agents Accelerate Inside Guardrails
The recommended workflow is as follows: first, humans define architecture boundaries and acceptance contracts; then agents generate small-batch implementations and tests; next, run contract tests, environment self-checks, budget gates, and static checks; after passing, enter human review; finally, merge and update architecture decision records. Each cycle should retain observable evidence: what changed, why it changed, what environment the tests used, how much budget was consumed, and which skills were registered or disabled.
Agents are accelerators, not accountable owners. They can quickly generate code, add tests, and draft migrations, but architecture consistency, test authenticity, production isolation, and budget governance must be backed by engineering systems. In this case, “three weeks without reading code, about 30 rounds of calls in a single day, about five times the budget, tests connected to production, fake skills, and a final restart” was not an isolated accident. It was the inevitable result of missing guardrails. Turn specifications into contracts, make environment isolation a hard gate, make skill registration a verifiable capability, and make budget and review part of the daily rhythm. Only then can multi-agent refactoring become controllable, observable, and rollback-capable.
