runtimeanalyz.ing

← All capabilities

how_it_got_here

CLI · MCP Pro

How execution got here. The call chains that reached a method, in call order, taken from recorded runs. It is the stack trace you'd have got if the method had raised, for every route into it.

One call tree · route 3 of 3
SubscriptionsController root
call
1
Cancellation
Cancellation
process
2
Refund
Refund
void!
3
Invoice voided
Cancellation
void!
4 · same tree, again
Invoice voided twice
Other routes Admin::InvoicesController#destroy → Invoice#void! 29 calls Refund#process → Invoice#void! 9 calls
Every record stores its call tree and its step in that tree, so each call to void! can be followed back to the root. In this tree void! is reached twice, once through Refund and once directly from Cancellation.

The problem

A support ticket says an invoice was voided twice. Invoice#void! is meant to run once, from the admin screen, and you need to find out what else ran it.

def void!
  accounts.credit(account, total, memo: "void #{number}")
  update!(state: "void")
end

A stack trace would answer that, but you get a stack trace when something raises, and nothing raised. Reading the code shows that void! is called from Admin::InvoicesController, from Refund#process and from a Cancellation service, and each of those has its own callers. That gives you a list of plausible routes without telling you which of them run.

The usual next step is to add puts caller to void!, run again and read through the log.

The fix

The recording already has the stack for every call to void! that ran:

$ ra how_it_got_here Invoice#void! --examples
Invoice#void!
· 5 distinct routes · 18 calls
SubscriptionsController#cancel
└ Cancellation#call
  └ Refund#process
    └ Invoice#void!   6 calls
  └ Invoice#void!   6 calls
Admin::InvoicesController#destroy
└ Invoice#void!   4 calls
Invoice#void!   1 call
Refund#process
└ Invoice#void!   1 call
called directly: spec/models/invoice_spec.rb:29
via Admin::InvoicesController#destroy: spec/requests/admin_invoices_spec.rb:4
via SubscriptionsController#cancel → Cancellation#call → Refund#process: spec/requests/subscriptions_spec.rb:4
via SubscriptionsController#cancel → Cancellation#call: spec/requests/subscriptions_spec.rb:4
via Refund#process: spec/services/refund_spec.rb:4

The first route contains the bug. Cancellation#call refunds the last invoice, which voids it, and then voids it again itself, so every cancellation with a refund due voids twice: six cancellations made twelve calls to void!. The last five lines name a spec example that takes each route, so the one to run while you fix this is spec/requests/subscriptions_spec.rb:4. The fix is to leave voiding to Refund:

def call
  invoice = @subscription.last_invoice
  Refund.new(invoice).process if invoice
  @subscription.update!(state: "cancelled")
  @subscription.student.current_enrollment&.withdraw
end

Where it shows up

  • Over MCP, how_it_got_here returns the merged routes as text and as structured blocks, so your agent can see which route has no spec before it changes the method.
  • On the command line, run ra how_it_got_here Klass#method, and add --examples to name the spec example behind each route.

How it works

Each record in the recording stores the call tree it belongs to and its step within that tree, along with the method that made the call and the method that received it. Starting from every recorded call to Invoice#void!, how_it_got_here walks back up its own tree, one caller at a time, to the root.

Identical chains are merged and counted, so a route that ran a thousand times appears once with its count, and a route that ran once has a line of its own beside it. Each chain is in call order, the same order as a stack trace read from the bottom.

Each tree is stamped with the spec example or request that produced it, so --examples can name a spec that takes each route, or say that none does. A route whose root is Invoice#void! itself was called directly from a spec body. Spec bodies are not traced, so the chain starts at the method.

Run it on your app

The output on this page comes from our example Rails app, and it is regenerated whenever a tool changes. These are the commands behind it. On your own app, run ra record first, then put your own class, method or file in their place.

ra how_it_got_here 'Invoice#void!' --examples

The double void is app/services/cancellation.rb:8-9. The method is app/models/invoice.rb:57.

Reading the result

  • Frames inside gems and the framework are not recorded, so a chain starts at the first method your app defines: the controller action, the job’s perform, or the method a spec called.

Every tool reads the same recording. What gets recorded and how far to trust an answer are on the questions page.

what_calls

CLI · LSP · MCP free

The callers of a method. Every caller of a method in the recorded runs, with the file and line of each call and how often it was made, including calls made through public_send. what_it_calls lists the methods it calls.

See more →

specs_that_reach

CLI · LSP · MCP Pro

Run only the specs that reach your change. The spec examples whose recorded runs reach a method. After a change you rerun those examples alone, and the red-green loop takes seconds.

See more →

how_to_reach

MCP · CLI Pro

The setup a method needs. The collaborators and the call path that put an object into the state a method needs, taken from a recorded run. Use it to write setup for tests that use real objects in place of doubles.

See more →