jiebi/TextFlow huggingface dataset:
Original text:
maximum ack delay.  Packets that are not ack-eliciting are only acknowledged when an ACK frame is sent for other reasons. <del> When sending a packet for any reason, an endpoint should attempt to </del>  bundle an ACK frame if one has not been sent recently.  Doing so helps with timely loss detection at the peer.

Reviewer comments:
I believe this always should have been normative, so this change is editorial, but I'll let the editors confirm that.

Revised text:
maximum ack delay.  Packets that are not ack-eliciting are only acknowledged when an ACK frame is sent for other reasons.  <ins> When sending a packet for any reason, an endpoint SHOULD attempt to </ins> bundle an ACK frame if one has not been sent recently.  Doing so helps with timely loss detection at the peer.
Original text:
acknowledged, an endpoint SHOULD declare an earlier packet lost if it was sent a threshold amount of time in the past.  To avoid declaring packets as lost too early, this time threshold MUST be set to at <del> least kGranularity.  The time threshold is: </del>  If packets sent prior to the largest acknowledged packet cannot yet be declared lost, then a timer SHOULD be set for the remaining time.

Reviewer comments:
Adds more text about what the constant kGranularity represents where it's first used and changes a SHOULD to a RECOMMENDED. Inspired by
This seems reasonable.

Revised text:
acknowledged, an endpoint SHOULD declare an earlier packet lost if it was sent a threshold amount of time in the past.  To avoid declaring packets as lost too early, this time threshold MUST be set to at  <ins> least the local timer granularity, as indicated by the kGranularity constant.  The time threshold is: </ins> If packets sent prior to the largest acknowledged packet cannot yet be declared lost, then a timer SHOULD be set for the remaining time.
Original text:
contain the final size of the stream.  Setting this bit indicates that the frame marks the end of the stream. <del> An endpoint that receives a STREAM frame for a send-only stream MUST terminate the connection with error STREAM_STATE_ERROR. </del>  The STREAM frames are shown in fig-stream.

Reviewer comments:
I assume this has been the intention all along, but I do not find normative language in the current draft.

Revised text:
contain the final size of the stream.  Setting this bit indicates that the frame marks the end of the stream.  <ins> An endpoint MUST terminate the connection with error STREAM_STATE_ERROR if it receives a STREAM frame for a locally- initiated stream that has not yet been created, or for a send-only stream. </ins> The STREAM frames are shown in fig-stream.
Original text:
has not yet caught up. The RECOMMENDED time threshold (kTimeThreshold), expressed as a <del> round-trip time multiplier, is 9/8. </del>  Implementations MAY experiment with absolute thresholds, thresholds from previous connections, adaptive thresholds, or including RTT A connection MAY use the delay between sending a PATH_CHALLENGE and receiving a PATH_RESPONSE to set the initial RTT (see kInitialRtt in <del> ld-consts-of-interest) for a new path, but the delay SHOULD NOT be considered an RTT sample. </del>  Until the server has validated the client's address on the path, the amount of data it can send is limited to three times the amount of can be established by consecutive PTOs, but since the PTO timer is reset when a new ack-eliciting packet is sent, an explicit duration must be used to account for those cases where PTOs do not occur or <del> are substantially delayed.  This duration is computed as follows: </del>  For example, assume:

Reviewer comments:
Moves some RECOMMENDEDs up and removes others which are already elsewhere in the draft and reference those sections. This was intended to address , which I can still do in this PR if NAME would like, but I still think of Min CWND and Initial CWND as constants per-connection, even if they're not truly constants.

