The standard specifies that ++x is equivalent to x+=1, and that the value of an assignment expression like x+=1 is "the value of the left operand after the assignment" (and that "the type of an assignment expression is the type of the left operand").
So, since x is of type char, the expression ++x has type char and its value is the value of x after the assignment, so it must be in the range CHAR_MIN to CHAR_MAX.
You miss that ++x is not an "x" it's an expression for which the op also writes "The usual arithmetic conversions ensure that two operands to a '+' operator of type signed char are both promoted to signed int before the addition is performed" and I believe that that can be found in the text of the standard too. The underlying idea is to allow most of the run-time calculations to be performed with the "native" most efficient type, which is int. I still claim it's implementation dependent and allowable to evaluate to "128 > 127". I won't discuss this more, I accept that you have a different opinion.
The usual arithmetic conversions do cause the x and the 1 in the implicit x+=1 to be promoted to int before the addition, and the addition is performed in an int. However, since the lvalue that is the left hand operand of the assignment has type char, the result is converted back to char (which, if char is signed and the result is not in the range of char gives an implementation-defined result) and stored in the object designated by the lvalue. The result of the expression is this converted char value.
The important point here is that the result of an assignment expression (including pre- and post- increment and decrement) is the value that was stored in the object that was assigned to.
You can readily check this by examining the value of sizeof ++x (where x has type char).