Mostly Done.
Contents

8 Debug without guessing

When the evidence stops, the patch stops.

A coding agent can produce a convincing root-cause story before the logs have finished scrolling. That is useful while you generate hypotheses and dangerous while you repair. The faster the explanation arrives, the more important it is to separate what was observed from what merely fits.

The discipline is a plain sequence: reproduce, trace, measure, change, prove. When the evidence stops, the patch stops too.

8.1 Reproduce against the real system

A bug begins with the built binary, not a description of what the code appears to do. A formatter corrupts valid escaped quotes while its validator accepts an unmatched quote with exit code 0. Before anyone touches the parser, both halves get reproduced against the built binary at a named revision: the exact input, the corrupted output, the exit code. If the complaint is visual, drive the actual rendering path, not the unit that seems responsible.

A reproduction should record:

This keeps the investigation attached to behavior. It also lets another agent challenge the diagnosis without reconstructing the original session.

8.2 Trust numbers before screenshots

Visual bugs invite visual guessing, and a screenshot pipeline can lie as persuasively as code. Take a visual test harness that reads rendered frames back from the GPU. Its readback path used a bottom-up row order and a bottom-origin crop, and it matched a mirrored image at 92 percent, so a wrong fix passed its visual check against a wrong capture. The replacement went through the real compositor path and added an orientation anchor: a known red-and-blue image whose asymmetry calibrates row order and makes any mirrored band crater the match ratio. The capture path earned trust the same way a test does, by being shown to fail against known-bad input.

When someone says your output looks wrong, move quickly from pixels to a model:

The screenshot still matters. It tells you the product looks wrong. The numbers tell the agent where the product’s model disagrees with what the human saw.

8.3 Measure the phase named in the claim

Consider a renderer whose README quotes a cold render time from parsing through rasterization. The measured call returned a lazy image; drawing happened later, on first paint. The number also came from a debug build. A phase profiler showed platform text measurement and drawing consuming most of the cold path, while the renderer’s own parse and geometry code used much less. Nothing was wrong with measuring image construction. The error was naming it rasterization.

A trustworthy performance investigation separates phases that the runtime may schedule differently:

parse -> shape -> layout -> allocate surface -> draw -> present

The exact phases vary by system. The rule does not. Instrument the work the claim names, run the relevant build and platform variants, and keep the profiler so the number can be regenerated.

When the measurement disagrees with the documentation, change the documentation unless the discrepancy exposes a real product contract.

8.4 Do not use flaky as a cause

Consider a CI matrix whose Linux job fails intermittently and wears the flaky label until someone refuses it. The refusal turns up an emulation layer faulting the compiler under a foreign architecture. A native build removes the failure, and the rule goes into the platform documentation, where later contributors inherit the diagnosis instead of rediscovering the symptom.

Flaky describes the investigator’s view, not the mechanism. A better report states:

Not yet understood preserves the obligation to keep looking. Flaky often closes the issue without an explanation.

8.5 Stop when the missing fact is outside the evidence

Suppose your agent is chasing a patch that comes out empty. It rules out the obvious extraction and path theories, and the remaining answer depends on a diagnostic artifact nobody has. The right result is a request for that artifact, not a plausible code change.

A mature workflow permits these outcomes:

Agents are biased toward producing a diff. Humans are often biased toward rewarding one. Both need an explicit escape from speculative progress.

8.6 Preserve unsupported input

Debugging discipline shows in fallback behavior too. Picture a math renderer handed a_b_c, an invalid double subscript, which it displays as a_c. The engine silently discarded content. The repair preserved the original source and displayed a diagnostic that maps to a useful source range. It did not guess that the user meant a_{bc}.

The same ethic applies beyond parsers. A migration should preserve the record it cannot convert, and a workflow engine should expose the state it cannot resume. An editor keeps the bytes it cannot interpret. Degrade so the user’s input stays visible and locatable; silent disappearance is not graceful.

The honest system says what it could not do and keeps the evidence needed to do better later.

use ← and → to turn pages