# Reference patch — 02-the-kitchen-says-no

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

1. **Overlay** `../contract-delta/OrderState.java` and `../contract-delta/FoodOrderApi.java` onto
   `contract/`. Contract files are given, so nothing here is charged. `OrderState` gains
   `PROBLEM` and counts it as terminal; `FoodOrderApi` gains `void reject(String orderId)` and the
   table row that says who is told and what the stage does for itself.
2. **Add** `ProblemStage.java` (in this directory) to `reference/src/`. New file, so
   `measureChange` charges nothing for it.
3. **Change one line** in `reference/src/Stages.java`, in `standard()` — the by-name list, which is
   where a stage you reach by asking for it goes:

   ```diff
   -                List.of(new CancelledStage()));
   +                List.of(new CancelledStage(), new ProblemStage()));
   ```
4. **Add five lines** to `reference/src/OrderBook.java`, immediately after `cancel`:

   ```java
       @Override
       public void reject(String orderId) {
           Order order = find(orderId);
           lifecycle.named(OrderState.PROBLEM).enter(order, clock, notifier);
       }
   ```

Measured: `reference_diff = 7` (5 added in `OrderBook.java`, 1 added and 1 removed in
`Stages.java`), so `diff_budget = max(3, 7 + ceil(7/2)) = 11`.

Verified after the patch: the curveball suite is 10 tests green, and the base suite is 23 tests
green. Nothing in the base suite calls the new operation, and nothing in the base suite changes
behaviour, so the two enum constants and the new method are additive in the strict sense.

## Why this one costs more than the other two, and why that is the right amount

The other two requirement changes cost two lines each and did not open `OrderBook`. This one does,
and it is worth being precise about why rather than treating it as a failure.

**A new rule is free; a new operation is not.** Curveballs 01 and 03 both change a *rule* — when a
stage is allowed, and what an order costs. Rules have somewhere to live, so they cost a new file and
a line of wiring. This curveball asks for something a caller can *do* that it could not do before,
and a new verb on the interface has to have a body in the class that implements the interface.
There is no seam that makes that free, and a design that claimed to would be hiding the operation
somewhere worse.

**What matters is what those five lines contain.** Read them again: find the order, ask the
lifecycle for the stage, ask the stage to take it. No conditional. No mention of `PREPARING` or
`OUT_FOR_DELIVERY`. No clock reading, no notification, no arithmetic, no history. They are
`cancel`'s five lines with one identifier changed — and that is the actual claim being made, that
the coordinator grows a line per **operation** and never a line per **rule**.

Put the two methods side by side in a defence:

```java
    public void cancel(String orderId)  { ... lifecycle.named(OrderState.CANCELLED).enter(order, clock, notifier); }
    public void reject(String orderId)  { ... lifecycle.named(OrderState.PROBLEM).enter(order, clock, notifier); }
```

The customer's branch and the restaurant's branch differ, in the class that runs every order on the
platform, by the name of a stage. Everything that actually differs between them — that one window
shuts a stage earlier than the other — is in two files neither of them mentions.

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

`OrderStage` was not opened: **the skeleton every transition runs did not change to accommodate a
new kind of transition.** Nor were `Order`, `Lifecycle`, `PlacedStage`, `CancelledStage`,
`DeliveredStage`, `PlainStage`, `Tariff`, `StandardTariff`, `DeliveryFeePolicy` or
`DistanceBandFee`.

**What the requirement itself cost: one new stage overriding the two hooks it already has.** Read
`ProblemStage` and notice how much of the requirement is not written down anywhere:

| The requirement said | Where it is enforced |
|---|---|
| it is timestamped like any other stage | nowhere — step 3 of the skeleton, and a stage cannot opt out of it |
| tell the customer, then the restaurant | a constructor argument — data about the stage, not code in it |
| a refusal notifies nobody | nowhere — the guard is step 1 and notifying is step 5, so there is nothing to suppress |
| a refusal leaves no history entry and does not move the order | nowhere — the guard runs before step 2, so there is nothing to undo |
| a channel falling over does not undo it | nowhere — `tell` swallows per audience and runs after the order has moved |
| whatever you keep must still show how far it got | nowhere — the history is append-only and the entry before the last one *is* the answer |
| it cannot happen twice, or to a finished order | one `Set` that does not contain `PROBLEM`, `DELIVERED` or `CANCELLED` |
| the restaurant's window is one stage longer than the customer's | one element of difference between two `EnumSet`s |
| charge nothing, but keep both quotes readable | `order.charge(0)`, because `charge` is the only mutator that can touch the money and it cannot reach a quote |

Nine rules; one `Set`, one constructor call and one `charge(0)`. **That is the template method's
payoff**: a new stage says the one or two things that are different about it, and inherits
everything a stage has in common with every other stage from somewhere it cannot reach.

## What this costs without the seam

The plausible alternative is to write the transition out inside the new method, because it is "just
one method":

```java
    @Override
    public void reject(String orderId) {
        Order order = find(orderId);
        if (order.state() != OrderState.PLACED
                && order.state() != OrderState.ACCEPTED
                && order.state() != OrderState.PREPARING) {
            throw new IllegalStateException("an order that is " + order.state() + " cannot be refused now");
        }
        Instant at = clock.instant();
        order.moveTo(OrderState.PROBLEM);
        order.charge(0);
        List<Audience> told = new ArrayList<>(2);
        for (Audience audience : List.of(Audience.CUSTOMER, Audience.RESTAURANT)) {
            try {
                notifier.send(audience, orderId, OrderState.PROBLEM);
                told.add(audience);
            } catch (RuntimeException channelDown) {
                // a failed notification is lost on purpose
            }
        }
        order.record(new StageEvent(OrderState.PROBLEM, at, told));
    }
```

**Measured, not asserted: 25 added lines** in `OrderBook.java` — against this patch's 7, of which 5
are the same method reduced to delegation. Both pass all ten curveball tests.

The line count is the least of it. This version is the *fifth* copy of the five steps, and it is the
copy where the ordering guarantees stop being guarantees: nothing stops a later edit moving
`notifier.send` above `order.record`, or reading `clock.instant()` a second time inside the loop.
The four things the skeleton makes true of stages that do not exist yet — timestamped once, applied
before worked, worked before recorded, recorded before told — are true here only for as long as
somebody keeps them true by hand. The refusal path is the sharpest example: in the reference, "a
refused transition changes nothing" is a property of *where the guard sits in the skeleton*; here it
is a property of the author having remembered to put the `if` at the top, in a method that already
mutates three things below it.

## The sibling question an interviewer will ask

"`ProblemStage` and `CancelledStage` are nearly the same file. Why not one class with a flag?"

It is a fair question and the answer is in `ProblemStage`'s javadoc: they are the same today and
they are the two things most likely to diverge, because one is the customer's decision and the other
is ours. A cancellation grows a cancellation fee; a problem grows an apology voucher and a mark
against the restaurant. The duplication is four lines of set membership; the shared class would
start branching on the discriminator at the first divergence, and then every line in it has to be
read twice to work out which of the two cases it is about. And the duplication that would actually
hurt — the timestamp, the ordering, the history, the notification, the refusal semantics — is not
duplicated at all, because it is in `OrderStage` once.
