Debug the Debugger
Part VIII β Building the AI Debugger
The checker that passed everything
Chapters 44β50 built an impressive machine: gated assistants, frozen bundles, invariant checkers, enumerated spaces, discriminating runs, verification ledgers, a benchmark design. Then a practitioner notices the invariant checker has passed every report for a month β including one she hand-verified as containing an unresolving citation. The cause is embarrassing: a bundle migration changed the hash algorithm, the checker’s resolver silently failed open, and every I-2 verdict since is theater. The debugging system debugged everything except itself.
OBSERVATION: thirty-one consecutive PASS verdicts including one report with a citation hash absent from the current manifest; resolver logs showing quiet fallback-to-pass after the migration date. HYPOTHESIS H1 (tool defect): the checker’s resolver fails open on unknown hash formats. H2 (bundle defect): the migration produced manifests the checker was never specified for. H3 (spec defect): I-2 never defined fail-open vs. fail-closed, so both implementations “comply.” INFERENCE: none yet β H1/H2/H3 predict different signatures on a pinned pre-migration bundle: H1 fails there too on new-format hashes; H2 passes there; H3 is confirmed by the spec text’s silence regardless of runs.
This chapter’s question: how do we turn the book’s method on its own tooling β catching stale bundles, broken probes, and checker bugs β before they certify fiction?
Why “the tooling is infrastructure, not suspect” fails first
The obvious move β trusting the debugger’s instruments while suspecting everything else β fails because instruments are the most leveraged liars in the system: one checker bug certifies hundreds of reports. Five self-tooling defects hide behind infrastructure-trust:
- Stale bundles. Dumps captured before a version roll, re-index, or flag flip, re-used as current evidence. Every downstream verdict inherits the staleness silently.
- Broken probes. Ranked-list inspectors, trial runners, or hash resolvers failing open/closed without signaling. A probe that cannot report its own failure reports fiction. The fail-open resolver in the opening violates one of Saltzer and Schroeder’s 1975 security principles: fail-safe defaults β base the decision on permission, not exclusion, so that the default when something is unknown is denial, not access (Saltzer & Schroeder, 1975). A checker that does not recognize a hash format must default to REJECT.
- Checker bugs. The I-1βI-6 implementation drifting from the chapter spec (fail-open resolvers, skipped plurality checks on long reports, trial-counting off-by-ones). The spec is the test oracle; the code is the suspect. Verifiers are not exempt from this: Yang and colleagues spent three years testing C compilers and reported 325+ previously unknown bugs, finding that every compiler they tested both crashed and silently generated wrong code on valid input (Yang et al., 2011). The checker that passes everything is the compiler that silently miscompiles.
- Spec gaps. Invariants underspecified at the edges (unknown hash formats, RECONSTRUCTED evidence, assistant version strings in new formats). Gaps are not edge cases β they are where failures migrate.
- Self-grading loops. The assistant that writes reports also maintaining the checker, or the benchmark scored by its own subject. Self-grading is decoration with commit access.
OPINION: every debugger eventually faces the case where the instrument is the suspect. Professionals schedule that confrontation; amateurs wait for it to schedule them.
The mental model: the inspector gets inspected. The same loop β OBSERVE β REPRODUCE β MINIMIZE β LOCALIZE β HYPOTHESIZE β EXPERIMENT β VERIFY β PREVENT β applies to checkers, builders, and runners, with one addition: each tool carries a self-test suite with known-answer fixtures (a bundle with a deliberately unresolving citation must FAIL I-2; a single-story report must FAIL I-3), run on every tool change and on a schedule. Trust in tooling is a cached verdict with an expiry date.
The method: suspect each instrument in turn
Apply the book’s loop to the three Part VIII instruments, cheapest-suspicion-first:
- Crash Dump Builder (Ch45). Failure signature: reopener cannot reproduce from the manifest. Probes: hash re-verification (do cited hashes resolve today?), version-drift audit (model/index strings vs. live system β changeable facts re-pinned and dated), ABSENT-slot review (were gaps marked or silently passed?). Known-answer fixture: a manifest with one corrupted hash must be flagged, never silently accepted.
- Diagnostic Invariant Checker (Ch46). Failure signature: impossible pass streaks, or PASS on deliberately broken fixtures. Probes: fixture battery (each invariant fed a crafted violation β unlabeled claims, unresolving cites, single-story, post-dated predictions, single-trial, confidence-justified β each must REJECT); fail-open/fail-closed audit (every resolver path reviewed for its unknown-input behavior); spec-diff (implementation vs. Chapter 46 text, line by line). Known-answer fixture: the six violations above, each mapped to its invariant.
- Benchmark Runner + Experiment Designer (Ch48/50). Failure signature: scores that never vary, or tables that always confirm favorites. Probes: calibration tasks with known diagnoses (does the pipeline convict the planted cause?), trial-count enforcement audit, provenance-split integrity (held-out arm actually held out?). Known-answer fixture: a planted-cause task the pipeline must solve and a relief-gamed transcript it must fail.
flowchart TD
T["each Part VIII instrument: dump builder / invariant checker / experiment designer + benchmark runner"] --> FX["feed every verdict-path clean AND crafted-violation known-answer fixtures"]
FX --> Q{"does the path catch every planted violation?"}
Q -->|yes| TR["TRUSTED β stamp with a re-test expiry date; scheduled re-run"]
Q -->|"passes a violation (fail-open)"| SUS["SUSPECT β quarantine that verdict-path"]
Q -->|"catches it at the wrong gate"| GAP["spec gap β amend the chapter's invariant sentence, not just the code"]
SUS --> RQ["queue past verdicts through the suspect path for re-check, newest first (quarantine, never blanket-void)"]
GAP --> FIX["fix code + spec"]
RQ --> FIX
FIX --> RP{"fixtures re-pass x3 consecutive runs?"}
RP -->|yes| TR
RP -->|no| SUS
SELF-TEST LEDGER (worked sketch; constructed, not a measured run):
Fixture F1: report with unresolving citation β I-2 must REJECT.
OBSERVED: PASS (resolver fell back to pass on unknown hash format).
=> H1 supported here (fail-open bug); FIX: fail-closed + format registry.
Fixture F2: single-hypothesis report β I-3 must REJECT.
OBSERVED: REJECT β checker healthy on this path.
Fixture F3: manifest with corrupted hash β builder must FLAG.
OBSERVED: accepted silently => builder hash-path suspect (H1-pattern).
RULE: one fixture failure suspends that verdict-path (not the whole
tool) until fixed + fixtures re-pass Γ3 consecutive runs.
OBSERVATION (constructed illustration, not a measured run): F1 fails (fail-open), F2 passes, F3 fails (silent accept) β two suspect paths, one healthy. UPDATED BELIEF: checker I-2 path and builder hash path distrusted pending fixes; I-3 path retains provisional trust; all verdicts emitted through suspect paths since the migration are marked for re-check, not automatically voided.
No tool’s self-report (“all systems nominal”), no version-string confidence, no agreement between the tool and its own logs, no single clean self-test run, and no downstream quiet (few incident reports lately) certifies an instrument. Fixtures, fixture batteries, and Γ3 re-passes decide.
Example: the migration postmortem in one page
The practitioner quarantines the suspect paths (I-2 verdicts since migration β RE-CHECK queue), fixes the resolver to fail-closed with a registered-format table, adds the unknown-format fixture to the battery, re-runs the full battery Γ3, and re-checks the queued reports β two of which flip from PASS to REJECT and reopen their cases. The postmortem records the spec gap too: Chapter 46’s I-2 said “unresolving citations β REJECT” but never named unknown formats; the spec gets one clarifying sentence. Tool fix, spec fix, case re-openings: three artifacts, each dated. The inspector, inspected.
Research lineage: mutation-test the checker, and diff it against an independent one
The fixture battery is mutation testing aimed at the checker. Mutation testing (Chapter 7) injects a deliberate fault into the code under test and asks whether the test suite catches it; the “mutation score” is the fraction caught. Here the code under test is the invariant checker, and each crafted violation β an unlabeled claim, an unresolving citation, a single-story report β is a mutant the checker must kill. A checker with a 100% fixture pass rate on clean inputs but a low kill rate on violated ones is exactly the untested test suite mutation testing was designed to expose.
Differential testing is how Csmith found silent miscompiles, and it works here. Yang and colleagues could not predict the correct output for a random C program, so they compiled it with several compilers and flagged any disagreement (Yang et al., 2011). The checker has the same property: for a borderline report there may be no ground-truth verdict, but two independent implementations of I-1βI-6 that disagree localize a bug in at least one. Where a second implementation is too expensive, a second operator hand-checking the same report is the cheap version β and where even that is too much, equivalence modulo inputs is a third option: mutate the parts of a report the verdict should not depend on β a reworded thought line, two reordered independent observations, an unconsumed trailing field β and require the same verdict. A verdict that flips under a change it should be blind to has localized a checker bug with one implementation and no oracle (Le et al., 2014).
Known-answer self-tests on startup are a regulated norm elsewhere. Cryptographic modules certified to NIST FIPS 140 must run known-answer tests β fixed input, fixed expected output β every time they start, and refuse to operate if any fails. The Part VIII tools deserve the same: the fixture battery runs on every tool change and on a schedule, and a failed fixture quarantines the path rather than logging a warning nobody reads.
Lab 51: plant a fault in your own tooling path (proposed)
PROPOSED, not executed: no author-measured results are reported. The evidence this chapter requires is the reader’s own self-test log.
Setup. Take one Part VIII paper tool you actually use (checklist, manifest, or check log). The fault presence (clean path vs. one planted fault: corrupted hash, unlabeled claim, single-story report, or post-dated prediction) is the independent variable; the tool form and operator are controlled. A second reader (or blinded self after β₯48h) runs the tool.
Task.
- Before planting, write H1/H2/H3 with distinct predicted detection outcomes: H1: “the tool path catches the planted fault at its named invariant/slot”; H2: “the tool passes the fault (fail-open β suspect path found)”; H3: “the tool catches it but at the wrong gate (spec gap located).”
- Plant one fault per series; run the tool blind; record catch/pass/misgate with the deciding record quoted.
- For any pass, file the spec-or-code defect and re-run Γ3 after fixing.
| Hypothesis | Predicted self-test signature | FORECAST | OBSERVATION (Γ3) | UPDATED BELIEF |
|---|---|---|---|---|
| H1 tool healthy | caught at named gate | ___ | ___ ___ ___ | live/exonerated |
| H2 fail-open | passed silently | ___ | ___ ___ ___ | live/exonerated |
| H3 spec gap | caught at wrong gate | ___ | ___ | live/exonerated |
Success criterion. A self-test log with planted faults, blind verdicts, quoted deciding records, and filed fixes with Γ3 re-passes. An untested tool declared “working” is explicitly not completion.
Companion tool: Debugger Self-Test Suite
What it accepts: the current tool implementations/forms (builder, checker, designer, runner, ledgers), the spec texts (Ch44β50 requirements), and the fixture battery with known-answer verdicts. What it performs: it runs every fixture against every tool path (clean + violated inputs), audits fail-open/fail-closed behavior on unknown inputs, diffs implementation against spec, enforces Γ3 consecutive re-passes after fixes, tracks verdict-path trust (TRUSTED / SUSPECT / QUARANTINED) with expiry dates, and queues past verdicts through newly-suspect paths for re-check. What it can establish: which tool paths are currently trustworthy and which past verdicts need re-checking β for the examined toolset and fixture battery only. What it cannot establish: bug-freedom, future correctness, or completeness of the fixture battery itself (fixtures are hypotheses about failure, not proofs). It never treats self-reports, version confidence, log agreement, single clean runs, or quiet downstream as health evidence. How its output changes your next action: path TRUSTED β use with scheduled re-test; SUSPECT β quarantine that verdict-path, fix, re-pass Γ3; QUARANTINED past verdicts β re-check queue, newest first; spec gap found β amend the spec sentence, never just the code.
Paper form, sufficient for this chapter:
Toolset ___ Spec versions ___ Fixtures (n=___) Date ___
PATHS: builder-hash ___ | I-1 ___ I-2 ___ I-3 ___ I-4 ___ I-5 ___ I-6 ___
designer ___ | runner-trials ___ | runner-provenance ___
FIXTURES: pass ___/___ (failures: ___ at paths ___)
TRUST: TRUSTED ___ | SUSPECT ___ | QUARANTINED ___ (re-check queue n=___)
RE-PASS Γ3: ___ NEXT SCHEDULED TEST: ___ (trust expires then)
Where a software implementation does not yet exist in the reader’s stack, this record is the tool. Quis custodiet, answered with fixtures.
Reusable procedure: distrust your instruments on schedule
- Fixture every path β each invariant/slot gets clean + violated known-answer inputs.
- Audit unknowns β every resolver/probe names its fail-open/fail-closed behavior; fail-open requires written justification or removal.
- Diff spec vs. code β implementation checked against the chapter text, not against its own logs.
- Quarantine, don’t void β suspect paths suspend new verdicts; past verdicts queue for re-check, newest first.
- Expire trust β every TRUSTED stamp carries a re-test date; expiry returns the path to SUSPECT until re-passed Γ3.
Failure modes
- Infrastructure exemption. “Tools aren’t suspects.” Leverage inverts this: the most-used path deserves the most suspicion.
- Self-grading. Assistant maintaining its own checker. Independence or decoration.
- Fixture-free trust. “It worked so far.” So-far is a single run stretched over time.
- Void-or-ignore. Either voiding all past verdicts (panic) or none (denial). Quarantine + prioritized re-check is the middle path.
- Code-only fixes. Patching the resolver without amending the spec sentence that permitted ambiguity. Gaps refill unless specced shut.
- Silent re-check flips. Quietly flipping old PASSes to REJECTs without notifying case owners. Re-checks are verdicts; verdicts are communicated.
Limits, per contract: one self-test covers one toolset under one spec version and fixture battery; it warrants path-trust, not bug-freedom; spec or dependency changes expire trust until re-passed. UNKNOWN wherever fixtures are absent or trust is expired. And there is a floor: self-testing reduces the trusted computing base of your debugger to the spec you wrote, the fixtures you chose, one independent operator, and the hardware β it pushes that boundary outward but never removes it. Thompson’s Reflections on Trusting Trust (Thompson, 1984) is the canonical statement of the limit: you cannot verify all the way down.
References
- Jerome H. Saltzer and Michael D. Schroeder. The Protection of Information in Computer Systems. Proceedings of the IEEE 63(9), 1975, pp. 1278β1308. https://doi.org/10.1109/PROC.1975.9939
- Xuejun Yang, Yang Chen, Eric Eide, and John Regehr. Finding and Understanding Bugs in C Compilers. Proceedings of the 32nd ACM SIGPLAN Conference on Programming Language Design and Implementation (PLDI), 2011, pp. 283β294. https://doi.org/10.1145/1993498.1993532
- Richard A. DeMillo, Richard J. Lipton, and Frederick G. Sayward. Hints on Test Data Selection: Help for the Practicing Programmer (mutation testing). Computer 11(4), 1978, pp. 34β41. https://doi.org/10.1109/C-M.1978.218136
- National Institute of Standards and Technology. FIPS 140-3: Security Requirements for Cryptographic Modules (known-answer self-tests). 2019. https://doi.org/10.6028/NIST.FIPS.140-3
- Vu Le, Mehrdad Afshari, and Zhendong Su. Compiler Validation via Equivalence Modulo Inputs. Proceedings of the 35th ACM SIGPLAN Conference on Programming Language Design and Implementation (PLDI), 2014, pp. 216β226. https://doi.org/10.1145/2594291.2594334
- Ken Thompson. Reflections on Trusting Trust. Communications of the ACM 27(8), 1984, pp. 761β763. https://doi.org/10.1145/358198.358210
Debugging Checklist
- Every tool path has clean + violated known-answer fixtures?
- Fixture battery run against current implementation (not just forms)?
- Fail-open/fail-closed behavior named for every resolver and probe?
- Implementation diffed against the chapter spec text?
- Suspect paths quarantined with past verdicts queued newest-first?
- Fixes re-passed Γ3 consecutively before trust restored?
- Spec sentences amended where gaps permitted the defect?
- Trust stamps dated with expiry and next scheduled test?
What This Chapter Established
- Debugger self-test: the book’s loop applied to its own instruments β stale-bundle detection, broken-probe audits, checker fixture batteries, spec-diffing, quarantine-with-re-check, and expiring trust β demonstrated on the constructed fail-open resolver case, no measured runs claimed.
- The inspector-inspected mental model with the leveraged-liar principle and the quarantine-not-void rule.
- Lab 51 as a proposed planted-fault self-test the reader executes; the Debugger Self-Test Suite contract (accepts/performs/can-establish/cannot-establish/next-action).
- What was NOT proved: any tool’s correctness, any fixture battery’s completeness, or any standing verdict’s immunity. One mirror held up; nothing certified.
- Research grounding: the fail-open resolver violates fail-safe defaults (Saltzer & Schroeder) β the default on an unknown input must be REJECT; verifiers are software and every C compiler tested both crashed and silently miscompiled (Yang et al.), so the checker-that-passes-everything is that silent miscompile; the fixture battery is mutation testing aimed at the checker (DeMillo et al.), differential testing against a second implementation or operator localizes disagreements, equivalence-modulo-inputs finds verdict-flips under changes the verdict should ignore with no second implementation (Le et al.), and known-answer self-tests on every tool change are a regulated norm elsewhere (FIPS 140); and self-testing has a floor β you cannot verify all the way down (Thompson).
- Part VIII’s closing map: Chapter 44 set the oversight stance, 45 froze the bundle, 46 enforced the invariants, 47 enumerated the space, 48 priced the runs, 49 set the verification bar, 50 designed the benchmark, this chapter tested the instruments. The debugger, built and self-suspecting.
Next
The debugger is built, gated, and self-tested β on frozen pasts. But frozen pasts are postmortems; production failures are fires, and fires need instruments that are already running when the alarm sounds: logging, containment, and replay that survive the incident instead of being assembled after it. What that always-on instrumentation is β and what it costs β is Part IX’s to establish, not this one’s. Chapter 52, “AI Observability,” opens that Part; this chapter claims nothing about its results.