Rewrite chkEncode to avoid the IORef, the weird repeatedly evaluated IO, the internal state machine
This encode should be a lot simpler. It doesn't need internal state, it just needs to evaluate pieces in the right order and spit out the results. It doesn't need a multi-shot IO. It can produce a list of IOs or an IO of a lazy ByteString if we want to maintain all of that laziness or we could give up some of it and just produce an IO of a regular ByteString. The laziness is nice but it is an optimization (and the whole library doesn't even work right yet).
A simpler interface will be easier to test and integrate with other pieces in the stack. A simpler interface will also imply (or reflect?) a simpler implementation which will be easier to reason about.
Some part of this work should result in an intermediate structured representation. Currently we go from flow plaintext though chkEncode and then ciphertext all the way through to an encoded share. There is a lot of metadata involved that could have a structured representation (instead of only an encoded representation) in the program. This should also allow testing using the intermediate representation (we should be able to encode to it and decode from it, and we should be able to encode it to the share bytes, and decode share bytes to it). Some of this exists in Tahoe.CHK.Decode but is only used by the decoder.