AI-Assisted Debuggingon the JVM

run(application)→Unexpected behavior→Why?

What to expect

Experience & intuition

Debugging approaches

What not to expect

A model leaderboard

One universal recipe

Beyond the JVM

My path to debugging

Algorithms

Kotlin Coroutines
IDE tooling

PLDI · PPoPP

→

Concurrency
Testing

Lincheck GitHub

Concurrent JVM code

CAV · ISSTA

→

Debugging

Execution traces
Failing schedules

AI-Assisted Debugging
Debugging production

I start with println()

OrderDemo.java→PricingService.java
3PricingService pricing = new PricingService(args.length == 0);
4Order order = new Order();
5Money result = pricing.price(order);
6if (!order.currency.equals(result.currency)) {
7 throw new AssertionError("expected " + order.currency + ", got " + result.currency);
8}
Console
AssertionError: expected EUR, got USD
  at OrderDemo.main(OrderDemo.java:7)
OrderDemo.java→PricingService.java
1class PricingService {
2Money price(Order order) {
3 Money cached = prices.get(order.sku);
4 if (cached != null) {
5 return useCached(cached);
6 } else {
7 return fetchPrice(order);
8 }
9}
Console
AssertionError: expected EUR, got USD

Which branch?

OrderDemo.java→PricingService.java
1class PricingService {
2Money price(Order order) {
3 Money cached = prices.get(order.sku);
4 if (cached != null) {
+ println("useCached");
6 return useCached(cached);
7 } else {
+ println("fetchPrice");
9 return fetchPrice(order);
10 }
11}
Console
useCached
AssertionError: expected EUR, got USD

Which call?

OrderDemo.java→PricingService.java
1class PricingService {
2Money price(Order order) {
3 Money cached = prices.get(order.sku);
+ println("sku=" + order.sku +
+ ", requested=" + order.currency +
+ ", cached=" + cached);
7 if (cached != null) {
+ println("useCached");
9 return useCached(cached);
10 } else {
+ println("fetchPrice");
12 return fetchPrice(order);
13 }
14}
Console
sku=JVM-1, requested=EUR, cached=90 USD
useCached
AssertionError: expected EUR, got USD

Which values?

Agents add logs too

CURSOR · 10 DECEMBER 2025

Introducing Debug Mode

Cursor Debug Mode article preview

ReLog

Generate diagnostic logs
Refine them after execution

Debugging framework · ASE 2026

A developer and an AI robot point at each other, each holding the same log printout
MeAI

An agent runs the same loop

PricingService.java
1class PricingService {
2Money price(Order order) {
3 Money cached = prices.get(order.sku);
4 if (cached != null) {
5 return useCached(cached);
6 } else {
7 return fetchPrice(order);
8 }
9}
Console
AssertionError: expected EUR, got USD
AI ChatDemo
You · OrderDemo.java:7

Test failed: expected EUR, got USD

Locating the price calculation
✓Read PricingService.java
PricingService.java
1class PricingService {
2Money price(Order order) {
3 Money cached = prices.get(order.sku);
4 if (cached != null) {
+ println("useCached " + cached);
6 return useCached(cached);
7 } else {
+ println("fetchPrice " + order);
9 return fetchPrice(order);
10 }
11}
Console
useCached 90 USD
AssertionError: expected EUR, got USD
AI ChatDemo
You · OrderDemo.java:7

Test failed: expected EUR, got USD

Checking both price sources
✓Add call + result logs
PricingService.java
1class PricingService {
2Money price(Order order) {
3 Money cached = prices.get(order.sku);
4 if (cached != null) {
+ println("useCached " + cached);
6 return useCached(cached);
7 } else {
+ println("fetchPrice " + order);
9 return fetchPrice(order);
10 }
11}
Console
useCached 90 USD
AssertionError: expected EUR, got USD
AI ChatDemo
You · OrderDemo.java:7

Test failed: expected EUR, got USD

Checking which currency was requested
✓Inspect the request and cache key
PricingService.java
1class PricingService {
2Money price(Order order) {
3 Money cached = prices.get(order.sku);
+ println("sku=" + order.sku +
+ ", requested=" + order.currency +
+ ", cached=" + cached);
7 if (cached != null) {
+ println("useCached " + cached);
9 return useCached(cached);
10 } else {
+ println("fetchPrice " + order);
12 return fetchPrice(order);
13 }
14}
Console
sku=JVM-1, requested=EUR, cached=90 USD
useCached 90 USD
AssertionError: expected EUR, got USD
AI ChatDemo
You · OrderDemo.java:7

Test failed: expected EUR, got USD

Comparing request and cached price
✓Log SKU, currency and cached value
PricingService.java
1class PricingService {
2Money price(Order order) {
3 Money cached = prices.get(order.sku);
4 println("sku=" + order.sku +
5 ", requested=" + order.currency +
6 ", cached=" + cached);
7 if (cached != null) {
8 println("useCached " + cached);
9 return useCached(cached);
10 } else {
11 println("fetchPrice " + order);
12 return fetchPrice(order);
13 }
14}
Console
sku=JVM-1, requested=EUR, cached=90 USD
useCached 90 USD
AssertionError: expected EUR, got USD
AI ChatDemo
You · OrderDemo.java:7

Test failed: expected EUR, got USD

Connecting the observations
✓Read request and cached currency

Currency is missing from the cache key

A EUR order gets a USD price

0:00 / 0:19

Logs need a feedback loop

ReLog · ASE 2026 · revised 26 August 2026

DeepSeek-V3 · 311 faulty Java methods · correct repairs

One round of logging

Add logs → Run → Diagnose

26.0%

Improve the observations

Add logs → Run → Read output

↳ Revise logs → Rerun → Diagnose

31.2%

println() has limits

Change a log

Recompile + rerun

Inside a dependency

No source edits

A skeleton still waiting for its laptop to finish compiling

println() has limits

Change a log

Recompile + rerun

Inside a dependency

No source edits

A frustrated man pleading to get through a locked fence

Let’s use a debugger
for debugging

Run, pause, inspect, resume

PricingService.java
●1class PricingService {
●2Money price(Order order) {
●3 Money cached = prices.get(order.sku);
●4 if (cached != null) {
●5 return useCached(cached);
●6 } else {
●7 return fetchPrice(order);
●8 }
●9}
Debug: OrderDemo▶Debug
Debug session not started
Console
PricingService.java
●1class PricingService {
●2Money price(Order order) {
●3 Money cached = prices.get(order.sku);
●4 if (cached != null) {
●5 return useCached(cached);
●6 } else {
●7 return fetchPrice(order);
●8 }
●9}
Debug: OrderDemoⅡ■Running
Waiting for a breakpoint
Console
PricingService.java
●1class PricingService {
●2Money price(Order order) {order: Order@402
●3 Money cached = prices.get(order.sku);90 USD
●4 if (cached != null) {cached ≠ null
●▶5 return useCached(cached);cached: 90 USD
●6 } else {
●7 return fetchPrice(order);
●8 }
●9}
Debug: OrderDemo▶Resume■↷↓↑Suspended · main
Frames
main ▾
price:5PricingService
main:5OrderDemo
Variables
▸this = {PricingService@401}
▾order = {Order@402}
id = 42
▸sku = "JVM-1"
▸currency = "EUR"
▾cached = {Money@403}"90 USD"
amount = 90
▸currency = "USD"
Console
PricingService.java
●1class PricingService {
●2Money price(Order order) {
●3 Money cached = prices.get(order.sku);
●4 if (cached != null) {
●5 return useCached(cached);
●6 } else {
●7 return fetchPrice(order);
●8 }
●9}
Debug: OrderDemo↻RerunFinished · exit code 1

Console

Exception in thread "main" java.lang.AssertionError:
  expected EUR, got USD
  at OrderDemo.main(OrderDemo.java:7)
Tests: 1 failed

Connect your agent to IntelliJ

Settings → Tools → MCP Server
✓ Enable MCP Server
Your AI agentAuto-Configure

Other MCP clients → Copy Config

Restart the agent

Debugger tools are available

xdebug_set_breakpointxdebug_control_sessionxdebug_get_frame_values

MCP Server setup · Debugger tools · IDEA 2026.1.3+

An agent uses the debugger

PricingService.java
●1class PricingService {
●2Money price(Order order) {
●3 Money cached = prices.get(order.sku);
●4 if (cached != null) {
●5 return useCached(cached);
●6 } else {
●7 return fetchPrice(order);
●8 }
●9}
Debug: OrderDemo▶Debug
Debug session not started
Console
AI ChatDemo
You · OrderDemo.java:7

Test failed: expected EUR, got USD

Choosing where to pause
✓Set breakpoints · lines 5 and 7
PricingService.java
●1class PricingService {
●2Money price(Order order) {
●3 Money cached = prices.get(order.sku);
●4 if (cached != null) {
●5 return useCached(cached);
●6 } else {
●7 return fetchPrice(order);
●8 }
●9}
Debug: OrderDemoⅡ■Running
Waiting for a breakpoint
Console
AI ChatDemo
You · OrderDemo.java:7

Test failed: expected EUR, got USD

Waiting for either branch
✓Start OrderDemo
PricingService.java
●1class PricingService {
●2Money price(Order order) {order: Order@402
●3 Money cached = prices.get(order.sku);90 USD
●4 if (cached != null) {cached ≠ null
●▶5 return useCached(cached);cached: 90 USD
●6 } else {
●7 return fetchPrice(order);
●8 }
●9}
Debug: OrderDemo▶Resume■↷↓↑Suspended · main
Frames
main ▾
price:5PricingService
main:5OrderDemo
Variables
▸this = {PricingService@401}
▾order = {Order@402}
id = 42
▸sku = "JVM-1"
▸currency = "EUR"
▾cached = {Money@403}"90 USD"
amount = 90
▸currency = "USD"
Console
AI ChatDemo
You · OrderDemo.java:7

Test failed: expected EUR, got USD

Inspecting the cached-price branch
✓Read order and cached
PricingService.java
●1class PricingService {
●2Money price(Order order) {order: Order@402
●3 Money cached = prices.get(order.sku);90 USD
●4 if (cached != null) {cached ≠ null
●▶5 return useCached(cached);cached: 90 USD
●6 } else {
●7 return fetchPrice(order);
●8 }
●9}
Debug: OrderDemo▶Resume■↷↓↑Suspended · main
Frames
main ▾
price:5PricingService
main:5OrderDemo
Variables
▸this = {PricingService@401}
▾order = {Order@402}
id = 42
▸sku = "JVM-1"
▸currency = "EUR"
▾cached = {Money@403}"90 USD"
amount = 90
▸currency = "USD"
Console
AI ChatDemo
You · OrderDemo.java:7

Test failed: expected EUR, got USD

Checking the cache key
✓Read prices.get(order.sku)
PricingService.java
●1class PricingService {
●2Money price(Order order) {
●3 Money cached = prices.get(order.sku);
●4 if (cached != null) {
●5 return useCached(cached);
●6 } else {
●7 return fetchPrice(order);
●8 }
●9}
Debug: OrderDemo↻RerunFinished · exit code 1

Console

Exception in thread "main" java.lang.AssertionError:
  expected EUR, got USD
  at OrderDemo.main(OrderDemo.java:7)
Tests: 1 failed
AI ChatDemo
You · OrderDemo.java:7

Test failed: expected EUR, got USD

Connecting the observations
✓Resume the test

Currency is missing from the cache key

A EUR order gets a USD price

0:00 / 0:19

Does debugger access help?

debug-gym · Microsoft Research · March 2025

Claude 3.7 Sonnet · 300 SWE-bench Lite Python tasks

TASKS SOLVED · MEAN OF 3 RUNS

Edit + run
37.2%
+ interactive debugger
48.4%

The workflow matters too

Debug2Fix · Garg & Huang, Microsoft · February 2026

186 GitBug-Java tasks · pass rate

GPT-5Haiku 4.5Sonnet 4.5
Baseline60.2%71.0%75.7%
+ debugger tools60.8%70.4%64.5%
+ debugging subagent64.0%76.1%78.0%
Mandatory first debugger call73.1%82.3%85.5%
Edits locked→Call debug subagent→Edits enabled

Logpoint: Debugger in println style

PricingService.java
●1class PricingService {
●2Money price(Order order) {
●3 Money cached = prices.get(order.sku);
●4● if (cached != null) {
●5 return useCached(cached);
●6 } else {
●7 return fetchPrice(order);
●8 }
●9}
Logpoint PricingService.java:4
"sku=" + order.sku +
", requested=" + order.currency +
", cached=" + cached
More
Done
Console
PricingService.java
●1class PricingService {
●2Money price(Order order) {
●3 Money cached = prices.get(order.sku);
Logpoint"sku=" + order.sku + ", requested=" + order.currency + ", cached=" + cached
●4● if (cached != null) {
Logpoint"useCached"
●5● return useCached(cached);
●6 } else {
Logpoint"fetchPrice"
●7● return fetchPrice(order);
●8 }
●9}
Console
PricingService.java
●1class PricingService {
●2Money price(Order order) {
●3 Money cached = prices.get(order.sku);
Logpoint"sku=" + order.sku + ", requested=" + order.currency + ", cached=" + cached
●4● if (cached != null) {
Logpoint"useCached"
●5● return useCached(cached);
●6 } else {
Logpoint"fetchPrice"
●7● return fetchPrice(order);
●8 }
●9}
Console
sku=JVM-1, requested=EUR, cached=90 USD
useCached
AssertionError: expected EUR, got USD
Completed
without
suspension

Logpoints, ready for agents

JETBRAINS · INTELLIJ IDEA 2026.2

JetBrains Println Debugging Done Right article cover

Set observations

Run once

Read the output

No step / resume loop

The agent sets logpoints

PricingService.java
●1class PricingService {
●2Money price(Order order) {
●3 Money cached = prices.get(order.sku);
Logpoint"sku=" + order.sku + ", requested=" + order.currency + ", cached=" + cached
●4● if (cached != null) {
Logpoint"useCached"
●5● return useCached(cached);
●6 } else {
Logpoint"fetchPrice"
●7● return fetchPrice(order);
●8 }
●9}
Console
AI ChatDemo
You · OrderDemo.java:7

Test failed: expected EUR, got USD

Preparing the observations
✓Set all logpoints · lines 4, 5 and 7
PricingService.java
●1class PricingService {
●2Money price(Order order) {
●3 Money cached = prices.get(order.sku);
Logpoint"sku=" + order.sku + ", requested=" + order.currency + ", cached=" + cached
●4● if (cached != null) {
Logpoint"useCached"
●5● return useCached(cached);
●6 } else {
Logpoint"fetchPrice"
●7● return fetchPrice(order);
●8 }
●9}
Console
sku=JVM-1, requested=EUR, cached=90 USD
useCached
AssertionError: expected EUR, got USD
AI ChatDemo
You · OrderDemo.java:7

Test failed: expected EUR, got USD

Running without pauses
✓Completed without suspension
PricingService.java
●1class PricingService {
●2Money price(Order order) {
●3 Money cached = prices.get(order.sku);
Logpoint"sku=" + order.sku + ", requested=" + order.currency + ", cached=" + cached
●4● if (cached != null) {
Logpoint"useCached"
●5● return useCached(cached);
●6 } else {
Logpoint"fetchPrice"
●7● return fetchPrice(order);
●8 }
●9}
Console
sku=JVM-1, requested=EUR, cached=90 USD
useCached
AssertionError: expected EUR, got USD
AI ChatDemo
You · OrderDemo.java:7

Test failed: expected EUR, got USD

Connecting the observations
✓Read values and executed branch

Currency is missing from the cache key

A EUR order gets a USD price

0:00 / 0:11

Logpoints or breakpoints?

Know what to observe?

Logpoints

Set → Run → Read

Need to explore?

Breakpoints

Pause → Inspect → Choose next

Logpoints first · pause when needed

The bundled ij-debugger skill follows this strategy

Debugging
without a debugger?

A confused suited man looking around for the missing debugger

Two invoices, one number

Webshop example · Igor Kulakov, JetBrains · May 2026

Request A

INV-00001

Request B

INV-00002

Passing run · sequential requests

Request A

INV-00001

Request B

INV-00001

Rare failure · overlapping requests

A shocked hamster reacting to the duplicate invoice number

Same lines, different hit counts

IntelliJ coverage · two controlled JVM runs

InvoiceService.javaPassFail
19if (generator == null) {✓✓
20 generator = createGenerator();✓✓
22number = generator.nextNumber();✓✓
28max = repository.findMaxInvoiceNumber();✓✓

100% line coverage in both runs

InvoiceService.javaPassFail
19if (generator == null) {22
20 generator = createGenerator();12
22number = generator.nextNumber();22
28max = repository.findMaxInvoiceNumber();12

Inspect generator initialization

Sampling gives us a direction

Sampling profiler

ILLUSTRATIVE STACK SAMPLES
AppDBAppDBDBAppAppDBApp
InvoiceService.createGenerator()

A clue where to look

Rare calls may be missed

An option in production

Every execution has a history

Trace Recorder Order 42

price(order) EUR

prices.get("JVM-1") → 90 USD

cached != null → true

useCached(cached) → 90 USD

AssertionError: expected EUR, got USD

Every event is too much

1 secondof execution
≈1 GBof full trace

Calls · arguments · locals · reads · writes

Order of magnitude · varies with the program

What deserves the detail?

A frantic investigator connecting far too many log printouts on a conspiracy board

Keep the useful parts

First 2 + last 2 · preserve selected failures

0199
Iteration 0USD → USD
Iteration 1USD → USD
196 more iterations
Iteration 198USD → USD
Iteration 199EUR → USD

First 2 + last 2 · preserve selected failures

0199
Iteration 0USD → USD
Iteration 1USD → USD
196 iterations omitted
Iteration 198USD → USD
Iteration 199EUR → USD
Illustrative trace 200 iterations · 1,409 events
Batch@401.run()
loadCatalog()
decode("90 USD") → Money@403
String.split(" ") → ["90", "USD"]
Integer.parseInt("90") → 90
normalize(Money@403) → Money@403
index(Money@403)
prices.put("JVM-1", Money@403) → null
for each order
iteration 0
price(Order@404) → Money@403
prices.get("JVM-1") → Money@403
useCached(Money@403) → Money@403
validateCurrency(Order@404, Money@403)
expected = "USD", actual = "USD"
passed = 1
… iterations 1–198 …
iteration 199
price(Order@603) → Money@403
prices.get("JVM-1") → Money@403
useCached(Money@403) → Money@403
validateCurrency(Order@603, Money@403)
expected = "EUR", actual = "USD"
throw AssertionError
Selected view 1 event · Failure
throw AssertionError

Start with failures or logs

Throw · catch · assertion

Logging · stdout · stderr

Illustrative trace 200 iterations · 1,409 events
Batch@401.run()
loadCatalog()
decode("90 USD") → Money@403
String.split(" ") → ["90", "USD"]
Integer.parseInt("90") → 90
normalize(Money@403) → Money@403
index(Money@403)
prices.put("JVM-1", Money@403) → null
for each order
iteration 0
price(Order@404) → Money@403
prices.get("JVM-1") → Money@403
useCached(Money@403) → Money@403
validateCurrency(Order@404, Money@403)
expected = "USD", actual = "USD"
passed = 1
… iterations 1–198 …
iteration 199
price(Order@603) → Money@403
prices.get("JVM-1") → Money@403
useCached(Money@403) → Money@403
validateCurrency(Order@603, Money@403)
expected = "EUR", actual = "USD"
throw AssertionError
Selected view 5 events · + call path
Batch@401.run()
for each order
iteration 199
validateCurrency(Order@603, Money@403)
throw AssertionError
Illustrative trace 200 iterations · 1,409 events
Batch@401.run()
loadCatalog()
decode("90 USD") → Money@403
String.split(" ") → ["90", "USD"]
Integer.parseInt("90") → 90
normalize(Money@403) → Money@403
index(Money@403)
prices.put("JVM-1", Money@403) → null
for each order
iteration 0
price(Order@404) → Money@403
prices.get("JVM-1") → Money@403
useCached(Money@403) → Money@403
validateCurrency(Order@404, Money@403)
expected = "USD", actual = "USD"
passed = 1
… iterations 1–198 …
iteration 199
price(Order@603) → Money@403
prices.get("JVM-1") → Money@403
useCached(Money@403) → Money@403
validateCurrency(Order@603, Money@403)
expected = "EUR", actual = "USD"
throw AssertionError
Selected view 7 events · + nearby state
Batch@401.run()
for each order
iteration 199
price(Order@603) → Money@403
validateCurrency(Order@603, Money@403)
expected = "EUR", actual = "USD"
throw AssertionError

Keep the useful parts

Measured example · Kotlin compiler · KT-75831

16.4Mrecorded events
756selected events
128 kB of text for the agent

Trace Recorder: lower cost

Our internal experiments · combined results

AVERAGE MODEL COST · REPORTED UNITS

Without trace
7.806
Trace Recorder
3.336
57%lower cost
SOLVED46.7% → 50.9%

Uncertain gain
p = 0.15 · interval −1.5 to +9.9 pp

AVERAGE TIME541 → 588 s

Lower cost
Longer runs

Would you do any of this
in production?

A calm cartoon dog with coffee and a laptop surrounded by flames

Production changes the rules

Extra printlnRebuild + redeploy
Remote debuggerPauses · mutable state · JDWP
IDE logpointsDebug connection · mutable state
Coverage comparisonNo controlled reruns
Execution tracesToo much to record
Sampling profilerCan run continuously · partial evidence
×Extra printlnRebuild + redeploy
Remote debuggerPauses · mutable state · JDWP
IDE logpointsDebug connection · mutable state
Coverage comparisonNo controlled reruns
Execution tracesToo much to record
Sampling profilerCan run continuously · partial evidence
×Extra printlnRebuild + redeploy
×Remote debuggerPauses · mutable state · JDWP
IDE logpointsDebug connection · mutable state
Coverage comparisonNo controlled reruns
Execution tracesToo much to record
Sampling profilerCan run continuously · partial evidence
×Extra printlnRebuild + redeploy
×Remote debuggerPauses · mutable state · JDWP
×IDE logpointsDebug connection · mutable state
Coverage comparisonNo controlled reruns
Execution tracesToo much to record
Sampling profilerCan run continuously · partial evidence
×Extra printlnRebuild + redeploy
×Remote debuggerPauses · mutable state · JDWP
×IDE logpointsDebug connection · mutable state
×Coverage comparisonNo controlled reruns
Execution tracesToo much to record
Sampling profilerCan run continuously · partial evidence
×Extra printlnRebuild + redeploy
×Remote debuggerPauses · mutable state · JDWP
×IDE logpointsDebug connection · mutable state
×Coverage comparisonNo controlled reruns
×Execution tracesToo much to record
Sampling profilerCan run continuously · partial evidence
×Extra printlnRebuild + redeploy
×Remote debuggerPauses · mutable state · JDWP
×IDE logpointsDebug connection · mutable state
×Coverage comparisonNo controlled reruns
×Execution tracesToo much to record
✓Sampling profilerCan run continuously · partial evidence

Keep the values
Keep the app running

Live request→→Response

Snapshot

order.currency = "EUR"
cached.currency = "USD"

Verify the root cause
of production incidents

appglass.org

How AppGlass makes it possible

AppGlass Java agent installed in the JVM

Production restrictionAppGlass mechanism
Rebuild + redeploy→Install tracepoints at runtime
Production restrictionAppGlass mechanism
Rebuild + redeploy→Install tracepoints at runtime
Thread pauses→Capture values, then continue
Production restrictionAppGlass mechanism
Rebuild + redeploy→Install tracepoints at runtime
Thread pauses→Capture values, then continue
Mutable state→Validated read-only expressions
Production restrictionAppGlass mechanism
Rebuild + redeploy→Install tracepoints at runtime
Thread pauses→Capture values, then continue
Mutable state→Validated read-only expressions
JDWP connection→Connect through the AppGlass Java agent
Production restrictionAppGlass mechanism
Rebuild + redeploy→Install tracepoints at runtime
Thread pauses→Capture values, then continue
Mutable state→Validated read-only expressions
JDWP connection→Connect through the AppGlass Java agent
No controlled reruns→Capture the next matching live request
Production restrictionAppGlass mechanism
Rebuild + redeploy→Install tracepoints at runtime
Thread pauses→Capture values, then continue
Mutable state→Validated read-only expressions
JDWP connection→Connect through the AppGlass Java agent
No controlled reruns→Capture the next matching live request
Too much to record→Selected snapshots + capture limits

Expression safety checker

order.id == 42

Expression

→

Compile

Application types

→

Check

Bytecode + callees

→

Install

Validated only

MutationI/OLocksLong loopsDeep recursionBlocked

Unsafe at runtime → tracepoint disabled

Gandalf blocks the way for unsafe expressions

Control plane

AI agent

AI agent

Code editor

IntelliJ IDEA

→
Control plane routes connections

Control plane

←

Application JVM

Java agent inside the application JVM

Java agent

No inbound port needed on the application

Runtime evidence for debugging

ApproachRuntime evidenceProduction
printlnChosen events + valuesCode change + redeploy
DebuggerFrames + object statePauses · can change state
IDE logpointsChosen events + valuesDebug connection required
CoverageLines + execution countsComparable runs needed
ProfilerSampled call stacksAvailable in production
Execution traceCalls + state over timeFull capture is too large
AppGlassSelected runtime snapshotsRead-only, without pauses

Give the AI agent runtime evidence