Blast radius of a symbol
richter:impact answers "what breaks if I change this?" before you change it.
php artisan richter:impact "App\Services\PostPublisher"
php artisan richter:impact PostPublisher # substrings work too
php artisan richter:impact "App\Services\PostPublisher" --explain # chain from each reached entry surface
php artisan richter:impact "App\Services\PostPublisher" --json # machine-readable, for scripting
php artisan richter:impact "App\Services\PostPublisher" --markdown # PR-ready markdownCallers and dependencies
The report prints the symbol's callers (what breaks if you change it) and its dependencies (what it reaches), breadth-first. Each hop shows its depth (d1, d2, …) and the edge it was reached through, so a caller chain reads back to the entry point one hop at a time:
Callers (what breaks if you change "App\Services\PostPublisher"):
d1 App\Http\Controllers\PostController::publish (via action-to-service) — app/Http/Controllers/PostController.php
d2 App\Http\Controllers\PostController (via controller-to-action) — app/Http/Controllers/PostController.php
d3 route::POST::/posts/{post}/publish (via route-to-controller) — routes/web.php:24
Dependencies (what "App\Services\PostPublisher" reaches):
d1 App\Events\PostPublished (via action-to-event) — app/Events/PostPublished.phpEvery hop carries its defining file (and line, when known), project-relative, so you never have to grep for what a report names.
Entry surfaces
Between the callers and dependencies, the report names the entry surfaces the callers walk reaches (routes, commands, schedules, and Livewire/Filament/Nova component classes), with the same annotations detect-changes carries: defining location, [test-referenced] / [⚠ no test references this] tags, security exposure and Pennant gates. A surface connected only by an association edge (a model relation or a model-to-policy link) is listed separately here too, as context rather than a caller.
--explain adds the shortest call chain from each surface down to the symbol.
The tags are orientation, not verdicts: impact reports no risk figure at all, and the section reads (none) when the walk reaches no surface.
When a symbol matches nothing
A symbol that matches nothing is a lead rather than a dead end. The report names the nearest graph nodes, ranked by shared identifiers, so a lookup under the wrong root namespace surfaces the real node. When nothing in the graph resembles it, the report says how many nodes were scanned.
--json
With --json, stdout is a single document:
{target, callers, dependencies, entryPoints, associationEntryPoints, entryPointPaths,
entryPointLocations, entryPointSecurity, entryPointGates, entryPointAuthGates,
entryPointTestReferences}The entry-point keys share detect-changes' vocabulary and shapes, so a consumer parses both reports identically. Each callers/dependencies hop is {depth, node, via, file?, line?}; an entryPointPaths hop carries no depth. On failure, stdout is {"error": "…"}.