The anti-corruption layer that needed a second mapper

One supplier, two API versions running simultaneously during their own migration, and a translation layer built for one shape.

interface ConsignmentMapper
{
    public function supports(array $payload): bool;
    public function toConsignment(array $payload): Consignment;
}

// selected by a discriminator in the payload, because
// the supplier sends both shapes to the same webhook
// endpoint during their rollout.

final class MapperChain implements ConsignmentMapper { /* ... */ }

The layer had assumed one upstream shape at a time, which is the assumption every translation layer starts with and which a supplier mid-migration violates. A chain selected by a discriminator is the smallest change that handles it, and the discriminator itself was not documented — it was found by logging payloads and diffing.