Revised text:
has not yet caught up. The RECOMMENDED time threshold (kTimeThreshold), expressed as a  <ins> round-trip time multiplier, is 9/8.  The RECOMMENDED value of the timer granularity (kGranularity) is 1ms. </ins> Implementations MAY experiment with absolute thresholds, thresholds from previous connections, adaptive thresholds, or including RTT A connection MAY use the delay between sending a PATH_CHALLENGE and receiving a PATH_RESPONSE to set the initial RTT (see kInitialRtt in  <ins> pto-handshake) for a new path, but the delay SHOULD NOT be considered an RTT sample. </ins> Until the server has validated the client's address on the path, the amount of data it can send is limited to three times the amount of can be established by consecutive PTOs, but since the PTO timer is reset when a new ack-eliciting packet is sent, an explicit duration must be used to account for those cases where PTOs do not occur or  <ins> are substantially delayed.  The rationale for this threshold is to enable a sender to use initial PTOs for aggressive probing, as TCP does with Tail Loss Probe (TLP) RACK, before establishing persistent congestion, as TCP does with a Retransmission Timeout (RTO) RFC5681. The RECOMMENDED value for kPersistentCongestionThreshold is 3, which is approximately equivalent to two TLPs before an RTO in TCP. This duration is computed as follows: </ins> For example, assume:
Original text:
See the Retry Packet Integrity section of QUIC-TLS.  The server populates the Destination Connection ID with the connection ID that the client included in the Source Connection ID of the Initial packet. Initial or 0-RTT packets.  A server MUST NOT send more than one Retry packet in response to a single UDP datagram.  A client MUST accept and process at most one Retry packet for each connection attempt.  After the client has received and processed an Initial or Retry packet from the server, it MUST discard any connection ID as part of its token validation logic (see token- integrity).  The next Initial packet from the client uses the connection ID and token values from the Retry packet; see negotiating-connection-ids. Aside from this, the Initial packet sent by the client is subject to absent.  A client MUST treat a failed validation as a connection error of type TRANSPORT_PARAMETER_ERROR. <del> A Retry packet does not include a packet number and cannot be explicitly acknowledged by a client. </del> 17.3. This version of QUIC defines a single packet type which uses the

Reviewer comments:
This was getting a little long. You could get lost in there. The text was fine, it was mostly well structured. All it needed was one small move and some headers.

Revised text:
See the Retry Packet Integrity section of QUIC-TLS. <ins> 17.2.5.1. </ins> The server populates the Destination Connection ID with the connection ID that the client included in the Source Connection ID of the Initial packet. Initial or 0-RTT packets.  A server MUST NOT send more than one Retry packet in response to a single UDP datagram. <ins> 17.2.5.2. </ins> A client MUST accept and process at most one Retry packet for each connection attempt.  After the client has received and processed an Initial or Retry packet from the server, it MUST discard any connection ID as part of its token validation logic (see token- integrity). <ins> A Retry packet does not include a packet number and cannot be explicitly acknowledged by a client. 17.2.5.3. </ins> The next Initial packet from the client uses the connection ID and token values from the Retry packet; see negotiating-connection-ids. Aside from this, the Initial packet sent by the client is subject to absent.  A client MUST treat a failed validation as a connection error of type TRANSPORT_PARAMETER_ERROR.  17.3. This version of QUIC defines a single packet type which uses the
Original text:
For example, if the dynamic table is 100 bytes, then the Required Insert Count will be encoded modulo 6.  If a decoder has received 10 <del> inserts, then an encoded value of 3 indicates that the Required </del>  Insert Count is 9 for the header block. 4.5.1.2.

Reviewer comments:
The algorithm was updated at somepoint but the example was lagging.
Insert Count will be encoded modulo 6. If a decoder has received 10 inserts, then an encoded value of 3 indicates that the Required Insert Count is 9 for the header block. should be Insert Count will be encoded modulo 6. If a decoder has received 10 inserts, then an encoded value of 4 indicates that the Required Insert Count is 9 for the header block. Background: I implemented ReqInsertCount for decoder. When I test it with: MaxTableCapacity = 100 EncodedInsertCount = 3 TotalNumberOfInserts = 10 My function returns 8, not 9.I guess that EncodedInsertCount = 4 is right since ReqInsertCount for encoder calculates 4 with ReqInsertCount = 9. NAME pointed out that it looks like the algorithm was replaced, and the example text we are looking at is left as is.

