Skip to content

Comparing runs

Every run that reaches the server is persisted to SQLite, so “it worked yesterday” is a question with an answer on disk rather than a shrug.

Open two browser tabs on http://127.0.0.1:4747 and select a different run in each. Both are live views of the same server, so the good run and the bad run sit next to each other on your screen.

Work the divergence from the top:

Most “it worked yesterday” bugs are a tool whose external world changed. Comparing inputs and outputs node by node finds that faster than re-reading the agent’s code.

The run picker lists runs most-recent-first with app name, status, source and error count. For a specific one, the GET /api/runs endpoint returns the same list as JSON:

Terminal window
curl -s http://127.0.0.1:4747/api/runs | jq '.runs[] | {id, app, status, errorCount, startedAt}'

Each entry: { id, app, startedAt, finishedAt, status, schemaVersion, source, eventCount, errorCount, live }. status is running | ok | error | aborted; source is live | import | demo; live says whether the owning app socket is connected right now.

For a real textual diff, export both runs to NDJSON and use your normal tools:

Terminal window
graphmind record run_01H8… --out good.ndjson
graphmind record run_01H9… --out bad.ndjson
# just the node inputs, in order, so a diff is readable
jq -r 'select(.type=="node.started") | "\(.payload.nodeId)\t\(.payload.input|tojson)"' good.ndjson > good.txt
jq -r 'select(.type=="node.started") | "\(.payload.nodeId)\t\(.payload.input|tojson)"' bad.ndjson > bad.txt
diff -u good.txt bad.txt | less

Each line of the export is a full wire envelope — { gm, seq, ts, runId, type, payload } — so any JSON tooling works on it. See recording runs.

The MCP server exposes recorded runs read-only, so an agent in your editor can do the comparison narratively:

Terminal window
claude mcp add graphmind -- npx graphmind-ai mcp

“Compare the last two support-agent runs — which node first got different input?”

list_runs, get_run, get_node and find_errors read the database directly (no server needed), and each result carries a deep link back into the viewer, so the answer cites the exact node you can then open. See the MCP reference.

The single highest-leverage habit here:

await gm.run('handle-ticket', fn); // good
await gm.run(`handle-ticket:${ticketId}`, fn); // better

The run name becomes the agent node’s label and rides along in the run’s metadata. A week later you can find the run you mean instead of guessing between six timestamps.