How about allowing characters to encode surrogates, but signalling an
error if a surrogate is passed to string-set!

This would allow you to do what you want with characters, would allow
you to deal to some degree with poorly formed strings that are imported
into Scheme, but would not allow you to construct poorly-formed strings
within Scheme.