Is unescaping of \; in foreach() intentional or not?

The behavior is as expected. The lists documentation says:

The sequence \; does not divide a value but is replaced by ; in the resulting element.

The original \; in your string() call encodes \; because the escape sequence documentation says:

A \; outside of any Variable References encodes itself

Historically this evolved in the very early days when someone said “I need to escape a ; for this specific use case” and implemented the \; behavior without considering the semantics in general. Due to compatibility, it cannot be changed now. A policy for this would have a huge runtime cost.