# Reference patch — 01-coupons-that-stack

The answer key for how the reference absorbs this change, and the thing that makes `budget.json`
reproducible. **Never shipped to the candidate.**

1. **Add** `FixedAmountCoupon.java` (in this directory) to `reference/src/`. New file, so
   `measureChange` charges nothing for it.
2. **Add one line** to `reference/src/CartFactory.java`, in `standardCoupons()`:

   ```diff
            coupons.put("SAVE10", new PercentageCoupon(10));
   +        coupons.put("FIVER", new FixedAmountCoupon(500));
   ```

`edits.json` beside this file is the same edit in machine-readable form, so the measurement can be
re-run: baseline is `reference/src/*.java`, current is the same files with the `reference-patch`
`*.java` added and each `edits.json` replacement applied, fed to
`measureChange(baseline, current)`.

Measured: `reference_diff = 1` (1 added, 0 removed, one file touched), so
`diff_budget = max(3, 1 + ceil(1/2)) = 3`.

Both the curveball suite (7 tests) and the base suite (23 tests) are green after the patch. **No
base assertion is invalidated** — the base suite's coupon tests assert that an unknown code and a
repeated code are refused, and both are still true. That is deliberate: the base contract states
that there is exactly one coupon code *today* and that two different coupons would apply percentage
first, so this requirement adds a code without contradicting anything the candidate was told.

## Where the requirement landed, and where it did not

`Cart`, `CartLines`, `Totals`, `PercentageCoupon` and `ThresholdShipping` were not opened. The
class that runs the operations has no opinion about kinds of discount, and the class that adds the
money up has no opinion about which discounts exist.

Three decisions in the base design are what reduced this to one line, and each has a plausible
shortcut that would have cost far more:

- **`Promotion` is about *discount*, not about *coupon*.** A `Coupon` interface with a
  `percentOff()` method would have made a flat amount a second concept, and totalling would then
  need to know about both. One interface with a pure `discountMinor(lines, owed)` makes a flat
  voucher the same kind of thing as a percentage one.
- **The order lives on the promotion (`DiscountStage`), not in the wiring.** The shortcut is to
  apply promotions in the order they were registered or typed. It passes every base test — there is
  only one coupon — and then fails this curveball's third test, which types the vouchers in the
  wrong order on purpose. With a stage, "percentage first" is a property of
  `PercentageCoupon` and cannot be wired up wrongly; `Totals` sorts and `List.sort` is stable, so
  within a stage the arrival order still stands.
- **The cap is in `Totals`, once.** `FixedAmountCoupon.discountMinor` returns 500 even when 405 is
  owed, because clamping is not its business. The requirement's "we never pay a customer to shop
  with us" therefore needed no code at all. A cap written inside each promotion would be the same
  rule copied per implementation, and the copies would drift the first time two discounts combined.

The test worth watching in a candidate's design is the fourth one, where 45 off then 500 off a 450
cart leaves nothing owing for the goods and **499 still owing for delivery**. A design that
computes delivery from the subtotal, or that short-circuits a zero-payable cart, gets that wrong —
and it is exactly what the contract stated about the threshold being measured on the discounted
amount.
