The one that matters is orphan PRODUCTS. A price orphan is recognised now; a
product orphan is not, and it is worse — a second product makes every
activePricesFor() ask about the wrong one, so price recognition goes blind for
that whole family and the guard disables itself through the gap beside it.
Duplicated products are reported, not deactivated. That is the owner's call and
it is the careful one: an archived PRICE is provably harmless, which is what the
grandfathering rule has always rested on, but deactivating a PRODUCT makes its
prices unsellable and contracts can be running on those. What was proven for one
is not proven for the other.
The money gate moves into the class whose docblock promises it. Today
AdoptStripePrice can compare three properties and the other four are filtered in
one client, which the fake cannot express — so no test can reach the promise the
class makes.
Also: a sweep command for orphans nobody can adopt, a day's throttle on the
warning that currently fires on every customer booking, and six mechanical
fixes.
Self-review caught one of my own: the plan to tell "created" from "adopted" by
comparing the price id before and after the call cannot work — there is no id
before, in either case. AdoptStripePrice counts its own adoptions instead, which
needs it bound as a singleton, and the spec now says why.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>