A server that refuses a request before storing anything has to look at every card first. zit's scan pass does that, and when it met a pushed delta it reconstructed the target against the committed store, then and there, so a bad delta could be refused without a trace. The basis had to be held.
fossil does not push one check-in at a time. It sends a batch as one round, with each manifest a delta against the previous one, and a file edited three times as three chained deltas. The basis of nearly every delta in such a round is another card of the same round, delivered a few bytes earlier or later. It is not in the store yet, because the round is not stored until the scan approves it. The scan looked in the store, found nothing, and called the card malformed.
Every multi-check-in push from a stock fossil client was refused. Every single-check-in push landed, including rounds with several file cards, which is why sixteen interop scenarios and every zit-to-zit test were green. And until 0.3.20 the refusal itself was unreadable to a fossil client, so the push reported success. A tester spent two days on it, most of them on card syntax, because "malformed" is what the card said.
0.3.21 lets the scan park a delta whose basis is not held and settle it once the whole round has been seen. A basis is either committed or was delivered by this round, possibly as another parked delta, so resolution runs to a fixpoint and the sender's order does not matter. A basis absent from both the store and the round is still refused, and now by name: the card and the basis it wanted. The draft's own rule for that case is to keep the delta and raise a phantom, which a stateless server cannot do across requests, so naming the refusal is the honest floor.
The proof is a smoke scenario that clones a served zit repository with stock fossil, commits three times offline, pushes once, and checks all three check-ins in the served store. It fails against 0.3.20.
The same release accepts application/x-fossil-debug, the request type
fossil sends under --httptrace. It was refused with a bare 415 on the
premise that the reference client never sends it. The one tool for seeing
the wire was unavailable in the one situation it exists for.