Revised text:
For example, if the dynamic table is 100 bytes, then the Required Insert Count will be encoded modulo 6.  If a decoder has received 10  <ins> inserts, then an encoded value of 4 indicates that the Required </ins> Insert Count is 9 for the header block. 4.5.1.2.
Original text:
MAX_STREAMS frames contain the following fields: A count of the cumulative number of streams of the corresponding <del> type that can be opened over the lifetime of the connection. Stream IDs cannot exceed 2^62-1, as it is not possible to encode stream IDs larger than this value.  Receipt of a frame that permits opening of a stream larger than this limit MUST be treated as a FRAME_ENCODING_ERROR. </del>  Loss or reordering can cause a MAX_STREAMS frame to be received which states a lower stream limit than an endpoint has previously received. STREAMS_BLOCKED frames contain the following fields: <del> A variable-length integer indicating the stream limit at the time the frame was sent.  Stream IDs cannot exceed 2^62-1, as it is not possible to encode stream IDs larger than this value.  Receipt of a frame that encodes a larger stream ID MUST be treated as a STREAM_LIMIT_ERROR or a FRAME_ENCODING_ERROR. </del>  19.15.

Reviewer comments:
This is based on and is targeted for that. Careful. I did a bit of language tweaking to the 260 limits on the MAX_STREAMS frame to make it a little more useful. The text there was a little old.

Revised text:
MAX_STREAMS frames contain the following fields: A count of the cumulative number of streams of the corresponding  <ins> type that can be opened over the lifetime of the connection.  This value cannot exceed 2^60, as it is not possible to encode stream IDs larger than 2^62-1.  Receipt of a frame that permits opening of a stream larger than this limit MUST be treated as a FRAME_ENCODING_ERROR. </ins> Loss or reordering can cause a MAX_STREAMS frame to be received which states a lower stream limit than an endpoint has previously received. STREAMS_BLOCKED frames contain the following fields:  <ins> A variable-length integer indicating the maximum number of streams allowed at the time the frame was sent.  This value cannot exceed 2^60, as it is not possible to encode stream IDs larger than 2^62-1.  Receipt of a frame that encodes a larger stream ID MUST be treated as a STREAM_LIMIT_ERROR or a FRAME_ENCODING_ERROR. </ins> 19.15.
Original text:
or more packets are sent prior to a dramatic decrease in congestion window; see persistent-congestion.  4. At a high level, an endpoint measures the time from when a packet was

Reviewer comments:
Feel free to suggest better text or other reasons, but this is what I came up with.
QUIC spec uses kMinimumWindow of 2 MSS compared to TCP's 1 MSS. Let's state the deviation and add justifiaiton text.
LGTM, modulo one suggestion

Revised text:
or more packets are sent prior to a dramatic decrease in congestion window; see persistent-congestion. <ins> 3.1.8. TCP uses a minimum congestion window of one packet.  However, loss of that single packet means that the sender needs to waiting for a PTO (pto) to recover, which can be much longer than a round-trip time. Sending a single ack-eliciting packet also increases the chances of incurring additional latency when a receiver delays its acknowledgement. QUIC therefore recommends that the minimum congestion window be two packets.  While this increases network load, it is considered safe, since the sender will still reduce its sending rate exponentially under persistent congestion (pto). </ins> 4. At a high level, an endpoint measures the time from when a packet was
Original text:
for this connection received on the old IP address, but MAY continue to process delayed packets.  9.6.3. A client might need to perform a connection migration before it has A client that migrates to a new address SHOULD use a preferred address from the same address family for the server.  9.7. Endpoints that send data using IPv6 SHOULD apply an IPv6 flow label

Reviewer comments:
This just writes down what NAME suggested in .
Two things need to be clarified around SPA: The CID is not path-specific; it's there to ensure that the client has a second CID before the handshake completes. If the client opts not to migrate, the client can use that CID on the The IP addresses in the SPA are only for use in the transition post-handshake; the client should not cache the IP addresses for any other use.
(Just filling in the text here) on the original address from the server, which is the existing connection, not-migrated. +1 to the second point
If the server is telling the client to migrate to a SPA, should this be changed to "the client MUST select one of the two server's preferred addresses ..."? If the text remains a SHOULD, and the client stays on the old server address, what can the server do if it is only willing to continue with the new connection on the SPA? The server could close the initial connection using the old server address but there isn't a transport error code (e.g. MIGRATION_ERROR) that would explicitly tell the client why the connection is being closed.
The "SHOULD" is intentional. A server cannot rely on clients moving.
Then perhaps a cautionary note such as "If the client opts not to migrate to a preferred address, the server may [lower case] decide to close the connection (e.g. SERVER_BUSY)".
If the server isn't willing at all to handle connections on its published IP address, then (naively) it shouldn't accept connections on that IP address and should publish the other one instead. The scenario this targets is that it's somehow better for the client to use a different address, but that other address can't necessarily be known in advance.
Migrating to a unicast address avoids having to perform connection-based routing through a load balancer.

