diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,10 +1,20 @@
 Changelog
 =========
 
+Version 0.2.2.0
+---------------
+
+*January 7, 2020*
+
+<https://github.com/mstksg/conduino/releases/tag/v0.2.2.0>
+
+*   Added `feedbackEither` for more flexibility on top of `feedback`
+*   Some documentation cleanup
+
 Version 0.2.1.0
 ---------------
 
-*October 30, 2019*
+*January 7, 2020*
 
 <https://github.com/mstksg/conduino/releases/tag/v0.2.1.0>
 
diff --git a/conduino.cabal b/conduino.cabal
--- a/conduino.cabal
+++ b/conduino.cabal
@@ -4,10 +4,10 @@
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: 52a1f0db85b8a281a18d41d489266853a6510e8e6ea76c959b68bf3cbb5ac0ce
+-- hash: 94a4f8415936756af3ddab6ae4a237db4a3f8ee9d4b93ce27fe211cceca6761b
 
 name:           conduino
-version:        0.2.1.0
+version:        0.2.2.0
 synopsis:       Lightweight composable continuation-based stream processors
 description:    A lightweight continuation-based stream processing library.
                 .
diff --git a/src/Data/Conduino.hs b/src/Data/Conduino.hs
--- a/src/Data/Conduino.hs
+++ b/src/Data/Conduino.hs
@@ -72,7 +72,7 @@
   -- * Pipe transformers
   , mapInput, mapOutput, mapUpRes, trimapPipe
   , hoistPipe
-  , feedbackPipe
+  , feedbackPipe, feedbackPipeEither
   -- * Wrappers
   , ZipSource(..)
   , unconsZipSource
@@ -219,7 +219,7 @@
 -- @since 0.2.1.0
 feedPipe
     :: Monad m
-    => [i]
+    => [i]                      -- ^ input to feed in
     -> Pipe i o u m a
     -> m ([o], Either (i -> Pipe i o u m a) ([i], a))
 feedPipe xs = (fmap . second . first) (. Right)
@@ -234,7 +234,7 @@
 -- @since 0.2.1.0
 feedPipeEither
     :: Monad m
-    => [i]
+    => [i]                      -- ^ input to feed in
     -> Pipe i o u m a
     -> m ([o], Either (Either u i -> Pipe i o u m a) ([i], a))
 feedPipeEither xs p = do
@@ -392,7 +392,22 @@
     :: Monad m
     => Pipe x x u m a
     -> Pipe x x u m a
-feedbackPipe p = fmap fst . runStateP Seq.empty $
+feedbackPipe = feedbackPipeEither . mapInput (either id id)
+
+-- | A version of 'feedbackPipe' that distinguishes upstream input from
+-- downstream output fed back.  Gets 'Left' from upstream, and 'Right' from
+-- its own output.
+--
+-- *  Will feed all output back to the input
+-- *  Will only ask for input upstream if output is stalled.
+-- *  Yields all outputted values downstream, effectively duplicating them.
+--
+-- @since 0.2.2.0
+feedbackPipeEither
+    :: Monad m
+    => Pipe (Either i o) o u m a
+    -> Pipe i o u m a
+feedbackPipeEither p = fmap fst . runStateP Seq.empty $
        popper
     .| hoistPipe lift p
     .| awaitForever (\x -> lift (modify (:|> x)) *> yield x)
@@ -400,10 +415,10 @@
     popper = lift get >>= \case
       Empty -> awaitEither >>= \case
         Left r  -> pure r
-        Right x -> yield x >> popper
+        Right x -> yield (Left x) >> popper
       x :<| xs -> do
         lift $ put xs
-        yield x
+        yield (Right x)
         popper
 
 -- | A newtype wrapper over a source (@'Pipe' () o 'Void'@) that gives it an
diff --git a/src/Data/Conduino/Internal.hs b/src/Data/Conduino/Internal.hs
--- a/src/Data/Conduino/Internal.hs
+++ b/src/Data/Conduino/Internal.hs
@@ -291,6 +291,8 @@
 --
 -- Note to avoid the usage of 'void', 'Data.Conduino.Lift.evalStateP' might
 -- be more useful.
+--
+-- @since 0.2.1.0
 runStateP
     :: Monad m
     => s
diff --git a/src/Data/Conduino/Lift.hs b/src/Data/Conduino/Lift.hs
--- a/src/Data/Conduino/Lift.hs
+++ b/src/Data/Conduino/Lift.hs
@@ -108,6 +108,8 @@
 -- the @s@ that it gets when it terminates.  If any other pipe in this
 -- chain modifies or uses state, all modifications will be overwritten when
 -- the @(a, s)@-producing pipe terminates.
