If your team is experimenting with coding agents, Pest 5 changes the conversation in a useful way. It stops agent safety from being a vague review culture problem and turns it into a verification design problem. That is the right frame. Agents will keep producing plausible code quickly. The only durable answer is to make correctness cheap to prove.
For Laravel and PHP teams, the practical stack is now much clearer: use Pest tests for hard business truth, static analysis for structural correctness, evals for fuzzy behavior, and human review for judgment calls. Pest 5 matters because it gives these pieces a workflow shape that actually fits agent-assisted development instead of treating AI as a separate novelty lane.
The mistake to avoid is simple: do not bolt an agent onto a weak codebase and expect code review to absorb the risk. If the rules that matter are not encoded already, the agent did not create your problem. It exposed it.
The Shift: Verification First, Agent Second
Most teams still evaluate agent output backward. They start with the generated diff, then ask a reviewer to mentally simulate whether it is safe. That does not scale. It did not scale before AI either, but agents make the failure obvious because they can produce ten reviewable changes in the time a human properly reasons through one.
Pest 5 pushes a better operating model. Its current release and docs position the framework around faster feedback loops, test impact analysis, an agent verification command, browser testing, and eval-oriented workflows for AI-heavy applications. The important part is not the feature checklist. The important part is what that checklist implies: verification should be the product boundary for agent-written code.
That means you need a clear split of responsibilities:
- Unit and feature tests own business rules and regression checks.
- Static analysis owns type discipline, framework misuse, and structural drift.
- Evals own broader output quality where exact equality is the wrong assertion model.
- Human review owns architecture, scope discipline, naming, and product intent.
If you keep those boundaries fuzzy, agents become a source of anxiety. If you make them explicit, agents become another producer feeding a strong acceptance pipeline.
This is why I think the real value of Pest 5 is not "AI support." It is that Pest is finally treating verification as something that can be shaped around agent workflows without turning the test suite into theater.
Step 1: Put Non-Negotiable Rules in Tests
If a broken rule would create money loss, permission leaks, bad state transitions, or user-visible corruption, it belongs in a normal test. Not in a prompt. Not in a reviewer checklist. Not in tribal knowledge.
This is the first thing to fix in a Laravel codebase before you let agents touch meaningful flows. The suite has to answer, in executable form, what must never change.
What should live in Pest tests
In practice, these categories almost always belong there:
- Pricing and tax calculations
- Authorization and policy outcomes
- State machine transitions
- Validation invariants
- Idempotency behavior for jobs, webhooks, and retries
- Data transformation rules used by downstream systems
Those are not "nice to have" tests. Those are the contract that makes agent iteration safe.
Example: pricing logic the agent cannot negotiate with
Suppose an agent refactors a checkout service to reduce duplication. The code might look cleaner and still be wrong in exactly the place that matters. This is where a narrow, explicit Pest suite earns its keep.
use App\Domain\Billing\Cart;
use App\Domain\Billing\Money;
use App\Domain\Billing\PercentageDiscount;
it('never drops below zero when discounts exceed subtotal', function () {
$cart = new Cart(subtotal: Money::fromInt(5000));
$total = $cart
->applyDiscount(new PercentageDiscount(150))
->total();
expect($total->toInt())->toBe(0);
});
it('applies store credit after discount rules without producing negative totals', function () {
$cart = new Cart(subtotal: Money::fromInt(8000));
$total = $cart
->applyDiscount(new PercentageDiscount(25))
->applyStoreCredit(Money::fromInt(9000))
->total();
expect($total->toInt())->toBe(0);
});
it('keeps cents precise across discount math', function () {
$cart = new Cart(subtotal: Money::fromInt(1999));
$total = $cart
->applyDiscount(new PercentageDiscount(10))
->total();
expect($total->toInt())->toBe(1799);
});
These tests are not interesting in a literary sense. Good. They should be boring and merciless. An agent can refactor the service, swap collaborators, rename classes, or optimize internals. What it cannot do is silently break the money rules without a visible failure.
Keep the suite sharp, not bloated
A common overreaction is to compensate for AI with giant end-to-end coverage everywhere. That usually makes the feedback loop worse.
What you want is a layered suite with different response times:
- Small unit tests for hard domain rules
- Focused feature tests for Laravel request and policy flows
- A limited number of integration tests for database, queues, mail, or external boundaries
- Browser tests only where UI behavior really matters
If every rule is validated only through a slow browser or full-stack flow, test impact analysis becomes less useful and agents lose the speed advantage you wanted in the first place.
The test suite should be opinionated about cost. Cheap tests should guard expensive mistakes.
Step 2: Use Static Analysis to Catch Structural Drift Early
Tests catch behavior. They do not reliably catch a service returning the wrong shape, a nullable leak creeping through a boundary, a collection changing element type, or a helper method becoming a soft-typed dumping ground. Agents are especially good at creating those kinds of problems because the code still looks plausible.
That is why static analysis needs to be part of agent verification, not a separate quality initiative you keep postponing.
Pest 5’s broader ecosystem message matters here too. The official release material ties Pest to a stronger toolchain around verification, not just test syntax. That matches reality: Laravel teams need Pest plus PHPStan plus Larastan, not Pest alone.
Make contracts explicit where agents touch them
Application services, data mappers, and domain actions benefit a lot from explicit shapes. Once you document the return contract tightly, both humans and tools get less room to lie to themselves.
<?php
namespace App\Actions\Orders;
use App\Models\Order;
final class BuildOrderSummary
{
/**
* @return array{
* id: int,
* customer_email: string,
* currency: string,
* lines: list<array{
* sku: string,
* name: string,
* quantity: int,
* total_cents: int
* }>,
* total_cents: int
* }
*/
public function handle(Order $order): array
{
return [
'id' => $order->id,
'customer_email' => $order->customer_email,
'currency' => $order->currency,
'lines' => $order->items
->map(fn ($item) => [
'sku' => $item->sku,
'name' => $item->name,
'quantity' => $item->quantity,
'total_cents' => $item->total_cents,
])
->values()
->all(),
'total_cents' => $order->total_cents,
];
}
}
That docblock is not decorative. It gives PHPStan and Larastan something strong enough to reason about when an agent starts "cleaning up" a serializer or changing a downstream consumer.
Without those contracts, the typical failure mode looks like this:
- Agent swaps an integer for a float because it feels convenient.
- A collection becomes lazy where an eager array was assumed.
- A nullable property leaks into mail or queue payload logic.
- The code passes casual review because the diff feels tidy.
Static analysis is the right place to kill those changes fast.
A sane Laravel baseline for agent work
If you want a practical bar, this is a good starting point:
- Run PHPStan and Larastan in CI on every agent-authored change.
- Treat new ignores as debt that needs explanation, not as routine cleanup.
- Add return types, generic collections, and array shapes to application services first.
- Tighten hot paths before edge paths. Orders, billing, auth, webhooks, and exports usually deserve the earliest attention.
The point is not to reach some abstract purity level. The point is to shrink the space where an agent can introduce quiet structural decay and still look productive.
Official references are worth linking where readers want to go deeper: Pest 5 announcement, Agent plugin docs, PHPStan, and Larastan.
Step 3: Use Agent Verification for Fast Probes, Not Permanent Coverage
The Pest agent plugin is one of the more practical pieces in this release because it acknowledges a real workflow need: sometimes the agent does not need a new permanent test yet. It needs a one-off proof that the change works inside the real test environment.
That is a very different thing from saying "the agent should just click around and hope." The plugin’s current docs describe a single verification command that can exercise backend behavior directly and, with browser tooling installed, drive real UI flows as well. That is useful precisely because it stays inside the verification boundary instead of inventing a separate AI sandbox.
Good uses for one-off agent verification
This is where I think it fits best:
- Route still returns
200after a controller refactor - Policy still blocks a user class correctly
- Dashboard still renders after a Livewire or Blade change
- Queue side effect still happens after an event wiring change
- Form flow still works before you decide whether it deserves a permanent browser test
Bad uses for one-off agent verification
This is where teams will misuse it:
- Replacing durable tests with ad hoc probes
- Treating a successful one-off check as proof of broad correctness
- Letting the agent invent vague success criteria on the fly
- Skipping normal feature tests because the probe was "good enough"
The rule should be strict: agent verification probes are temporary evidence, not long-term coverage. If a behavior matters repeatedly, promote it into a real test.
Example: one-off probe after a dashboard access change
The official docs show the shape of this pattern with a --agent command. In practice, a Laravel team might use it like this after an authorization or rendering change:
./vendor/bin/pest --agent='$user = \\App\\Models\\User::factory()->create(); $this->actingAs($user)->get("/dashboard")->assertOk();'
That is useful because it checks the actual application boundary quickly. But if the dashboard access rule is business-critical, stop there only once. The second time that flow matters, turn it into a committed feature test.
This is the right mental model:
- Probe once to unblock local iteration
- Promote repeated or risky behavior into the suite
- Never confuse fast evidence with durable coverage
That distinction will save your team from creating a pile of unverifiable agent folklore.
Step 4: Treat Evals as Behavioral Sweeps, Not Truth Machines
Evals are the most misunderstood part of this stack because teams tend to swing between two bad extremes. Either they avoid them because they sound fuzzy, or they overuse them because they sound modern.
The right use is narrower and more valuable: evals are great for broad behavioral assessment when exact string equality or a single assertion is the wrong testing model.
That matters a lot for teams building AI features into Laravel apps, but it also matters for agent-driven code changes where you want to validate classes of behavior across many cases.
What belongs in evals
Good candidates include:
- Prompt-driven summarization quality
- Classification consistency across datasets
- Whether generated content follows policy constraints
- Whether a support assistant refuses unsafe requests correctly
- Whether a code transformation preserved behavior across a bank of fixtures
What does not belong there:
- Refund arithmetic n- Role-based authorization truth
- Validation rules with deterministic outcomes
- Anything you can express cleanly as a direct assertion
If the outcome is exact and deterministic, use a normal test. Evals are not a fashionable replacement for good engineering.
Example: evaluating a support assistant policy boundary
A Laravel team shipping an internal or customer-facing assistant might use dataset-style cases to test refusal and escalation behavior. Pest’s eval tooling makes sense here because "correct" is often about policy adherence across varied prompts, not one exact sentence.
it('handles refund policy scenarios consistently', function (string $message, string $expectedLabel) {
$reply = app(SupportAgent::class)->respond($message);
$label = app(ResponsePolicyClassifier::class)->classify($reply);
expect($label)->toBe($expectedLabel);
})->with([
['I want a refund for an order placed 5 minutes ago', 'allow_refund_path'],
['Refund me for a non-refundable item from 8 months ago', 'deny_with_policy_reason'],
['Can you refund my friend\'s order if I know their email?', 'deny_identity_mismatch'],
['I was charged twice, what should I do?', 'route_to_human_or_duplicate_charge_flow'],
]);
That example still looks test-like because it should. The important shift is conceptual: you are validating behavior quality across a realistic set of cases, not pretending one string comparison will capture the whole policy surface.
For AI-heavy products, this is the missing layer between deterministic tests and manual spot checks. It gives teams a way to keep agent and model behavior under pressure without faking precision they do not actually have.
Step 5: Make Test Impact Analysis Serve the Loop, Not Replace the Gate
Test impact analysis is the feature that will probably change team behavior fastest, because feedback speed is where most agent workflows fall apart. The official Pest 5 announcement positions the TIA engine as a way to rerun only affected tests after changes. If that works well in your codebase, it is a big operational win.
But there is a trap here too. Teams will be tempted to treat changed-area test selection as if it were the whole safety model. That is wrong.
Use test impact analysis to accelerate local verification and agent iteration. Do not use it as an excuse to weaken merge gates.
What the workflow should look like
A strong Laravel pipeline for agent-authored work usually wants three speeds:
Fast local loop
This is what the agent or developer runs repeatedly during implementation:
vendor/bin/pest --dirty
vendor/bin/phpstan analyse
The goal here is cheap confidence. The changed-area rerun should answer, "did this edit obviously break the things it touches?"
Risk-targeted checks
If the change touches policies, serialization, or UI flows, run the extra layer that matches the risk:
vendor/bin/pest tests/Feature/Auth
vendor/bin/pest --agent='$user = \\App\\Models\\User::factory()->create(); $this->actingAs($user)->get("/settings")->assertOk();'
Now you are validating both the durable contract and the one-off path that mattered during the edit.
Full merge gate
Before merge, the bar still needs to be broad:
vendor/bin/pest
vendor/bin/phpstan analyse
php artisan test --testsuite=Feature
Depending on the codebase, you may also add browser tests, architectural tests, or a targeted eval suite. The point is that the full gate still exists. TIA narrows the inner loop. It does not get to redefine what production confidence means.
Where TIA can mislead you
There are a few failure modes worth calling out:
- Coupling is hidden, so the impacted set is smaller than the real risk surface.
- The suite is too integration-heavy, so changed-area speedups are weak.
- Critical business rules are missing, so fast feedback still misses expensive bugs.
- Teams start trusting fast green runs more than the real suite.
That last one is the dangerous cultural bug. Fast feedback is for steering. Full verification is for merging.
Human Review Still Matters, but It Should Move Up the Stack
The better your automated verification gets, the more valuable human review becomes because it stops being wasted on machine-checkable details.
A good reviewer should not spend half their time re-deriving whether a discount can go negative or whether a nullable property escaped into a DTO. If those questions still dominate review, the pipeline is underbuilt.
What humans should own instead:
- Is the agent solving the right problem or just the nearest one?
- Did the abstraction improve clarity or create another layer for no reason?
- Are naming and boundaries getting stronger or weaker?
- Did the change increase hidden coupling?
- Is there an architectural consequence the local diff hides?
That is the level where senior engineering judgment matters. Everything below that should be pulled downward into tests, analysis, or evals over time.
A useful rule for teams adopting agents is this: if reviewers catch the same class of mistake twice, automate it by the third time. That one rule alone will make your agent workflow materially safer within a month.
Pest 5 does not solve verification for you. It gives PHP teams a better place to anchor it. That is enough to matter.
If you are running Laravel with coding agents today, the recommendation is straightforward. Start small, but make the stack explicit: durable Pest tests for core rules, PHPStan and Larastan for structure, one-off agent verification probes for fast iteration, evals for behavior classes that resist exact assertions, and a full merge gate that does not flinch.
Decision rule: if your confidence in an agent change still depends mostly on a reviewer reading the diff carefully, your verification system is too weak. Fix that before you scale the agent.
Read the full post on QCode: https://qcode.in/pest-5-agent-verification-testing-problem/