Example

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: True

The 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 test

Mechanism 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.

Read the chapter .

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.