Upload and download failures were printed and then forgotten: a batch
that lost files still exited 0, and a file the client could not write
was listed as if it had arrived. Nothing downstream could tell.
- Return a verdict from every transfer worker and raise once at the
end, so a run that lost a file exits 2.
- Raise the error when a downloaded file cannot be written locally,
instead of printing it and reporting the path as a success.
- Write a download beside its destination and move it into place once
complete, and refuse a destination that cannot be written, so a
failed transfer no longer leaves a truncated file behind.
- Carry on through the remaining sub-folders when a recursive download
loses a file or cannot create a folder locally.
- Name the file and give the reason in every failure message.
- Count a failure the API layer did not raise, such as a file that
disappeared between the directory walk and its turn to be sent.
- Extract the directory walk from `Uploader.upload` to keep it within
the complexity limit.
- Cover all of the above in `tests/test_transfer.py`.