Non-overlapping files is not the same thing as independent work. If one worker consumes what another is still producing, it either guesses silently or breaks loudly, and both are avoidable.
The instinct when fanning work out to several agents at once is to check for file collisions and call it safe. If worker A is editing one set of files and worker B is editing a different set, nothing should conflict, and on the surface that is true, a merge of the two branches will apply cleanly. But a clean merge is not the same claim as independent work, and treating the two as equivalent is how a parallel wave produces silently wrong output that looks, at first glance, like it worked.
The failure shows up when worker B’s task depends on something worker A is producing, even though the two never touch the same file. B might be writing an integration test against an API that A is still designing. B might be importing a module A has not finished exporting yet. B might be consuming a data shape that A’s task is in the middle of changing. None of that shows up as a file-level conflict, because B is not editing A’s files, it is only reading, importing, or calling into work that does not exist yet in a finished state.
When that happens, B has exactly two options, and neither is good. It can guess what the finished interface will look like, based on partial information or its own assumptions about what A is likely to produce, and write code against the guess. That guess is often wrong in some detail, and the wrongness does not surface as an error, it surfaces as a merge that applies cleanly and a behaviour that is subtly off, the worst kind of bug because nothing flags it for review. Or B can notice the dependency, stub the missing piece, and leave a visible placeholder, which is honest, but now the placeholder either gets merged by accident or someone has to remember to come back and finish the wiring, and someone has to remember is exactly the kind of soft obligation that gets lost in a busy pipeline.
The question that actually determines whether two workers can run in the same wave is not ‘do their files overlap’ but ‘does either one call, import, or read something the other is still producing’. That is a dependency question, not a collision question, and it has to be asked explicitly before launch, because it will not show up in a diff review afterwards, by the time the diffs exist, the guess or the stub is already baked in.
In practice this means dependency chains get resolved into sequential waves rather than a single flat batch. The worker producing the interface goes first, alone or alongside genuinely unrelated work, and finishes to a state the consumer can build against safely. Only then does the consumer launch, in the next wave, with something real to read rather than something to guess about. This costs wall-clock time compared to firing everything at once, and it is worth it every time, because the alternative cost is a defect that passes review clean and gets found in production instead.
The other trap is assuming that because two tasks are independent in the product sense, they solve unrelated problems, ship unrelated features, they are independent in the execution sense. Product independence and data independence are different claims. Two features can be conceptually unrelated while one implementation happens to read a config file, a shared type, or a database migration the other is mid-flight on. The check has to be at the level of what the code actually touches and reads, not what the task description says the feature is about.
Before any wave launches I walk each worker brief and ask, plainly, whether it needs anything from a sibling that is not yet finished. If the answer is yes for any pair, that pair is not a wave, it is a sequence wearing a wave costume, and running it as parallel work only hides the ordering problem instead of solving it.