Suppose you are building a feature that approves work requests. One agent builds the approval API; another builds the approve button and the result notice. Once the two changes are merged, a verifier walks through the flow in which a user approves a request.
The server returns an error when an approval attempt arrives for a request that was already processed. The screen is supposed to read that error and tell the user “This request was already processed”. But what if the server and the screen disagree about where the error code lives? The server test checks the response the server itself produced; the screen test uses a fake response the screen expected. Both pass, and the connected screen may still show nothing but a generic error.
This article uses that situation as a worked example of splitting work and merging it again. It is not a reconstruction of a real incident or a recorded experiment. The API name, the response shape and the status codes were chosen for the example.
If you want to see the result first, go to the approval feature lab at the end and run ‘1. Separate checks’ and then ‘2. Connected check’. By changing the field the screen reads and the response the test injects, you can see which check misses what.
Are the server and the screen looking at the same response?
Let the approval endpoint be POST /requests/{id}/approve. The server checks the approval permission and the request state. When a user with permission tries to approve a request that was already processed, it returns HTTP 409 with this body:
{
"code": "ALREADY_PROCESSED"
}The screen owner expected the error response to look like this:
{
"error": {
"code": "ALREADY_PROCESSED"
}
}Assume the screen’s HTTP client puts the response body in response.data. The code the server sent is then in response.data.code, while the screen looks for response.data.error.code. If the screen was written to show a generic message when that code is absent, the user only ever sees “Approval failed”.
The server test checks for HTTP 409 and a top-level code, so it passes. The screen test injects a fake response with error.code and checks the notice text, so it passes too. Each test confirmed the behavior its own side defined, but neither checked that the value the server sends is the value the screen reads.
This kind of mismatch happens when people split work as well. Handing tasks to coding agents does not create the agreement between server and screen by itself. To run several implementations at the same time, first separate what each side may decide on its own from what has to be settled together.
Decide the shared behavior before splitting the work
If the split is just “build the server” and “build the screen”, each owner may fill in the missing requirements on their own. Whether to reload the list after a successful approval, what to show for an already processed request and how to report a permission error all land in that gap.
For this example, this much can be settled up front:
| Situation | Server | Screen |
|---|---|---|
| User with permission, request can be approved | Change the state to approved and return success | Show the success notice, then refresh the list |
| User with permission, request already processed | Return 409 with a top-level code, no further changes |
Show “This request was already processed”, then refresh the list |
| User without approval permission | Return 403 without changing the state | Tell the user they are not allowed |
This is the behavior definition for this example. It does not mean every approval API has to use the same status codes or screen behavior. If the existing service already has error-response conventions, those come first.
Now the server owner can implement the state change and the response, and the screen owner can handle the agreed response. The verifier prepares the success, already-processed and no-permission scenarios and the data they need from the start. The check that connects the real API and the screen runs after the implementations are merged.
Calling in the verifier at the end is not the only option. During requirements reading they can also hunt for missing conditions such as “what happens when an already approved request is approved again?”. But having scenarios ready and having run the implementation through them are two different states.
- Row 1: Decide approval behavior and response shape
- Row 2: Server implementation unit checks, Screen implementation unit checks, Prepare verification scenarios
- Row 3: Merge the changes, Verification ready
- Row 4: Check the real approval flow
- Row 5: Confirm error notices and state changes
- Decide approval behavior and response shape → Server implementation unit checks
- Decide approval behavior and response shape → Screen implementation unit checks
- Decide approval behavior and response shape → Prepare verification scenarios
- Server implementation unit checks → Merge the changes
- Screen implementation unit checks → Merge the changes
- Prepare verification scenarios → Verification ready
- Merge the changes → Check the real approval flow
- Verification ready → Check the real approval flow
- Check the real approval flow → Confirm error notices and state changes
Scenario preparation can run alongside implementation, but the real flow check waits for the two implementations to be merged.
Write this dependency into the task instructions too. Tell the server owner, for instance, “an already processed request leaves the state untouched and returns 409 with a top-level code”, and tell the screen owner “read that code, show the notice, then refresh the list”. If the response has to change, the server owner does not fix it alone and declare the task done; they tell the screen and verification owners what is affected. The integrator checks that all three are working against the same response shape.
Calling agents and separating working folders are different things
Coding tools offer ways to delegate parts of a task. A subagent is a helper agent to which the main agent hands off some of the work. OpenAI’s documentation describes running several subagents and collecting their results, and it distinguishes read-heavy work such as exploration and review from several agents editing code at the same time; the latter can bring conflicts and coordination overhead. OpenAI Subagents (opens in a new tab).
Claude Code’s agent teams are another example, with a shared task list, messages between owners and task dependencies. As of the official documentation checked on September 10, 2026, it is an experimental feature that is disabled by default, and the documentation says that for tasks editing the same files or with many dependencies, alternatives such as a single session may fit better. What this article covers is the feature as documented, not results from using it. Claude Code agent teams (opens in a new tab).
Separately from that, there are ways to separate the working files. A worktree is an additional working folder attached to the same Git repository, and each folder can carry a different change. Opening several conversations and creating several working folders are different decisions. Git worktree (opens in a new tab).
Building the approval API and the screen in separate working folders keeps one owner’s edits from landing in the other’s files right away. It does not solve the error-response problem above. Even if Git merges the two changes without conflicts, the screen still reads the error code from the wrong field.
The runtime environment needs its own look as well. Folders can be separate while both owners start a server on the same port or write to the same test database. If work has to run in parallel, ports and data ranges have to be assigned per task too. Separate working folders do not mean every runtime resource is separate.
Do not define the fake response in two different places
Using a fake response in the screen’s unit tests is useful. You can check the button’s pending state and the error text quickly without starting the server every time. The problem is when that data drifts from the real response and nothing checks for it.
In this example, the server response shape can be fixed once and the screen’s fake response made to match it. If an API specification or a shared response type already exists, use that as the reference. A check that compares the real server response against the specification also lowers the risk of the document or the type being wrong together with the code.
Matching the response shape is not the end either. The screen can read the code correctly and still show the wrong message, or show the right notice and fail to refresh the list. Tests that check field structure and tests that check what the user will see cover different things.
You do not need a perfect specification before splitting. But it has to be visible which assumptions the parallel work rests on. If the approval states and the error format are still in flux, settle those first and then continue the screen work. Having every owner start writing code at the same moment is not the goal.
Match the change under review to the version that was checked
If a hand-off says only “implemented, tests pass”, you have to go and find out what was checked. Both the API owner’s unit tests and the screen owner’s unit tests passing does not mean the real approval flow was verified.
It is better to receive, from each owner, where the change is, which version it targets, which checks ran and what was not covered. For the screen owner in this example, a report could read:
Implemented the approve button’s pending state and the error notices. Ran the unit tests that use the agreed error response shape. Have not yet run the check connected to the real approval API.
This too is an example report. In real work it also ties the commit or change state to the commands that ran and their results. Documenting what remains unverified lets the integrator decide what to check next.
The reviewer reads the actual change alongside that summary. Putting the response the server emits, the field the screen reads and the value the test injects side by side is what surfaces the mismatch. A remark such as “the server sends a top-level code but the screen reads error.code, so the notice for an already processed request shows as a generic error” names the location and the condition, and helps more than “error handling is weak”.
Even when the review is delegated to another agent, it may repeat the same assumptions if all it receives is the implementer’s description. Give it the requirements, the actual diff and the check results, and have it look for paths that contradict the implementer’s claims. The number of reviewers alone says little about review quality.
When changes are added after review, check again which version the test results point at. If only the server tests were rerun after fixing the error response, whether the screen reads the changed response is still open. Rerun the checks related to the fix and decide whether the new change falls outside the scope of the earlier review.
The checks that remain when only the screen was fixed
Say the server’s top-level code was chosen as the reference. What to confirm after fixing the screen becomes clear once the before and after are laid out side by side.
| Change state | Screen unit check | Check with the real response | Next action |
|---|---|---|---|
Screen and fake response both use error.code |
Notice check passes | Fails with the generic error notice | Fix the screen to the agreed response shape |
Only the screen changed to code |
Fails because of the old fake response | Already-processed notice correct | Update the fake response to the agreed shape too |
Screen and fake response both use code |
Notice check passes | Notice correct; list refresh checked separately | Include the list in the user-flow check |
In the second row, a failing unit check is no reason to revert the screen fix. Confirm the real server response and the agreed behavior, then fix the stale test data. Conversely, the green mark in the third row does not say the list refresh was implemented. What a check actually compares matters more than its name.
After merging, confirm the real approval flow
The integrator can be the main agent, a separate agent or a person. Whoever it is has to know which versions of the approval API and the screen are being merged, and has to be able to check the connected result.
In this example, prepare a request that can be approved and approve it from the screen. Confirm the success notice and the list refresh, and check that the request state actually changed. Then try to approve an already processed request again and confirm that the server sends the 409 and the error code defined in the example, that the screen shows the right notice, and that no further state change happened.
For the second attempt you can use a situation where an older state is still around, such as another screen opened before the first approval. The button disappearing from the current screen does not cover that case. Note that this sequential scenario does not prove how two approval requests arriving at the same time are handled; if concurrent approval is possible in the feature, that condition needs its own check.
Also check an approval attempt by a user without permission. Even if the screen hides the button, confirm that the server rejects the request and keeps the state. The screen notice and the server’s permission check are each required behaviors.
If the integration check finds the code versus error.code difference, fix the implementation and the fake response to the agreed shape, then rerun the related unit tests and the real approval flow. The verifier should know what the result must be while not depending only on the fake responses the implementers made.
Running and confirming all of this can be delegated to agents too. What the developer decides is the expected user behavior, the scope of the split and the criteria for accepting the merged result. Have the agents bring the evidence those criteria need, and decide on deployment by the team’s operating procedure.
The next time you split work, you can start like this:
| Work to delegate | How to start | What to check when the result comes back |
|---|---|---|
| Reading the same PR from several angles | Review the same version read-only | Location of each remark and its failure condition |
| Features with little interference | Separate working folders and edit scopes | Each side’s checks and the merged user flow |
| Interlocking API and screen changes | Decide the response shape and behavior first | Real response and screen handling match |
| Verifying success and failure flows | Prepare scenarios first, run after merging | Screen notices and actual state changes |
| Small edits crowded into the same file | Handle in one writing session | Contents of the change and the checks it needs |
Try it: separate checks and the connected check
The lab below runs the approval feature above in miniature, in the browser. The server response is fixed to the top-level code defined in the article, and you can change how the screen and the test read that response. Rather than also changing the server’s response format so that the two merely look alike, the setup is meant to show which side has to change according to the agreed reference.
- Under Reproduce the mismatch, run the two checks in turn. The server and screen unit checks pass, but the connected notice fails. Compare the real response with the code the screen read.
- Select Fix the screen only and check again. This time the connected notice is right, but the screen unit check fails. Look at which response the test is still injecting.
- Use Align response and test to make both checks pass, then select Skip the list refresh. The behavior that a notice-only check misses shows up.
- Change the situation to connect to Request that can be approved and User without permission. Even with the wrong error field, the success path can pass. When permission is missing, check that the server state stays the same, apart from the notice.
Changing a setting clears the previous results. Real development needs the same separation, so that a pass recorded before a fix is not used as evidence for the implementation after it. The ‘connected check’ here is a check of the example server and screen models; it does not stand in for an integration test of a real service.
When changes that passed separately are connected
Run ‘1. Separate checks’ first, then ‘2. Connected check’. The responses below show why the same settings give different results.
Server owner
The agreement in this example: the error code goes in the top-level code.
{
"code": "ALREADY_PROCESSED"
}Already processed request → HTTP 409Screen owner
{
"error": {
"code": "ALREADY_PROCESSED"
}
}Not run yet. The initial settings reproduce the error-response mismatch from the article.
Separate checks · the 409 notice
not run
Server: 409, error code, no extra writesScreen: only the notice text, using the fake response
Connected check · selected situation
not run
The server model’s response is passed straight to the screen model