Find the first divergence, then move the failure with an override
Read the invoice intermediates in order, mark the first that departs from intent, then force discount to its intended value and watch the total become right.
Debugging is not making a symptom disappear. It is finding the earliest point where execution left intent, and proving that point causes the failure by changing it and nothing else. This program does both halves on the chapter’s billing example: an intent table read in order, and a counterfactual override forcing the discount to the value the spec requires.
Run it
Save this as invoice_divergence.py and run it with Python 3.11 or later. It uses the standard library only.
"""Chapter 1: locate the first divergence, then intervene with a counterfactual."""
def invoice_total(items, *, discount_override=None):
"""The chapter's function, with the stray reassignment that causes the failure."""
subtotal = sum(i["price"] * i["qty"] for i in items)
discount = subtotal * 0.10 if subtotal >= 1000 else 0.0
discount = 0.0 # the defect
if discount_override is not None: # counterfactual hook
discount = discount_override
total = subtotal - discount
return round(total * 1.20, 2)
items = [{"price": 600, "qty": 1}, {"price": 600, "qty": 1}]
subtotal = sum(i["price"] * i["qty"] for i in items)
inside = subtotal * 0.10 if subtotal >= 1000 else 0.0
after = 0.0 # the stray reassignment
total = subtotal - after
checkpoints = [
("subtotal", 1200.0, subtotal),
("discount", 120.0, after),
("total", 1080.0, total),
("tax", 216.0, round(total * 0.20, 2)),
]
print("intent table (spec vs observed):")
for name, intended, observed in checkpoints:
verdict = "match" if intended == observed else "DIVERGES"
print(f" {name:9} intended={intended:8.1f} observed={observed:8.1f} {verdict}")
print(f"discount inside the branch: {inside:.1f} after the branch: {after:.1f}")
print(f"first divergence: {next(n for n, i, o in checkpoints if i != o)}")
observed = invoice_total(items)
counterfactual = invoice_total(items, discount_override=120.0)
print(f"observed invoice_total(items) -> {observed}")
print(f"counterfactual invoice_total(items, discount_override=120.0) -> {counterfactual}")
print(f"failure moves under the counterfactual: {observed != counterfactual}")Expect two failures to fix: the discount correct inside the branch and zero after it, and the total moving from 1440.0 to 1296.0 under the override.
Expected outcome
intent table (spec vs observed):
subtotal intended= 1200.0 observed= 1200.0 match
discount intended= 120.0 observed= 0.0 DIVERGES
total intended= 1080.0 observed= 1200.0 DIVERGES
tax intended= 216.0 observed= 240.0 DIVERGES
discount inside the branch: 120.0 after the branch: 0.0
first divergence: discount
observed invoice_total(items) -> 1440.0
counterfactual invoice_total(items, discount_override=120.0) -> 1296.0
failure moves under the counterfactual: TrueThe last two lines are the chapter’s own figures: 1440.0 observed and 1296.0 under the counterfactual, the value the spec requires for a €1,200 order.
The intent table names the line to read
subtotal matches intent, so the subtotal computation is not the suspect.
discount is the first line that departs, so the investigation belongs there
and everything downstream is effect. The final €1,440 names none of the three
competing hypotheses; the intermediate does.
The inside-versus-after probe separates them. Reading the discount inside the branch and again after it distinguishes H1 (the condition never fired, so the discount is zero in both places) from H2 (assigned then overwritten, so 120.0 inside and 0.0 after). The chapter’s hypothesis record keeps the reading separate from the observation and names the next test first:
hypothesis:
id: H2
claim: discount is assigned then overwritten
status: SUPPORTED # -> CAUSALLY SUPPORTED after the override testMechanism and limitations
Documented: the chapter states that the failing order produces subtotal
1200, discount 120, total 1080, tax 216 and an invoiced 1296 against an
observed 1440; that the intent table marks discount as the first divergence
with total and tax downstream; that the stray reassignment after the
assignment block is the defect; and that the counterfactual override moves the
failure from 1440.0 to 1296.0, still not proof, since the override could mask
a second defect downstream.
Observed: the run above. The intent table marked discount as the first
divergence and total and tax as downstream. The discount read 120.0 inside
the branch and 0.0 after it, the observation the chapter uses to reject H1 and
support H2. The observed invoice was 1440.0 and the counterfactual invoice
was 1296.0, so the failure moved when and only when the discount was forced
to its intended value.
Not shown. One input, one code version, one trial, so the diagnosis is scoped to that envelope. The reverse intervention is absent: the chapter requires deleting the stray line and re-adding it to restore the failure before a hypothesis may be called CONFIRMED. The override could be masking a second defect downstream, and nothing here tests for one. Of the three hypotheses, only H1 and H2 are distinguished. Minimization, the boundary inputs 999 / 1000 / 1001 and the regression test are absent.
Understand this example
Copy this prompt into your AI tool. No code runs here.
Apply this example
Copy this prompt into your AI tool. No code runs here.