You were almost there:
For each matching line it selects the inner paragraph(without following newline) und joins.This works because, the command is only executed for the first line of each paragraph.
From the vim help about the global command:
The global commands work by first scanning through the lines and
marking each line where a match ...
Substitution with magic mode, so as to drop escaping ():
([^\n]) is any character except a newline in the 1st capture group.
\n is a newline.
([^\n]) is any character except a newline in the 2nd capture group.
For each such match, replace it by the 1st ...
Why it doesn't work
The reason your command works that way is that all the lines are marked for action first, then the join is run (aside: you can use :v/.../j to use the ex-command :join instead of :normal! J). Because the line numbers change, the execution gets off a bit.
Trying :vglobal with a smarter range
One idea is to join from each non-blank line to ...