debuggability-first repair
rule
do not rush to fix a bug. true bug fixing is the capability to fix all future bugs in the same class. when evidence is hard to locate, incomplete, or untrustworthy, build the reusable debugging system first, then diagnose and repair the current failure through it. the system converts one expensive investigation into a durable path from symptom to operation, correlation identity, structured evidence, proving boundary, and verification.
why
a quick fix that leaves no evidence path behind solves this failure and condemns the next one to the same archaeology. the reusable diagnostic built first makes every later failure in the class located by a query instead of a reconstruction. invariant: the same failure class is never diagnosed twice from scratch.
symptom: uploads occasionally failwithout the system: with the system built first:add a blanket retry add operation id + typed failurereturn "upload failed" record at the upload boundarycause stays unknown query the record → failed providernext failure = archaeology repair once; next failure = one query
priority decision
| observed condition | first action |
|---|---|
| reproducer and causal evidence are already accessible | use them to identify the proving boundary, then repair and verify. |
| logs lack operation/resource/correlation context | add structured diagnostic context at the boundary before changing business logic. |
| multiple systems cannot be traced through one request | establish correlation propagation and a queryable evidence trail first. |
| failure state cannot be reproduced or classified | create a narrow reproducer/fixture and typed failure taxonomy first. |
| evidence exists only as scattered strings or private console output | create an owned, searchable diagnostic surface with safe redaction first. |
the current symptom is the first proving case for the new system. no speculative telemetry: add the smallest durable evidence needed to explain the failure class and support the next repair loop.
repair workflow
- state the user-visible symptom and the fact that is wrong.
- trace backward to the first missing, ambiguous, or inaccessible evidence.
- define the reusable debugging capability: typed diagnostic, correlation id, structured log event, trace, bounded query, reproducer, or operational view.
- write its owning specification and install it at the narrowest boundary that can observe the required fact without exposing secrets.
- use the new evidence path to locate the root cause and the proving boundary.
- repair the root cause, then verify the original failure and one adjacent future instance through the same debugging capability.
- record the capability in the slice/standard so later agents start from the evidence path instead of rediscovering it.
ownership
| need | durable owner |
|---|---|
| stable public recovery category | typed API/domain diagnostic. |
| causal chain and protected cause | structured server log/trace. |
| request-to-background-work continuity | execution context and correlation identity. |
| repeatable failing condition | focused reproducer, fixture, or stable regression test. |
| recurring operational question | bounded query, dashboard, or queryable MDX/SQLite evidence index. |
| user-facing failure meaning | semantic state mapper, never raw diagnostics. |
do not
- patch a branch merely because it makes the visible symptom disappear.
- add a catch-and-continue fallback that destroys the causal evidence.
- log unstructured prose when operation, resource, state transition, and cause can be named fields.
- expose protected diagnostics, credentials, or internal stack detail to users.
- declare a bug fixed when the next agent would still need archaeology to locate the same failure class.
verification
- trigger the original failure and retrieve its evidence using only the new documented capability.
- confirm the evidence identifies operation, resource, correlation id, state, proving boundary, cause category, and remediation class.
- repair the root cause and prove the reproducer now passes.
- introduce a nearby failure in the same class and confirm the same evidence path makes it independently diagnosable.
- confirm user-facing copy remains safe and semantic while protected evidence remains accessible to authorized repair work.
good and bad
bad
// upload handler without diagnostic contextasync function uploadFile(file: File) {try {await provider.upload(file);return { success: true };} catch (error) {// no operation ID, no provider status, no typed failureconsole.error("upload failed", error);return { success: false, error: "upload failed" };}}
an upload occasionally fails. the agent adds a generic retry and returns
upload failed. with no operation ID, provider result, or stored state, the
next failure cannot tell whether the retry duplicated the upload or hid the
actual cause.
good
// upload boundary with structured diagnostic contextasync function uploadFile(file: File) {const operationId = crypto.randomUUID();const resourceId = `file/${file.id}`;try {const result = await provider.upload(file);// record success with operation ID for correlationawait logger.info("upload completed", {operationId,resourceId,provider: result.provider,status: "completed",});return { success: true, operationId };} catch (error) {// record typed failure category and causal errorawait logger.error("upload failed", {operationId,resourceId,provider: error.provider,failureCategory: error.code === "timeout" ? "transient" : "permanent",cause: error.message,});return { success: false, operationId, error: "upload failed" };}}
the boundary records operation ID, resource ID, provider status, typed failure category, and causal error in protected logs. the agent queries that record to locate the failed provider transition, repairs it, and reuses the same diagnostic path for the next upload failure.