You're right that looking at traces is likely to be a dead end here. However, I think there is a better way.
The path corresponding to each piece consists of a bunch of located trails. For each piece, one of those trails must be the one that traces completely around the outside; you can extract that one trail, stroke it with a white fill, and place it behind the piece. Unfortunately, after playing around with it a bit, there does not seem to be any rhyme or reason to *which* trail is the outer one for each piece. You could probably figure it out computationally by e.g. picking the trail with the biggest minimum and maximum x or y-values over all its vertices, but honestly, if it were me I might just play with it and manually figure out which index corresponds to the outer trail for each piece.
Here is a (hastily thrown together) modification of your code that assumes the trail at index 1 is always the outside one. This is false in general, but it happens to be true for rooks (and I think also pawns?), and you can see that it does work for rooks in the attached image. Hope this helps!
renderPiece :: PreparedFont Double -> Piece -> Diagram B
renderPiece font p = strokeP path # fc black # lw 0 <> outline # fc white # lw none
path = textSVG' opts [ch]
outline = strokeLocTrail (pathTrails path !! 1)
opts = TextOpts font INSIDE_H KERN False 1 70
ch = pieceToFontChar p