Please run the reproducible example provided.
When you do, you will see that write.csv writes an unnecessary empty
header field ("") over the row names column. This makes the number of
header fields equal to the number of columns _including_ row names. That
causes the original row names to be read as data by read.csv, following the
rule that the number of header fields determines whether row names are
present. read.csv accordingly assumes that the former row names are
unnamed data, calls the unnamed row names column "X" (or X.1 etc if X
exists) and then adds new, default, row names _instead of the original row
names written by write.csv_.
That's not helpful.