sandermuller / richter
Measure the magnitude of impact of code changes in a Laravel codebase, built on Laravel Brain
Requires
- php: ^8.4
- illuminate/console: ^12.0||^13.0
- illuminate/contracts: ^12.0||^13.0
- illuminate/process: ^12.0||^13.0
- illuminate/routing: ^12.0||^13.0
- illuminate/support: ^12.0||^13.0
- laramint/laravel-brain: ^2.5.0
- nikic/php-parser: ^5.0
- spatie/laravel-package-tools: ^1.93
- symfony/finder: ^7.2||^8.0
Requires (Dev)
- driftingly/rector-laravel: ^2.5
- larastan/larastan: ^3.10
- laravel/boost: ^2.5.3
- laravel/mcp: ^0.8||^0.9.3
- laravel/pao: ^1.1.4
- laravel/pint: ^1.30.5
- nunomaduro/collision: ^8.9.5
- orchestra/testbench: ^11.2
- phpstan/extension-installer: ^1.4.3
- phpstan/phpstan-deprecation-rules: ^2.0.5
- phpstan/phpstan-phpunit: ^2.0.18
- phpstan/phpstan-strict-rules: ^2.0.12
- phpunit/phpunit: ^12.5.33
- rector/rector: ^2.6.2
- sandermuller/boost-skills: ^2.29
- sandermuller/package-boost-laravel: ^1.1
- spaze/phpstan-disallowed-calls: ^4.14
- symplify/phpstan-extensions: ^12.0.2
- symplify/phpstan-rules: ^14.12
- tomasvotruba/cognitive-complexity: ^1.2
- tomasvotruba/type-coverage: ^2.3
Suggests
- laravel/mcp: Exposes the impact, trace, detect-changes and affected-tests tools plus orientation resources over MCP — supported range ^0.8||^0.9; richter registers a local MCP server when installed.
Conflicts
- laravel/mcp: <0.8.0 || >=0.10.0
This package is auto-updated.
Last update: 2026-08-31 09:09:45 UTC
README
Richter
Measures the magnitude of impact of code changes in a Laravel codebase. Like the Richter scale, but for your PHP.
Run php artisan richter:detect-changes on a branch and Richter reports the HTTP and CLI entry points the diff can reach, flags the ones no test references, and names the hazards the change carries: a guard removed, a payload key a consumer still reads, a validation constraint dropped. Review then starts from what the change reaches, instead of from a cold diff.
Changed files:
app/Models/Post.php (4 graph nodes)
app/Services/CategoryImporter.php (0 graph nodes) (UNRESOLVED: reach for this file could not be fully determined)
Entry points reached: 2 (some changed files could not be fully placed — see UNRESOLVED above)
- command::categories:sync (app/Console/Commands/SyncCategories.php) [test-referenced]
- route::PATCH::/api/posts/{post} (routes/api.php:41) [⚠ no test references this] [authed]
Hazards (1):
! [tier 2 model CWE-915] App\Models\Post::$fillable — $fillable gained owner_id
reach: gated (via its class)
Findings (in the changed source itself):
! app/Models/Post.php: eager-load string 'ownerprofile': segment 'ownerprofile' is not a method on any model — check the relation name
Risk: MEDIUM (advisory) — tier 2 `model` hazard on App\Models\Post::$fillable, reach gated
Impact: 2 entry point(s) · 7 impacted node(s)
What makes it worth installing:
- Member-level change impact. A one-method change seeds that method in the code graph, not the whole class. The graph covers routes, controllers, jobs, listeners, policies, resources, Blade views, and the Eloquent relations your code walks, plus edges a route-anchored analysis misses.
- The rows that matter, first. Reached entry points are ordered by how specifically the diff explains each one, so a change that touches a widely-referenced class leads with its own routes instead of with whatever sorts first alphabetically. The
--jsonpayload carries the same attribution. - Test-coverage prompts. Every reached entry point is tagged
[test-referenced]or[⚠ no test references this], a heuristic prompt rather than a coverage verdict. - Blast radius and traces on demand.
richter:impactlists a symbol's callers, its dependencies, and the entry surfaces behind them.richter:traceanswers "how does this even reach that?" with the shortest call chain. - Affected-test selection.
richter:affected-teststurns the diff's reach into a test selection, with an exit-code contract that fails toward running the full suite whenever the selection cannot be trusted. - Built for coding agents. Richter registers a local MCP server exposing every analysis read-only, so an agent can work with the graph mid-review without shelling out. The
--markdownreport is ready to post as a pull-request comment.
Richter is advisory by default: richter:detect-changes exits 0, and a low or empty result is a signal, not a guarantee of no impact. Opt into a CI gate with --fail-on, --fail-on-hazard or --fail-on-unresolved.
The analysis is static, built on Laravel Brain, and fast enough to run on every branch: it never executes your application's routes, jobs, or commands. It does, however, autoload classes from the analyzed checkout, and autoloading runs a file's top-level code. Treat a checkout you would not composer install on as one you should not analyze either.
Installation
composer require --dev sandermuller/richter
Requires PHP 8.4+ and Laravel 12 or 13. laravel/mcp is optional and, when present, must fall in the supported ^0.8||^0.9 range; see Installation for the laravel/boost v1 case.
Richter is accurate only once it knows your app's shape. Ask your agent to "set up Richter", or follow Set up your project.
Usage
php artisan richter:detect-changes # advisory impact of the current diff php artisan richter:detect-changes --explain # show how each entry point reaches the change php artisan richter:detect-changes --markdown # PR-ready markdown php artisan richter:impact "App\Services\PostPublisher" # blast radius of one symbol php artisan richter:trace PostController PostPublisher # shortest call chain between two symbols php artisan richter:affected-tests # the test selection the diff warrants
Each of these takes --json for machine-readable output. richter:detect-changes also takes --html=<path> for a self-contained visual report.
Documentation
Read the full documentation at sandermuller.github.io/richter.
Getting started
- Why Richter?: what a report tells you, what it refuses to guess at, and how the analysis runs
- Installation: requirements, the
laravel/mcpconstraint, publishing the config - Getting started: one command on a branch, and the line in the report worth acting on
- Set up your project: the setup skill, or two prompts you can paste to any agent
Change impact
- Detecting change impact: the main command, which diff is analysed, reading the report,
--explain - Report annotations: security exposure, Pennant gates, payload parity, middleware group membership
- Output formats:
--markdown,--html, and the--jsoncontract - Risk levels: the hazard tiers, the reach matrix, and the ladder that decides the level
- Gating in CI:
--fail-on,--fail-on-unresolved, and a pull-request workflow
Commands
- Blast radius of a symbol:
richter:impact - Shortest path between symbols:
richter:trace - Affected-test selection:
richter:affected-tests
Digging deeper
- Frontend changes: the Wayfinder/Ziggy bridge in full
- MCP server: the read-only tools and resources an agent can call
- Graph cache: the fingerprinted cache, profiling, scoped rebuilds
- Coverage beyond Laravel Brain: the edges a route-anchored analysis misses, and the known limits
Reference
- Configuration reference: every key in
config/richter.php - Benchmarking: scoring accuracy against replayable history
- Troubleshooting: a symptom index: empty reports, UNRESOLVED files, a level that reads medium everywhere, exit 2
Testing
composer test # test suite only composer qa-check # read-only pre-push gate: Rector + Pint dry-runs, PHPStan, tests (mirrors CI)
composer qa is the auto-fixing variant: it rewrites the working tree (Rector, Pint), so use qa-check when you only want to verify.
Changelog
See CHANGELOG for what changed per release.
Security
Found a vulnerability? Don't open an issue; see SECURITY for where to send it.
License
MIT. See LICENSE.
