# Reference patch — 02-buy-two-get-one

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** `MultiBuyPromotion.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 `standard()`:

   ```diff
                    .shipping(standardShipping())
   +                .plusPromotion(new MultiBuyPromotion("TEA", 3))
                    .build();
   ```

`edits.json` beside this file is the same edit in machine-readable form. Baseline is
`reference/src/*.java`; current is the same files with this directory's `*.java` added and the
`edits.json` replacements applied; `measureChange(baseline, current)` gives the number.

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.** That is not luck: no base test asserts a total for a cart holding
three or more of any one sku, so an offer on the third tea cannot contradict one. It was worth
checking rather than assuming — a curveball that silently breaks the base suite turns a design
measurement into a scoring accident.

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

`Cart`, `CartLines`, `Totals`, `CouponBook` and both existing promotions were not opened. This is
the curveball that pays the builder off, and the measurement is the argument:

- **`.plusPromotion(...)` is one *added* line at one call site.** With a four-argument
  `Cart` constructor, an always-on offer would have meant a fifth parameter, a changed signature,
  and every construction site restating the four things it was not changing. With a builder that
  accumulates, nothing that already existed had to be rewritten — which is precisely the difference
  between D4 level 2 and level 3.
- **`Promotion.discountMinor` already took the lines.** The tempting narrower signature is
  `discountMinor(long amountOwedMinor)`, which is enough for every discount that exists in the base
  problem. It is also the signature that makes this requirement a rewrite: a multi-buy is the first
  discount that cares *what* is in the basket rather than *how much* it comes to. The lines were
  passed from the start for that reason, and it is the one piece of anticipation in the design worth
  defending as anticipation rather than as a rule the requirement stated.
- **The stage did the ordering.** "Knock the free tea off first, then work the percentage out on
  what is left" is `ITEM_PROMOTION` sorting before `PERCENTAGE_COUPON`, which the base contract
  already stated and `Totals` already implemented. A design that applied every discount to the
  subtotal gets the fifth test wrong by 45 units and will never notice why.

Two things to watch in a candidate's design:

- **The line must not shrink.** Decrementing the quantity to make an item free is the shortcut that
  passes a naive total assertion and fails `theThirdTeaIsFree`, which checks the line still says
  three at 450. The warehouse picks three; the discount is money, not stock.
- **Integer division is the whole rule.** `quantity / groupSize` free items covers three, six and
  seven with no special cases. A hand-rolled loop counting to three tends to get seven wrong.