+--
+-- @since 0.2.1.0
 stateP
     :: Monad m
     => (s -> Pipe i o u m (a, s))
@@ -119,6 +121,8 @@
 
 -- | Like 'runStateP', but ignoring the final result.  It returns the final
 -- state after the pipe succesfuly terminates.
+--
+-- @since 0.2.1.0
 execStateP
     :: Monad m
     => s
@@ -134,6 +138,8 @@
 -- This can be cleaner than 'runStateP' because if @a@ is @()@, you
 -- don't have to sprinkle in 'void' everywhere.  However, it's only really
 -- useful if you don't need to get the final state upon termination.
+--
+-- @since 0.2.1.0
 evalStateP
     :: Monad m
     => s
@@ -142,6 +148,8 @@
 evalStateP s = fmap fst . runStateP s
 
 -- | 'stateP', but for "Control.Monad.Trans.State.Strict".
+--
+-- @since 0.2.1.0
 statePS
     :: Monad m
     => (s -> Pipe i o u m (a, s))
@@ -152,6 +160,8 @@
     x <$ lift (SS.put s')
 
 -- | 'runStateP', but for "Control.Monad.Trans.State.Strict".
+--
+-- @since 0.2.1.0
 runStatePS
     :: Monad m
     => s
@@ -165,6 +175,8 @@
         Free l -> Free $ go s' <$> l
 
 -- | 'execStateP', but for "Control.Monad.Trans.State.Strict".
+--
+-- @since 0.2.1.0
 execStatePS
     :: Monad m
     => s
@@ -173,6 +185,8 @@
 execStatePS s = fmap snd . runStatePS s
 
 -- | 'evalStateP', but for "Control.Monad.Trans.State.Strict".
+--
+-- @since 0.2.1.0
 evalStatePS
     :: Monad m
     => s
@@ -186,6 +200,8 @@
 -- "succesfully" terminates with 'Left'.  It would never happen before the
 -- pipe terminates, since you don't get the @'Either' e a@ until the pipe
 -- succesfully terminates.
+--
+-- @since 0.2.1.0
 exceptP
     :: Monad m
     => Pipe i o u m (Either e a)
@@ -237,6 +253,8 @@
 -- 'ExceptT' internally.
 --
 -- Note to avoid the usage of 'void', 'runExceptP_' might be more useful.
+--
+-- @since 0.2.1.0
 runExceptP
     :: Monad m
     => Pipe i o u (ExceptT e m) a
@@ -251,6 +269,8 @@
 -- | A handy version of 'runExceptP' that discards its output, so it can be
 -- easier to chain using '.|'.  It's useful if you are using 'runExceptP'
 -- to "isolate" failures from the rest of a chain.
+--
+-- @since 0.2.1.0
 runExceptP_
     :: Monad m
     => Pipe i o u (ExceptT e m) a
@@ -265,6 +285,8 @@
 -- "succesfully" terminates with 'Left'.  It would never happen before the
 -- pipe terminates, since you don't get the @'Either' 'SomeException' a@
 -- until the pipe succesfully terminates.
+--
+-- @since 0.2.1.0
 catchP
     :: Monad m
     => Pipe i o u m (Either SomeException a)
@@ -276,6 +298,8 @@
 -- | Like 'runExceptP', but for 'CatchT'.  See 'runExceptP' for usage
 -- details.  In general, can be useful for "isolating" a 'CatchT' pipe from
 -- the rest of its chain.
+--
+-- @since 0.2.1.0
 runCatchP
     :: Monad m
     => Pipe i o u (CatchT m) a
@@ -292,6 +316,8 @@
 --
 -- Essentially, instead of directly providing the @r@ in an @r -> 'Pipe'
 -- i o u m a@, the @r@ instead comes from the globally shared environment.
+--
+-- @since 0.2.1.0
 readerP
     :: Monad m
     => (r -> Pipe i o u m a)
@@ -304,6 +330,8 @@
 --
 -- It can be useful to "ignore" a globally shared environment and just give
 -- the @r@ directly and immediately.
+--
+-- @since 0.2.1.0
 runReaderP
     :: Monad m
     => r
@@ -323,6 +351,8 @@
 -- logging (logging things as you get them), you should probably just
 -- directly use 'WriterT' instead, with 'Data.Conduino.Combinators.repeatM'
 -- or 'Data.Conduino.Combinators.iterM' or something similar.
+--
+-- @since 0.2.1.0
 writerP
     :: (Monad m, Monoid w)
     => Pipe i o u m (a, w)
@@ -372,6 +402,8 @@
 --
 -- And @a@ and @b@ will be none the wiser to the fact that @q@ uses
 -- 'WriterT' internally.
+--
+-- @since 0.2.1.0
 runWriterP
     :: (Monad m, Monoid w)
     => Pipe i o u (WriterT w m) a
@@ -385,6 +417,8 @@
 
 -- | 'runWriterP', but only returning the final log after succesful
 -- termination.
+--
+-- @since 0.2.1.0
 execWriterP
     :: (Monad m, Monoid w)
     => Pipe i o u (WriterT w m) a
@@ -392,6 +426,8 @@
 execWriterP = fmap snd . runWriterP
 
 -- | 'writerP', but for "Control.Monad.Trans.Writer.Strict".
+--
+-- @since 0.2.1.0
 writerPS
     :: (Monad m, Monoid w)
     => Pipe i o u m (a, w)
@@ -401,6 +437,8 @@
     x <$ lift (WS.tell w)
 
 -- | 'runWriterP', but for "Control.Monad.Trans.Writer.Strict".
+--
+-- @since 0.2.1.0
 runWriterPS
     :: (Monad m, Monoid w)
     => Pipe i o u (WS.WriterT w m) a
@@ -413,6 +451,8 @@
         Free l -> Free $ go w' <$> l
 
 -- | 'execWriterP', but for "Control.Monad.Trans.Writer.Strict".
+--
+-- @since 0.2.1.0
 execWriterPS
     :: (Monad m, Monoid w)
     => Pipe i o u (WriterT w m) a
@@ -431,6 +471,8 @@
 -- *  When the @(a,s,w)@-returning pipe terminates, whatever state in the
 --    'RWST' is overwritten with the @s@ returned.  If other pipes in the
 --    chain modify the @s@, their modifications will be overwritten.
+--
+-- @since 0.2.1.0
 rwsP
     :: (Monad m, Monoid w)
     => (r -> s -> Pipe i o u m (a, s, w))
@@ -445,6 +487,8 @@
 -- | Turn a 'Pipe' that runs over 'RWST' into a state-modifying,
 -- environment-using, log-accumulating 'Pipe'.  See 'runStateP',
 -- 'runWriterP', and 'runReaderP' for the uses and semantics.
+--
+-- @since 0.2.1.0
 runRWSP
     :: (Monad m, Monoid w)
     => r
@@ -459,6 +503,8 @@
         Free l -> Free $ go w' s' <$> l
 
 -- | 'runRWSP', but ignoring the final state.
+--
+-- @since 0.2.1.0
 evalRWSP
     :: (Monad m, Monoid w)
     => r
@@ -468,6 +514,8 @@
 evalRWSP r s = fmap (\(x,_,w) -> (x,w)) . runRWSP r s
 
 -- | 'runRWSP', but ignoring the result value.
+--
+-- @since 0.2.1.0
 execRWSP
     :: (Monad m, Monoid w)
     => r
@@ -477,6 +525,8 @@
 execRWSP r s = fmap (\(_,s',w) -> (s',w)) . runRWSP r s
 
 -- | 'rwsP', but for "Control.Monad.Trans.RWS.Strict".
+--
+-- @since 0.2.1.0
 rwsPS
     :: (Monad m, Monoid w)
     => (r -> s -> Pipe i o u m (a, s, w))
@@ -489,6 +539,8 @@
     x <$ lift (RWSS.put s')
 
 -- | 'runRWSPS', but for "Control.Monad.Trans.RWS.Strict".
+--
+-- @since 0.2.1.0
 runRWSPS
     :: (Monad m, Monoid w)
     => r
@@ -503,6 +555,8 @@
         Free l -> Free $ go w' s' <$> l
 
 -- | 'evalRWSPS', but for "Control.Monad.Trans.RWS.Strict".
+--
+-- @since 0.2.1.0
 evalRWSPS
     :: (Monad m, Monoid w)
     => r
@@ -512,6 +566,8 @@
 evalRWSPS r s = fmap (\(x,_,w) -> (x,w)) . runRWSPS r s
 
 -- | 'execRWSPS', but for "Control.Monad.Trans.RWS.Strict".
+--
+-- @since 0.2.1.0
 execRWSPS
     :: (Monad m, Monoid w)
     => r
