Skip to content

0.3.0

Choose a tag to compare

@SanderMuller SanderMuller released this 18 Jul 12:18
Immutable release. Only release title and notes can be modified.

Added

  • Graph cache. The code graph is now served from an on-disk cache keyed by a content fingerprint of everything the build reads — app/, routes/, resources/views, the relevant config, and package versions — so repeated runs and MCP sessions stop paying a full rebuild. Staleness is designed out rather than expired out: any changed input changes the fingerprint. Configurable via richter.cache.enabled / richter.cache.directory; --no-cache on all three commands bypasses it for one run.
  • --markdown on richter:impact and richter:detect-changes. GitHub-flavoured markdown for pull-request descriptions and comments: risk badge up front, changed files as a table, entry points as a review checklist with test tags, and long lists collapsed into <details> instead of truncated.
  • --explain on richter:detect-changes. Each reached entry point carries the shortest call chain down to the changed code, each hop labelled with its edge type. JSON output always includes the chains as entryPointPaths, keyed by entry point — a self-listed entry class deliberately carries no chain, so consumers can tell "reached from the change" apart from "is itself the entry surface".

Fixed

  • Diff hunk lines starting with ++ or -- were misread as file headers. A removed SQL comment in a heredoc (-- …) or an added ++ $i; statement made the parser drop the change and report a falsely-empty "no impact" — the exact failure the tool exists to prevent. The parser now tracks hunk state, so headers are only recognised in the file preamble.
  • Container-binding edges were silently absent for strict-typed providers. Service providers opening with declare(strict_types=1); produced zero binding edges. Provider scanning is now done natively (and scans every class in a provider file, not only the first), so bind()/singleton()/scoped() calls and $bindings/$singletons properties resolve regardless of the declare.
  • --explain chains are now deterministic across cache and fresh builds. Edges sort canonically before the graph is built, so a warm cache and --no-cache pick the same (equal-length) chain for the same commit.
  • The MCP detect-changes tool returns a clean error for an option-shaped base ref (e.g. --upload-pack=…) instead of leaking an uncaught exception, matching the Artisan command's behavior.
  • The eager-load relation checker no longer caches model methods for the process lifetime. In long-lived processes (MCP server, queue worker), a relation added mid-session could trigger a false "not a method on any model" finding; the scan is now fresh per run while still running at most once per invocation.
  • Graph builds no longer leave laravel-brain.* config overridden in the host application after the build — the four path keys are restored once the analysis completes.
  • The README's safety claim was corrected: analysis never executes routes, jobs, or commands, but it does autoload classes from the analyzed checkout — with guidance for running against untrusted pull-request branches in CI.

Internal

  • The consolidated per-file AST pass now collects node buckets in a single traversal (previously five full descents per file across the tracers) and retains the entry-point subset of ASTs so the entry-point tracer no longer re-parses those files.
  • Name resolution is consolidated onto shared helpers; the previous five private copies are gone.
  • The test suite grew from 267 to 337 tests: end-to-end benchmark pass/control scoring, interface-implementation and container-binding edge coverage, MCP success paths, diff-parser edge cases (CRLF, binary, mode-only), and cache round-trip determinism.

Full Changelog: v0.2.0...v0.3.0