                              ::scheduling-time scheduling-time
                              ::actual-next actual-next
                              ::now now
-                             ::success success})))
+                             ::success success
+                             ::trigger-details prelog})))
         [tag
          log-state] (try [(first success) log-state]
                          (catch IllegalArgumentException ex

        (case tag
          ::specs/child-> (let [[_ callback ack] success]
                            (partial trigger-from-child io-handle callback ack))
          ::drained (do (throw (RuntimeException. "Start back here, after I've checked handshake test"))
                        (log/warn prelog
                                  ;; Actually, this seems like a strong argument for
                                  ;; having a pair of streams. Child could still have
                                  ;; bytes to send to the parent after the latter's
                                  ;; stopped sending, or vice versa.
                                  ;; I'm pretty sure the complexity I haven't finished
                                  ;; translating stems from that case.
                                  ;; TODO: Another piece to revisit once the basics
                                  ;; work.
                                  "Stream closed. Surely there's more to do")
                        (constantly nil))
          ::parent-> (partial trigger-from-parent
                              io-handle
                              (second success))
          ::no-op identity
          ;; This can throw off the timer, since we're basic the delay on
          ;; the delta from recent (which doesn't change) rather than now.
          ;; But we're basing the actual delay from now, which does change.
          ;; e.g. If the scheduled delay is 980 ms, and someone triggers a

          ;; still be 980 ms rather than the 940 that would have been
          ;; appropriate.
          ;; Q: What's the best way to avoid this?
          ;; Updating recent seems obvious, but also dubious.
          ;; Decrementing the delay seems like something the scheduler
          ;; should handle.
          ::query-state (fn [state]
                          (if-let [dst (second success)]
                            (do
                              (deliver dst state)
                              state)
                            (update state
                                    ::log2/state
                                    #(log2/warn %
                                                ::action-trigger
                                                "state-query request missing required deferred"
                                                {::trigger-details prelog}))))
          ::timed-out (fn [state]
                        (trigger-from-timer io-handle
                                            (update state
                                                    ::log2/state
                                                    #(log2/debug %
                                                                 "Re-triggering Output due to timeot"
                                                                 (assoc timing-details
                                                                        ::trigger-details prelog))))))
        state (update state
                        ::log2/state
                        #(log2/debug %
                                     ::action-trigger
                                     "Processing event"
                                     {::tag tag}))
          ;; At the end of the main ioloop in the reference
          ;; implementation, there's a block that closes the pipe
          ;; to the child if we're done.
          ;; I think the point is that we'll quit polling on
          ;; that and start short-circuiting out of the blocks
          ;; that might do the send, once we've hit EOF
          ;; Q: What can I do here to
          ;; produce the same effect?
          ;; TODO: Worry about that once the basic idea works.

          ;; If the child sent a big batch of data to go out
          ;; all at once, don't waste time setting up a timeout
          ;; scheduler. The poll in the original would have
          ;; returned immediately anyway.
          ;; Except that n-sec-per-block puts a hard limit on how
          ;; fast we can send.
          start (System/currentTimeMillis)
          ;; TODO: Break these pieces into something
          ;; like the interceptor-chain idea. They should
          ;; return a value that includes a key for a
          ;; seq of functions to run to perform the
          ;; side-effects.
          ;; I'd still have to call updater, get
          ;; that updating seq, and update the state
          ;; to recurse.

          ;; I'd prefer to do these next two
          ;; pieces in a single step.

          ;; TODO: Read up on Executors. I could wind up
          ;; with really nasty interactions now that I
          ;; don't have an agent to keep this single-
          ;; threaded.
          ;; Actually, it should be safe as written.
          ;; Just be sure to keep everything synchronized
          ;; around takes from the i/o handle. (Not
          ;; needing to do that manually is
          ;; a great reason to not introduce a second
          ;; one for bytes travelling the other direction)

          state' (try (updater state)
                      (catch ExceptionInfo ex
                        (update state
                                ::log2/state
                                #(log2/exception %
                                                 ex
                                                 ::action-trigger
                                                 "Running updater failed"
                                                 {::details (.getData ex)})))
                      (catch RuntimeException ex
                        ;; The eternal question in this scenario:
                        ;; Fail fast, or hope we can keep limping
                        ;; along?
                        ;; TODO: Add prod vs. dev environment options
                        ;; to give the caller control over what
                        ;; should happen here.
                        ;; (Note that, either way, it really should
                        ;; include a callback to some
                        ;; currently-undefined status updater
                        (comment state)
                        (update state
                                ::log2/state
                                #(log2/exception %
                                                 ex
                                                 ::action-trigger
                                                 "Running updater: low-level failure"))))
        _ (assert (::specs/outgoing state') (str "After updating for " tag))
        [my-logs forked-logs] (log2/fork (::log2/state state') ::schedule-next-timeout!)
        mid (System/currentTimeMillis)
        ;; This is taking a ludicrous amount of time.
        ;; Q: How much should I blame on logging?
        _ (schedule-next-timeout! io-handle (assoc state'
                                                   ::log2/state
                                                   forked-logs))
        end (System/currentTimeMillis)]
    (log2/flush-logs! (::log2/logger io-handle)
                      (log2/debug  my-logs
                                   ::action-trigger
                                   "Handled a triggered action"
                                   {::tag tag
                                    ::handling-ms (- mid start)
                                    ::rescheduling-ms (- end mid)})))
  nil)

(comment
  (let [delta_f ##Inf,
