The closer goroutine ran asynchronously on ctx cancellation, so the
"fully closed when Relay returns" guarantee was racy: callers could see
the function return before a and b were actually Close()d. Wait on a
done channel in the defer so the guarantee holds.