Revised text:
for this connection received on the old IP address, but MAY continue to process delayed packets. <ins> The addresses that a server provides in the preferred_address transport parameter are only valid for the connection in which they are provided.  A client MUST NOT use these for other connections, including connections that are resumed from the current connection. </ins> 9.6.3. A client might need to perform a connection migration before it has A client that migrates to a new address SHOULD use a preferred address from the same address family for the server. <ins> The connection ID provided in the preferred_address transport parameter is not specific to the addresses that are provided.  This connection ID is provided to ensure that the client has a connection ID available for migration, but the client MAY use this connection ID on any path. </ins> 9.7. Endpoints that send data using IPv6 SHOULD apply an IPv6 flow label
Original text:
subjectAltName field of the certificate (see RFC6125).  For a host that is an IP address, the client MUST verify that the address appears as an iPAddress in the subjectAltName field of the <del> certificate. </del>  Clients SHOULD NOT open more than one HTTP/3 connection to a given host and port pair, where the host is derived from a URI, a selected

Reviewer comments:
Addresses NAME comments on . While this adds a new MUST NOT, I believe it's editorial because it's simply stating the implicit result of the "MUST verify" in that PR. If anyone disagrees, speak up and I'll change it. Separately, I think there's a case for this "MUST NOT" to be a "SHOULD NOT," as we all know there are situations in which clients proceed through a certificate warning. It's a reasonable semantic distinction whether the client does not consider the server authoritative but processes the result anyway (MUST NOT), or considers the server authoritative because the user told it to (SHOULD NOT). Opinions on the bikeshed welcome.

Revised text:
subjectAltName field of the certificate (see RFC6125).  For a host that is an IP address, the client MUST verify that the address appears as an iPAddress in the subjectAltName field of the  <ins> certificate.  If the hostname or address is not present in the certificate, the client MUST NOT consider the server authoritative for origins containing that hostname or address.  See Section 5.4 of SEMANTICS for more detail on authoritative access. </ins> Clients SHOULD NOT open more than one HTTP/3 connection to a given host and port pair, where the host is derived from a URI, a selected
Original text:
stop using the corresponding connection IDs and retire them with RETIRE_CONNECTION_ID frames before adding the newly provided connection ID to the set of active connection IDs.  This ordering <del> allows an endpoint that has already supplied its peer with as many connection IDs as allowed by the active_connection_id_limit transport parameter to replace those connection IDs with new ones as necessary. Failure to cease using the connection IDs when requested can result in connection failures, as the issuing endpoint might be unable to </del>  continue using the connection IDs with the active connection. 5.2.

Reviewer comments:
We already had something here, but I think that it missed the central reason, which is the combination of: wanting to replace all connection IDs having used the limit from activeconnectionid_limit wanting to ensure that there is >= 1 available connection IDs left This phrasing isn't quite that explicit, but I think that it is clear enough.
It is not obvious from this, but the intent here is to allow NEWCONNECTIONID to exceed the limit from activeconnectionid_limit.
Nice wording, looks good!

Revised text:
stop using the corresponding connection IDs and retire them with RETIRE_CONNECTION_ID frames before adding the newly provided connection ID to the set of active connection IDs.  This ordering  <ins> allows an endpoint to replace all active connection IDs without the possibility of a peer having no available connection IDs and without exceeding the limit the peer sets in the active_connection_id_limit transport parameter; see transport-parameter-definitions.  Failure to cease using the connection IDs when requested can result in connection failures, as the issuing endpoint might be unable to </ins> continue using the connection IDs with the active connection. 5.2.
Original text:
generated, it is possible that the probe timer expiration is due to an incorrect RTT estimate at the client.  To allow the client to improve its RTT estimate, the new packet that it sends MUST be ack- <del> eliciting.  If Handshake keys are available to the client, it MUST send a Handshake packet, and otherwise it MUST send an Initial packet in a UDP datagram of at least 1200 bytes. </del>  Initial packets and Handshake packets could be never acknowledged, but they are removed from bytes in flight when the Initial and its address validation (see Section 8 of QUIC-TRANSPORT).  That is, the client MUST set the probe timer if the client has not received an acknowledgement for one of its Handshake or 1-RTT packets, and has <del> not received a HANDSHAKE_DONE frame. </del>  5.2.3.

