Skip to main content
This guide runs one complete investigation with the Laminar debugger, from a bad output to a proven fix. You will record a buggy run, find the broken LLM call with SQL, leave notes as you go, replay from a checkpoint with earlier calls served from cache, and close with an evaluation that shows the fix holds. Every command and every output below comes from a real run, and the whole loop is exactly what a coding agent with the Laminar skill executes on your behalf.
Debugger session view showing a named investigation with notes, two trace segments, and an evaluation

The finished session: two runs, three notes, and an eval card in one timeline. This is what you build in this guide.

The example agent

The agent is a three-step changelog writer: extract user-facing changes from a commit log, classify them, and write a one-line entry under a word cap. It has a planted bug: the prompt in write_entry is missing its f-string prefix, so the model receives the literal text {max_words} instead of the number.
agent.py
The instrumentation is the highlighted lines: Laminar.initialize() once, @observe() on each step. Nothing here is debugger-specific; this is normal tracing.

1. Set up the CLI

setup logs you in, links the directory to a project via .lmnr/project.json, and writes LMNR_PROJECT_API_KEY to your env file. See debugger setup for the details. Everything below assumes you run commands from this directory: the debug session commands and the SDK both resolve the session and project from the .lmnr files here.

2. Record the buggy run

Run the agent with debug mode on:
The output makes the bug obvious. Instead of a changelog entry, the model asks a question:
The first LMNR_DEBUG=true run registers a session, opens it in your browser, and writes .lmnr/debug-session.json. The LMNR_DEBUG_RUN line at the end carries the run’s ids; the same payload is saved to the file, so you never copy ids around. Every later run in this directory rejoins the session automatically.

3. Name the session and note what you see

Name the investigation and record the observation before touching anything. The CLI targets the session from .lmnr/debug-session.json, so no session id is passed:
Notes are markdown blocks in the session timeline, slotted between the runs they comment on. Anyone watching the session view sees them land live.

4. Find the broken call with SQL

List the spans of the recorded trace, using the trace_id from the LMNR_DEBUG_RUN line:
Three LLM calls, one per step. The suspect is the last one, under write_entry. Read its input:
There it is: the model received the literal string {max_words}. Note the finding, referencing the span with an XML tag that the session view renders as a clickable chip:

5. Fix the code and replay

Fix the bug by adding the missing f prefix:
agent.py
Now rerun without paying for the two calls that already worked. LMNR_DEBUG_REPLAY_TRACE_ID is the trace you recorded; LMNR_DEBUG_CACHE_UNTIL is the span id of the last LLM call to serve from cache, which is the classify_change call, the one right before the buggy call. The last two UUID groups are enough:
Nine words, under the cap. The run rejoined the session automatically, so only the two replay variables were set. Query the new trace and the replayed calls show up as CACHED spans:
Only the fixed call ran live. On a real agent, where the prefix is minutes of tool calls and LLM turns, this is the difference between iterating in seconds and iterating in coffee breaks. See how caching works for the mechanism.

6. Prove it with an eval

A replay shows the fix works on one input. To show it holds in general, run an evaluation under the same session. The eval imports the same agent and scores the output on two axes:
changelog_eval.py
Note what you are about to verify, run it, and close the investigation:
There is no eval-specific setup: any evaluation run with LMNR_DEBUG=true resolves the session from .lmnr/debug-session.json exactly like agent runs do, and appears in the timeline as a card with per-score averages.
Debugger session view showing the replayed trace, an evaluation card with two perfect scores, and the closing note

The bottom of the session: the fixed run with its 9-word output, the eval card scoring 1.0 on both metrics, and the closing note.

7. Read the whole story back

debug session summary prints the session’s full timeline, oldest first: traces and evals as self-closing tags, notes as raw markdown. This is how a coding agent re-orients after a context reset, and it reads as the complete story of the investigation:
The session view in the browser renders this same timeline, so a human watching the UI and a coding agent reading the summary see the same story.

Hand the loop to a coding agent

Everything in this guide is a CLI command or an environment variable, which is the point: a coding agent can drive all of it. With the Laminar skill installed by lmnr-cli setup, ask Claude Code, Cursor, or Codex to “debug why the changelog agent ignores the word cap” and it runs this exact sequence, record, inspect, fix, replay, evaluate, while you watch the session view fill in.

What’s next

The debugger process

The reference for every step in this loop, including all environment variables.

How caching works

What gets cached, the input hash, and which integrations support replay.

Evaluations

Datasets, executors, evaluators, and comparing runs over time.

Browser Use debugger

The same loop applied to a slow browser agent, where replay saves the most time.