runtimeanalyz.ing ● early access · sign-up open

← All capabilities

check_diff

CLI · CI · MCP free

Catch it in review. Point it at a branch or PR and it flags only what the change introduced, before it lands: a drifted mock, a fresh dependency cycle, new feature envy.

In checkout_spec.rb

Checkout
charge ( amount Money )
receipt Receipt
instance_double(Gateway) stubbed
allow(gateway)
  .to receive(:charge)
  .and_return(receipt)

On this branch · gateway_spec.rb

gateway_spec.rb:7 real
charge ( amount Money )
result Result
#ok? #receipt
Gateway changed in this PR
def charge(amount)
  Result.new(…)
end
Introduced drifted mock · checkout_spec.rb:12 38 findings already on main, not shown
The branch's recording is compared with main's. The stub still returns a Receipt, the real Gateway#charge now returns a Result, and only findings the change introduced are reported.

The problem

Mocks drift. Months ago you wrote Checkout’s spec with the gateway stubbed:

allow(gateway).to receive(:charge).and_return(Receipt.new(id: "rc_1"))

Today a PR changes Gateway#charge to return a Result, and updates Checkout to call #ok? on it. Gateway’s own spec is updated and goes green. Checkout’s spec still stubs the old Receipt, and it’s green too, because a double does exactly what it’s told. The build passes, review passes, and the first real checkout raises NoMethodError.

Design slips in the same way. A PR that makes Invoice call back into Report closes a dependency cycle. Nobody sees it in the diff, because the cycle is spread across two files and a method call.

Running every design check on the whole app doesn’t help a reviewer either: forty findings, thirty-eight of them older than the PR.

The fix

Check only what the change introduced:

$ ra check --since origin/main

✗ drifted mock   spec/models/checkout_spec.rb:12
  stub Gateway#charge returns Receipt
  on this branch the real one returns Result (#ok?, #receipt)
⚠ new cycle      app/models/invoice.rb ⇄ app/reports/report.rb
  lowest-traffic edge to cut: Invoice#summary → Report#row_for
· 2 findings introduced · 38 already on main, not shown

Update the stub to the recorded shape:

result = instance_double(Gateway::Result, ok?: true, receipt: Receipt.new(id: "rc_1"))
allow(gateway).to receive(:charge).and_return(result)

And run it on every PR:

- run: bundle exec rspec
- run: bundle exec ra check --since origin/${{ github.base_ref }}

A drifted mock fails the job. Design findings are posted as warnings, never as a failed build.

How it works

  1. Two recordings. The base branch’s recording is kept from its last CI run. The branch records as its specs run, and specs_that_reach keeps that to the examples the diff touches.
  2. Run the checks on both. verify_mock, break_cycle, feature_envy and the other design checks run against each recording.
  3. Keep the difference. A finding on the branch that isn’t on the base was introduced by the change. That’s the only kind it reports.

Over MCP an agent can run the same check on its own work before it opens the PR.

Limits

  • It needs the base recording. Without one from main, everything looks new. Cache the recording as a CI artefact on your main branch.
  • Only what the specs reach. A change on a path no spec runs produces no recording to compare.
  • Warnings are warnings. Only mocks that the change made wrong fail the build by default. Every design finding is a hint you can dismiss.

verify_mock

CLI · LSP · MCP · CI free

Catch a lying mock. A stub that passes green but returns something the real collaborator never would. Mutation testing can't see it. The recording can.

See more →

specs_that_reach

CLI · LSP · MCP free

Run only the specs that matter. The spec examples whose recorded runs reach a method, so a change reruns those and nothing else. Keeps a red-green loop in seconds.

See more →

break_cycle

CLI · MCP free

Cut a dependency cycle. Files or namespaces caught in a runtime dependency cycle, flagged with the single lowest-traffic edge to sever to break the loop.

See more →