                {})
           ;; this ensures that the 'main' shutdown logic will be runnable,
           ;; and gives us a way to observe when it has completed.
-          app-main-thread (future (tk/run-app app))]
+          app-main-thread (future (tk/run-app app))
+          shutdown-service (tk-app/get-service app :ShutdownService)]
+
+      ;; no shutdown requested yet
+      (is (nil? (internal/get-shutdown-reason shutdown-service)))
 
       ;; now we trigger a restart, which will call 'stop' for the first time,
       ;; which will request a shutdown but will block on the stop-should-proceed

                      context)
               (stop [this context]
                     (swap! lifecycle-events conj :stop)
                     (request-shutdown)
                     (deliver first-stop-begun? true)
                     @stop-should-proceed?

      ;; wait until we know that the shutdown has been requested
      @first-stop-begun?

      ;; we want to give the main thread a little time in case it is going to
      ;; do anything nefarious (as it would have before we consolidated the
      ;; shutdown logic into the core.async worker loop)
      (Thread/sleep 100)

      ;; at this point, the app's shutdown-reason-promise should be set
      (is (not (nil? (internal/get-shutdown-reason shutdown-service))))

      ;; validate that the first three events are as expected (we're still blocked
      ;; in the first 'stop').
      (is (= [:init :start :stop] @lifecycle-events))

      ;; main thread should still be blocked
      (is (not (realized? app-main-thread)))
