# A table holds every rule that is a function of (state, event)

A trip goes REQUESTED, MATCHED, DRIVER_ARRIVED, IN_PROGRESS, COMPLETED. A `switch (state)` inside
`handle` says that, and it is what most people write. Whether it works is not the question. What
happens when the rules move is, because the rules always move.

`corpus/trip-state-machine` puts them in a list of `Edge` values instead, each one a
`(state, event kind) -> state` row. Three real requirement changes were then measured against it. A
shortcut edge, so a driver who forgot to tap arrive can start anyway: `reference_diff: 3`, and one of
the three lines is a comma. A whole new event, a driver cancelling, across four files: 8. Then a
no-show timeout: 28.

The third is a different kind of requirement. Whether a wait has expired depends on the state, the
event, and how long the trip has sat at the kerb. `Edge` carries two inputs, so no number of rows
expresses it. `PATCH.md` says it in one line: *"The table is a function of two values, and this rule
is a function of three."*

That decision comes before the shape. Write the requirement as a signature and count its inputs. Two,
and it is a row. Three, and it belongs in a guard, asked after the table and before the log append.

`contrast/` measures both designs on three changes. The table wins the one where the rules have to
differ per market, at 4 lines against 10, and loses the other two on line count. Run
`node lessons/B6/contrast/measure.mjs`.