Reviewer comments:
This is what the pseudocode says, but I couldn't find anywhere in the text that clarified that if there were no ack-eliciting packets to arm PTO from, use Now().

Revised text:
generated, it is possible that the probe timer expiration is due to an incorrect RTT estimate at the client.  To allow the client to improve its RTT estimate, the new packet that it sends MUST be ack-  <ins> eliciting. </ins> Initial packets and Handshake packets could be never acknowledged, but they are removed from bytes in flight when the Initial and its address validation (see Section 8 of QUIC-TRANSPORT).  That is, the client MUST set the probe timer if the client has not received an acknowledgement for one of its Handshake or 1-RTT packets, and has  <ins> not received a HANDSHAKE_DONE frame.  If Handshake keys are available to the client, it MUST send a Handshake packet, and otherwise it MUST send an Initial packet in a UDP datagram of at least 1200 bytes. A client could have received and acknowledged a Handshake packet, causing it to discard state for the Initial packet number space, but not sent any ack-eliciting Handshake packets.  In this case, the PTO is set from the current time. </ins> 5.2.3.
Original text:
4.5.1.2. <del> The "Base" is used to resolve references in the dynamic table as </del>  described in relative-indexing. To save space, the Base is encoded relative to the Required Insert <del> Count using a one-bit sign ('S') and the "Delta Base" value.  A sign </del>  bit of 0 indicates that the Base is greater than or equal to the value of the Required Insert Count; the decoder adds the value of Delta Base to the Required Insert Count to determine the value of the

Reviewer comments:
The new principle is to only use backticks when referring to variables in pseudocode. There were two such instances that remain.
In I came up with a principle that might apply to use of . Field names, frame names, transport parameter names, and error codes don't use any form of decoration. But if reference is being made to a variable in pseudocode, we would use backticks to distinguish it from. Qpack is a little tricky here because there is a mix of things. Note: backticks are quoted in for HTML and in double quotes () in text output. Sparing use is probably best.
WFM. Thanks. I was never clear on whether "Base" or "Required Insert Count" referred to pseudocode variables or not.

Revised text:
4.5.1.2.  <ins> The Base is used to resolve references in the dynamic table as </ins> described in relative-indexing. To save space, the Base is encoded relative to the Required Insert  <ins> Count using a one-bit sign ('S') and the Delta Base value.  A sign </ins> bit of 0 indicates that the Base is greater than or equal to the value of the Required Insert Count; the decoder adds the value of Delta Base to the Required Insert Count to determine the value of the
Original text:
send multiple GOAWAY frames indicating different identifiers, but MUST NOT increase the identifier value they send, since clients might already have retried unprocessed requests on another connection.  An endpoint that is attempting to gracefully shut down a connection can send a GOAWAY frame with a value set to the maximum possible

Reviewer comments:
Draft 27 says: Maybe I'm missing it but I can't see where we say how the GoAway receiver handles an seeing the GoAway ID getting increased. We typically state what the receiver should do in such a case. For instance, with MAXPUSHID we say " receipt of a MAXPUSHID that contains a smaller value than previously received MUST be treated as a connection error of type H3IDERROR.". I suggest that we need text that states receiving a larger GOAWAY ID than previously received MUST be treated as a connection error of type H3IDERROR. N.B. since draft-27 we landed changes to make the GOAWAY symmetrical. I believe this issue is still applicable to the most recent editors draft
H3IDERROR SGTM

Revised text:
send multiple GOAWAY frames indicating different identifiers, but MUST NOT increase the identifier value they send, since clients might already have retried unprocessed requests on another connection. <ins> Receiving a GOAWAY containing a larger identifier than previously received MUST be treated as a connection error of type H3_ID_ERROR. </ins> An endpoint that is attempting to gracefully shut down a connection can send a GOAWAY frame with a value set to the maximum possible