Stopping Dovecot using launchctl
The trouble goes away if I uncomment the -F switch (which I commented while I was trying to get some other part of the set up to work). The -F switch runs Dovecot in the foreground and does not daemonize.

From this answer on SuperUser:


  Launchd expects the programs it launches not to daemonize themselves; if they do, launchd detects it as the program exiting, and cleans up all subprocesses (i.e. kills the now-daemonized program).


In this case it didn't kill the program (as I wouldn't expected if the switch was the wrong one), it just relinquished control so it wouldn't/couldn't shut it down.

Note to self, try the obvious thing, your assumptions may be wrong.