What would convince me that the feature works?
An agent has implemented a feature, added tests, and reported that everything passes. That is useful information, but it leaves a question unanswered: would those tests catch the mistake I care about?
In Architecture for delegation, I separated decisions, external operations, a controller, and a view. Those boundaries make a feature easier to understand in isolation. Here, I want to use them to verify its behavior without trusting the implementation’s own assumptions.
The goal is not more tests. It is useful feedback while the agent works: build a check, make a small change, run it, and use the failure to decide what needs correcting. Validation belongs inside implementation, not just at handoff.
Delegate the checks, not just the implementation
I can ask an LLM to help turn an architectural constraint into an ESLint rule, a product requirement into a behavioral test, or a critical journey into an end-to-end check. That is part of the engineering work I want to delegate, not infrastructure that must all exist before delegation becomes useful.
The starting point is an explicit expectation. “Write tests for this component” invites the agent to describe whatever it already implemented. “At 150 W, a 60-minute ride represents 540 kJ of mechanical work” gives it an independently checkable result. “Changing duration must update the estimate” gives it a UI requirement.
I want to review the proposed expectations and the boundary being exercised. The agent can then build the smallest missing check, run it, inspect the actual failure, and correct its implementation. If the check is wrong, it needs an explanation and evidence, not a weaker assertion chosen because it passes.
The risk is shared misunderstanding. The same LLM can produce an implementation and a test that agree with each other while violating the requirement. Another agent reviewing the result can help, but agreement between agents is not independent proof. I still need to inspect the expectations and establish that the checks can reject relevant defects.
Start with numbers the reader can check
The running example estimates work for a single-zone cycling workout. The rider supplies duration and zone; their profile supplies FTP in watts. For this example, Endurance targets 60% of FTP and Tempo targets 80%. Those are illustrative targets, not universal zone definitions. A zone spans a range, so choosing one target is an explicit product convention.
For a rider with an FTP of 250 W, I can calculate the expectations without running production code:
- 60 minutes in Endurance: 150 W for 3,600 seconds, or 540 kJ.
- 30 minutes in Endurance: the same power for half the time, or 270 kJ.
- 60 minutes in Tempo: 200 W for 3,600 seconds, or 720 kJ.
These are estimates of mechanical work at the planned power, not dietary calories or a prediction of actual ride output. The formula is watts × minutes × 60 ÷ 1,000.
A missing minutes-to-seconds conversion would produce 9 kJ instead of 540 kJ for the first ride. That is a specific mistake a test should reject.
Check the calculation at its own boundary
The calculation takes plain values, so I do not need a rendered screen to test its arithmetic or target selection.
The fixed expectations matter. Calculating expectedWorkKj by calling the production function, or copying its implementation into the test, could reproduce the same error. I want the examples derived from units and the stated target convention.
Other cases should protect distinct behavior: a different FTP must change target power, fractional minutes must not be truncated, and negative or non-finite numeric inputs must be rejected. For results involving floating-point arithmetic, I would use an explicit numeric tolerance rather than require an exact decimal representation.
A correct calculation can still be wired incorrectly
The arithmetic test does not establish that changing the form updates what the rider sees. A controller could keep an old estimate in state or forget to pass the new zone into the calculation. Every calculation test would still pass.
For that risk, I want the real controller, calculation, and view rendered together. Edit the labelled duration field and zone selector, then inspect the displayed kJ. Do not mock the calculation to return the value the test expects.
At 30 minutes, Tempo’s 200 W target gives 360 kJ. The second edit catches a different wiring mistake from the first: duration can be connected correctly while zone selection is ignored.
This follows Testing Library’s guiding principles: exercise the interface through its rendered DOM and the way people use it. An invalid-duration scenario should also check the validation message and unavailable Save action, not only the calculation’s null result.
The estimate is synchronous. I do not need fake network delays to exercise multiplication. The external service becomes relevant when the rider saves the workout.
Control saving without replacing the feature
Saving introduces a separate requirement: close the editor only after success, and retain duration and zone after failure so the rider can retry. A test with an immediately successful service can miss premature closing.
I keep the real feature and mutation bindings, but replace the save service so I can hold or reject the response. The scenario starts with the same 60-minute Endurance ride and 250 W FTP.
I also need a successful-save scenario: hold the request, confirm the editor stays open, resolve it, and confirm it closes. A form that never closes could pass the failure scenario alone. Retrying after failure should submit the retained source inputs and close on success.
These UI snippets describe a harness contract, not copy-and-run tests. The helpers must render the real feature, interact with its controls, and settle responses inside the framework’s update boundary. The numeric input assumption in the failure assertion is part of this sketch.
The fake service should fail on unexpected operations rather than silently return empty successes. Each render also needs its own query cache and cleanup when those bindings require them. The purpose is to isolate the feature, not replace it with a second implementation.
Use mutation testing to challenge the tests
Before trusting the arithmetic check, I would remove the multiplication by 60 and confirm that the 540 kJ expectation fails with 9 kJ. Then I would restore it and run the check again. The failure should identify the unit-conversion defect, not a missing provider or setup timeout.
Mutation testing tools automate related challenges by changing production code and running the tests. A mutation is caught when a test fails; a surviving mutation means the tests did not reject that change. An LLM can help configure a focused run, interpret survivors, and propose missing scenarios.
Other useful challenges include ignoring zone changes or closing the editor before saving succeeds. Some behavioral edits may need to be introduced deliberately rather than generated by a tool’s standard mutation operators.
A survivor is a question, not an instruction to add an assertion. It might expose missing coverage, a requirement I have not decided, or a change that does not alter observable behavior. I want tests for meaningful differences, not implementation details added solely to improve a mutation score.
The agent’s report should name the evidence: “The calculation returned 540 kJ for the reference ride. Removing the seconds conversion failed that assertion. Changing duration and zone updated the rendered estimate, and a rejected save retained the workout draft.”
Use each check for a different question
ESLint and dependency checks reject concrete structural mistakes, such as the calculation importing a query library or the view calling a domain service. An LLM can help write the rule. I would challenge it with allowed and forbidden fixtures, including aliases and re-exports where relevant. A rule that loads successfully has not yet shown that it enforces the constraint.
Calculation tests check the target convention, units, and invalid inputs. Interaction tests check that edits reach that calculation and that saving follows the intended lifecycle. Testing the arithmetic through every UI combination would add cost without necessarily protecting another risk.
Mutation testing asks whether those tests reject selected implementation changes. It challenges the tests; it does not decide whether the original target convention or product requirement was right.
End-to-end tests exercise the assembled journey through the browser. In an isolated environment, load a rider with a known FTP, select duration and zone, save, reload, and verify the persisted workout inputs and derived estimate. Screenshots alone do not establish persistence, and a browser test that intercepts saving still does not exercise the real save service.
The isolated feature test cannot establish real authentication, authorization, response contracts, or persistence because I replaced the service. The server must validate source inputs, including the rider’s FTP snapshot, and calculate its own result rather than trust client-supplied kJ. Real integration checks cover that contract and negative authorization cases when access is part of the change.
A fake is a way to control responses, not another source of business truth. Its agreement with production does not establish correctness if both share the same mistake. Expectations still need to come from the requirement.
The feedback I want from delegated work
The first article made the feature independently understandable. This article uses that boundary to put validation inside implementation. I want the agent to help build the checks, not just receive a finished test suite and hope its changes pass.
During a change, I would use fast lint, type, calculation, and targeted interaction checks to get feedback on the slice being edited. Focused mutation runs challenge important decisions and regressions. Relevant end-to-end journeys and the project’s full required checks belong before handoff; an expensive suite does not need to run after every keystroke.
A failure sends the agent back to the requirement and the code. It should correct the cause, rerun the relevant checks, and report what was exercised and what remains unverified. Human review still evaluates the requirement, the validation itself, and the remaining risk, rather than every generated line unaided.
There is a maintenance cost in rules, fakes, and rendering helpers, and I have not measured a productivity improvement from this approach. I want the smallest checks that give useful feedback, not a second application built to test the first.
The next article, Keeping agents consistent, is about making these responsibilities and checks part of the agent’s normal workflow.