Original text:
ClientHelloInner and ClientHelloOuter will lead to a very large overall size.  One particularly pathological example is "key_share" with post-quantum algorithms.  In order to reduce the impact of <del> duplicated extensions, the client may use the "outer_extension" </del>  extension. <del> OuterExtensions MUST only be used in ClientHelloInner.  It consists of one or more ExtensionType values, each of which reference an extension in ClientHelloOuter, and a digest of the complete ClientHelloInner. </del>  When sending ClientHello, the client first computes ClientHelloInner, <del> including any PSK binders, and then MAY substitute extensions which it knows will be duplicated in ClientHelloOuter.  To do so, the client computes the digest of the entire ClientHelloInner message as: </del>  where "inner" is the ClientHelloInner structure and "Extract", "Expand", and "Nh" are as defined by the KDF API in I-D.irtf-cfrg- hpke.  Then, the client removes and replaces extensions from <del> ClientHelloInner with a single "outer_extension" extension.  The list of outer extensions, OuterExtensions.outer_extensions, includes those which were removed from ClientHelloInner, in the order in which they were removed. This process is reversed by client-facing server.  Specifically, the server replaces the "outer_extension" with the corresponding sequence of extensions in the ClientHelloOuter.  If any referenced extensions are missing, the server MUST abort the connection with an "illegal_parameter" alert.  The server then computes the digest of the reconstructed ClientHelloInner.  If the digest does not equal OuterExtensions.inner_digest, then the server MUST abort the connection with a "decrypt_error" alert. </del>  6. It SHOULD contain TLS padding RFC7685 as described in padding. <del> The client then constructs the ClientHelloOuter message just as it does a standard ClientHello, with the exception of the following rules: </del>  It MUST offer to negotiate TLS 1.3 or above. <del> Any extensions compressed as described in outer-extensions must match the ClientHelloInner.  [[OPEN ISSUE: When #331 and compression ordering is resolved, be a bit more precise here.]] </del>  It MAY copy any other field from the ClientHelloInner except ClientHelloInner.random.  Instead, It MUST generate a fresh should provide guidance on what extensions are sensitive and suggest suitable substitutes.]] <del> To encrypt ClientHelloInner, the client first needs to generate the HPKE encryption context.  It computes the encapsulated key, context, and HRR key (see hrr) as: </del>  Note that the HPKE functions Deserialize and SetupBaseS are those which match "ECHConfig.kem_id" and the AEAD/KDF used with "context" <del> are those which match client's chosen preference from "ECHConfig.cipher_suites".  The encrypted ClientHelloInner is computed as: </del>  <del> The payload of the "encrypted_client_hello" extension in the </del>  ClientHelloOuter is a "ClientECH" with the following values: "cipher_suite", the client's chosen cipher suite; to ECHConfig, as follows: If decryption fails, the server MUST abort the connection with a <del> "decrypt_error" alert.  Otherwise, the server scans ClientHelloInner for an "outer_extension" extension and substitutes its value with the values in ClientHelloOuter and validates the ClientHelloInner digest, as described in outer-extensions. </del>  Upon determining the true SNI, the client-facing server then either serves the connection directly (if in Shared Mode), in which case it encrypted_client_hello(0xff08), with "TLS 1.3" column values being set to "CH, EE", and "Recommended" column being set to "Yes". <del> outer_extension(0xff04), with the "TLS 1.3" column values being set to "CH", and "Recommended" column being set to "Yes". </del>  11.2.

Reviewer comments:
Along the way, I've made two additional tweaks: Renamed outerextension to outerextensions, now that it contains multiple extensions. One of the diagrams even already referred to "outerextensions" because I didn't realize it was called "outerextension".Clarified in compression process and ClientHelloInner construction rules that the to-be-compressed extensions must be consecutive. Hopefully that helps with .
NAME FYI. I'm hoping this will help clarify why the ordering thing isn't actually a big deal.
I've updated the PR to include text to disambiguate the serialization. It's ambiguous whether the encoding includes the 4-byte (12-byte in DTLS) header from the Handshake structure or is just the body, from the ClientHello structure. I've picked the smaller one. This also saves having to answer, in DTLS, what happens when the inner sequence number is wrong.
There are two ClientHelloInners, the actual one used in the handshake, and the encoded one with outerextensions substituted in. Both are described as ClientHelloInner, but this makes it slightly ambiguous which goes into, say, the handshake transcript. (It's late now, so I'll leave this as an issue without a PR. If we don't end up resolving this separately, I'll probably fix this as part of . If we decide to implicitly copy over legacysession_id, the encryption payload becomes less and less defensibly an actual ClientHelloInner.)
In my own implementation, I've been referring to the plaintext payload as CompressedClientHelloInner. Which is terrible, but unambiguous.
Works for me. I was thinking EncodedClientHelloInner because it's shorter and still works even if you choose not to compress anything. (A no-op encoding is still an encoding.)
+1 to EncodedClientHelloInner, given that compression is optional. NAME if you have time, could you whip up a PR?

Revised text:
ClientHelloInner and ClientHelloOuter will lead to a very large overall size.  One particularly pathological example is "key_share" with post-quantum algorithms.  In order to reduce the impact of  <ins> duplicated extensions, the client may use the "outer_extensions" </ins> extension.  <ins> OuterExtensions consists of one or more ExtensionType values, each of which reference an extension in ClientHelloOuter, and a digest of the complete ClientHelloInner. </ins> When sending ClientHello, the client first computes ClientHelloInner,  <ins> including any PSK binders.  It then computes a new value, the EncodedClientHelloInner, by first making a copy of ClientHelloInner. It then MAY substitute extensions which it knows will be duplicated in ClientHelloOuter.  To do so, the client computes the digest of the entire ClientHelloInner message as: </ins> where "inner" is the ClientHelloInner structure and "Extract", "Expand", and "Nh" are as defined by the KDF API in I-D.irtf-cfrg- hpke.  Then, the client removes and replaces extensions from  <ins> EncodedClientHelloInner with a single "outer_extensions" extension. Removed extensions MUST be ordered consecutively in ClientHelloInner. The list of outer extensions, OuterExtensions.outer_extensions, includes those which were removed from EncodedClientHelloInner, in the order in which they were removed. Finally, EncodedClientHelloInner is serialized as a ClientHello structure, defined in Section 4.1.2 of RFC8446.  Note this does not include the four-byte header included in the Handshake structure. The client-facing server computes ClientHelloInner by reversing this process.  It scans EncodedClientHelloInner for an "outer_extensions" extension.  If there is none, the ClientHelloInner is the EncodedClientHelloInner.  Otherwise, it replaces the extension with the corresponding sequence of extensions in the ClientHelloOuter.  If any referenced extensions are missing, the server MUST abort the connection with an "illegal_parameter" alert.  The server then computes the digest of the reconstructed ClientHelloInner.  If the digest does not equal OuterExtensions.inner_digest, then the server MUST abort the connection with a "decrypt_error" alert. The "outer_extensions" extension is only used for compressing the ClientHelloInner.  It MUST NOT be sent in either ClientHelloOuter or ClientHelloInner. </ins> 6. It SHOULD contain TLS padding RFC7685 as described in padding.  <ins> If it intends to compress any extensions (see outer-extensions), it MUST order those extensions consecutively. The client then constructs EncodedClientHelloInner as described in outer-extensions.  Finally, it constructs the ClientHelloOuter message just as it does a standard ClientHello, with the exception of the following rules: </ins> It MUST offer to negotiate TLS 1.3 or above.  <ins> If it compressed any extensions in EncodedClientHelloInner, it MUST copy the corresponding extensions from ClientHelloInner. </ins> It MAY copy any other field from the ClientHelloInner except ClientHelloInner.random.  Instead, It MUST generate a fresh should provide guidance on what extensions are sensitive and suggest suitable substitutes.]]  <ins> To encrypt EncodedClientHelloInner, the client first needs to generate the HPKE encryption context.  It computes the encapsulated key, context, HRR key (see hrr), and payload as: </ins> Note that the HPKE functions Deserialize and SetupBaseS are those which match "ECHConfig.kem_id" and the AEAD/KDF used with "context"  <ins> are those which match the client's chosen preference from "ECHConfig.cipher_suites". </ins>  <ins> The value of the "encrypted_client_hello" extension in the </ins> ClientHelloOuter is a "ClientECH" with the following values: "cipher_suite", the client's chosen cipher suite; to ECHConfig, as follows: If decryption fails, the server MUST abort the connection with a  <ins> "decrypt_error" alert.  Otherwise, the server reconstructs ClientHelloInner from EncodedClientHelloInner, as described in outer- extensions. </ins> Upon determining the true SNI, the client-facing server then either serves the connection directly (if in Shared Mode), in which case it encrypted_client_hello(0xff08), with "TLS 1.3" column values being set to "CH, EE", and "Recommended" column being set to "Yes".  <ins> outer_extensions(0xff04), with the "TLS 1.3" column values being set to "", and "Recommended" column being set to "Yes". </ins> 11.2.