Code change:
The best and easiest plan is to discard, but we know that throwing out perfectly good packets can have bad effects on performance. It sends a bad signal to a congestion controller for instance. So we need to say either discard, or ensure that the data is properly attributed and consistently processed.
I think you covered it all this way. +1.
The conclusion in the draft is that early data is relatively "safe" (for some definition of the term, see the draft for details) if the handshake is complete. Early data in a transport that provides ordered delivery will always arrive before the handshake completes. But this is not the case in QUIC, or anything like DTLS. We should offer some advice for these situations. From martinthomson/http-replay
But can this really happen ? I mean, my understanding is that the handshake happens in response to a Hello message which also conveys early data. Can we have early data outside of a hello message, or may we receive a handshake response before a hello message ?
With reordering of packets in QUIC, you can get a packet that was sent before the handshake completes after the handshake completes. The order of sending might be "ClientHello, 0-RTT1, 0-RTT2, Client Finished", but the server can get "ClientHello, 0-RTT2, Client Finished, 0-RTT1". Early data is sent in separate packets (this is a hard rule right now, but we might change the format so that the packets can be sent together in future). This is unlike TCP fast open where you only get to send the "early data" in the SYN segment. You can send an awful lot of early data if you want.
OK thanks for the explanation. I tend to think that such behaviours are out of the current scope of the draft but will fall into it as QUIC progresses. Maybe all of this can be summarized (for now) as "In case a TLS implementation supports receiving early data after the handshake completes, such early data has to be silently discarded". That covers this case for QUIC and any possible TLS implementation specificity or even bug.
I think that we might add "... discarded unless the data can be unambiguously identified as being early data" or something like that. After all, it's not replayed data if the handshake completes, so I don't want to prohibit its use. The main problem is that it's hard to properly identify and handle. FWIW, we debated this for our DTLS 1.3 implementation and chose to discard. That is the easiest answer.
I'd still be tempted to simply discard for now, just to avoid the problems of confusing the client with an out-of-order handshake that may make it think the "late" early data were lost while in the end they are considered. I'd rather avoid a client sending a request again as regular data and seeing the server concatenate it with the late arrived early data for example. I really think it's something we can improve later as the out-of-order use cases become more obvious. It would also offer us a safety belt allowing more fancy stuff in QUIC, otherwise we could end up saying "nah don't do this or that, it could cause trouble with early data". The rule might then be relaxed. It will not affect TCP-based implementations anyway.

Related discussion:
text: status code often leads to clients retrying requests, which could result in increased load. <ins> 6.4. In protocols that deliver data out of order (such as QUIC HQ) early data can arrive after the handshake completes.  This leads to potential ambiguity about the status of requests and could lead to inconsistent treatment (see be-consistent).  Implementations MUST either ensure that any early data that is delivered late is either discarded or consistently identified and processed. </ins> 7. This document registers the "Early-Data" header field in the "Message