Original text:
Given a string of Unicode characters as input_string, return an ASCII string suitable for use in an HTTP field value.  Let byte_array be the result of applying UTF-8 encoding UTF8 to <del> input_string.  If there is an error in doing so, fail parsing. </del>  Let encoded_string be an empty string. For each byte in byte_array: <del> If byte is %x25 ("%"), append "%25" to encoded_string. </del>  <del> If byte is in the ranges %x00-1f or %x7f-ff, apply the percent- encoding defined in Section 2.1 of URI to byte and append the result to encoded_string. </del>  Otherwise, decode byte as an ASCII character and append the result to encoded_string. <del> Let formatted_string be the result of running Serialising a String (ser-string) with encoded_string. </del>  <del> Return the character "%" followed by formatted_string. </del>  4.2.

Reviewer comments:
Looks good except for minor details and potentially a bug (encoding vs decoding). I'd also like to see the spec enforce a consistent form of percent encoding (lower vs upper)
I am sceptical wrt "use the URI percent encoder". The reason being: percent encoding rules depend on what part of the URI needs encoding (and many APIs do not get that) calling the API for a single character multiple times usually requires converting a char to a string, and that might not be good for perf if we really care about that
I've removed the reliance on URI for percent encoding; was clean as NAME said it would be. Regarding case -- NAME RFC4648 Section 8 is explicitly case-insensitive. Given that and the discussion in the group yesterday, I've left this case-insensitive. Say if you still disagree.
NAME , I was suggesting an inline definition. The fact that uppercase is base16 would just be an observation (and not a particularly useful one). Lowercase tends to be the default mode in several places.
NAME it wasn't at all clear that's what you were suggesting. I'll make an attempt.
Looking at it, I think I prefer referring to 4648. This this addresses the original issue, I'll merge; if someone wants to propose an alternate way to specify this, please feel free to open a PR.
As currently defined, the two serialized DisplayStrings %"foo\"bla%22bar" and %"foo%22bla\"bar" are semantically identical. We should either use %-escapes or \-escapes, mixing them this way is asking for smuggling-attacks.
I think probably -escapes; will work up a PR.
I don't believe that relying on a different RFC for the hex encoding makes this much better (so I sort of disagree with Martin's proposal). Just define the escaping exactly inline, and then we can choose upper/lower.

Revised text:
Given a string of Unicode characters as input_string, return an ASCII string suitable for use in an HTTP field value. <ins> If input_string is not a string of Unicode characters, fail parsing. </ins> Let byte_array be the result of applying UTF-8 encoding UTF8 to  <ins> input_string. </ins> Let encoded_string be an empty string. For each byte in byte_array:  <ins> If byte is %x25 ("%"), %x22 (DQUOTE), or in the ranges %x00-1f or %x7f-ff: </ins>  <ins> Append "%" to encoded_string. Let encoded_byte be the result of applying base16 encoding (Section 8 of RFC4648) to byte. Append encoded_byte to encoded_string. </ins> Otherwise, decode byte as an ASCII character and append the result to encoded_string.  <ins> Let output be a string containing "%" followed by DQUOTE. </ins>  <ins> Append encoded_string to output. Append DQUOTE to output. Return output. </ins> 4.2.