Mystery solved: the bug was in my Ropen code. I was not returning QTDIR in the qid.type when opening the directory.
I'd hazard a guess that because Rwalk said it was a directory, but then Ropen said it was a file, sysfile.c:read() in the Plan 9 kernel didn't call unionread(), so only the first directory in the union mount was read. Maybe??
Anyway it works now. :)