Original text:
associate the QUIC DATAGRAM frame with a stream.  If an intermediary receives a QUIC DATAGRAM frame whose payload is too short to allow parsing the Quarter Stream ID field, the intermediary MUST treat it <del> as an HTTP/3 connection error of type H3_GENERAL_PROTOCOL_ERROR. Intermediaries MUST ignore any HTTP/3 Datagram fields after the Quarter Stream ID. </del>  Endpoints parse both the Quarter Stream ID field and the Context ID field in order to associate the QUIC DATAGRAM frame with a stream and MUST abruptly terminate the corresponding stream with a stream error of type H3_GENERAL_PROTOCOL_ERROR.  4.3. The CLOSE_DATAGRAM_CONTEXT capsule (type=0x01) allows an endpoint to

Reviewer comments:
Contexts are end-to-end, and intermediaries MUST NOT parse them.
In , capsules, contexts and context IDs are explicitly defined as end-to-end and there are requirements for intermediaries to not modify them, and to not even parse them. The motivation for this was to ensure that we retain the ability to deploy extensions end-to-end without having to modify intermediaries. NAME mentioned that these requirements might be too strict, as he would like to have his intermediary be able to parse the context ID, for logging purposes.
Part of the comment from the PR is also about consistency of interface for generic HTTP libraries that can be used to build either endpoints or intermediaries. If the context ID is always present, it's overly prescriptive to say that an intermediary can't parse it, and such a requirement couldn't be enforced anyways. If context IDs are negotiated on/off, whether this is possible depends on the negotiation mechanism.
I think its fine for a component that has bytes passing through it to look at those bytes. The import thing to do is set expectations on what that means for the protocol behaviour we are defining. We don't expect intermediaries to do anything with a context ID; they should not enforce the protocol semantic rules (odd, even, reuse etc). H2 and H3 somewhat solve this problem by decoupling frame parsing from frame payload interpretation. E.g. if the form of a request or response is invalid, an intermediary shouldn't pass it on. DATAGRAM is different because it isn't defined as an actual HTTP/3 frame with frame headers and frame payload. Something that might work is to formalise that quarter stream ID is part of the envelope that intermediaries have to be able to handle the syntax of, but context ID is part of content and separate.
Unless you encrypt capsules end-to-end, I don't think any text in the spec can in practice prevent intermediaries from inspecting and modifying capsules. You can send something like GREASE capsules, though.
LGTM! :shipit:

Revised text:
associate the QUIC DATAGRAM frame with a stream.  If an intermediary receives a QUIC DATAGRAM frame whose payload is too short to allow parsing the Quarter Stream ID field, the intermediary MUST treat it  <ins> as an HTTP/3 connection error of type H3_GENERAL_PROTOCOL_ERROR.  The Context ID field is optional and its use is negotiated end-to-end, see register-no-context-capsule.  Therefore intermediaries cannot know whether the Context ID field is present or absent and they MUST ignore any HTTP/3 Datagram fields after the Quarter Stream ID. </ins> Endpoints parse both the Quarter Stream ID field and the Context ID field in order to associate the QUIC DATAGRAM frame with a stream and MUST abruptly terminate the corresponding stream with a stream error of type H3_GENERAL_PROTOCOL_ERROR. <ins> Extensions MAY define a different mechanism to negotiate the presence of contexts, and they MAY do so in a way which is opaque to intermediaries. </ins> 4.3. The CLOSE_DATAGRAM_CONTEXT capsule (type=0x01) allows an endpoint to