The Eloquent scope that never ran
A local scope named latest() is silently shadowed by Eloquent's own builder method, so it never runs — and the unit test covering it passed, because it called the scope by the one route that bypasses the collision.

The blog index on this site is ordered newest first by published_at. The model
says so:
#[\Illuminate\Database\Eloquent\Attributes\Scope] protected function latest(Builder $query): Builder { return $query->latest('published_at'); }
Two controllers call it. Neither of them orders by published_at. That method
body has never executed once in production.
__call is a fallback, not a hook
Eloquent's local scopes are dispatched entirely from Builder::__call. In
Laravel 12 the relevant branch reads:
if ($this->hasNamedScope($method)) { return $this->callNamedScope($method, $parameters); }
hasNamedScope() is what knows about scopeFoo() methods and about the
#[Scope] attribute. It is reached from nowhere else in the normal query path.
PHP calls __call only when the requested method cannot be resolved on the
object — when it does not exist, or is not accessible from the call site. And
Illuminate\Database\Eloquent\Builder already defines a public latest(),
around line 434 of Builder.php:
public function latest($column = null) { if (is_null($column)) { $column = $this->model->getCreatedAtColumn() ?? 'created_at'; } $this->query->latest($column); return $this; }
So BlogPost::query()->published()->latest() binds straight to that method.
__call never fires, hasNamedScope() is never asked, and the scope on the
model is unreachable code that static analysis is perfectly happy with.
Dumping the SQL settles it:
select * from "blog_posts" where "published" = ? and "published_at" <= ? order by "created_at" desc
published() works fine, because Builder has no method by that name. It is
only the collision that kills the scope, and the collision is silent: no
exception, no deprecation, no log line. You just get the framework's behaviour
instead of yours.
The test that made it look fine
There is a unit test for this scope, and it passes:
$latest = BlogPost::query()->scopes(['latest'])->get(); expect($latest->first()->is($newer))->toBeTrue();
Builder::scopes() calls callNamedScope() directly rather than going through
__call. It exists precisely as the escape hatch for scopes you cannot invoke
by name. So the test exercised the one code path that does reach the scope, and
never touched the path both controllers use. Rewriting that single line as
BlogPost::query()->latest()->get() makes the test fail immediately.
That is my mistake, not the framework's. The test asserted that the scope's body is correct. It never asserted that calling it the way the application calls it produces that result.
The generated @method static Builder<static>|BlogPost latest() annotation on
the model made things worse, not better — it documents latest() as part of the
model's API and is equally true whichever implementation answers the call.
PHPStan had no reason to complain.
Why nobody noticed
For a blog where posts are written and published in the same sitting,
created_at and published_at order identically. The bug only becomes visible
with back-dated imports, posts drafted long before they go out, or anything
scheduled — which is exactly when you are least likely to be watching the index
page.
The rule
Before naming a local scope, check whether the builder already owns the name:
method_exists(\Illuminate\Database\Eloquent\Builder::class, 'latest'); // true
latest and oldest are the obvious traps because they read so naturally as
domain vocabulary. find, first, has, with, without, when and the
whole where* family are all real methods too.
Two ways out. Rename the scope to something Builder does not define —
newestFirst() says more anyway — or drop the scope and order explicitly at the
call site. The read-only API controller feeding the SSH client does the latter,
with a comment, because a scope that looks like it works is worse than no scope
at all:
BlogPost::query() ->published() ->orderByDesc('published_at') ->get()
One honest footnote: the Contact model has the same shadowed latest() scope,
and there it changes nothing, because the scope body is $query->latest() —
identical to the method shadowing it. Same bug, invisible. That is the part
worth remembering: this failure mode is only ever as loud as the difference
between your implementation and the framework's.