                               (filter first)
                               (sort-by first)
                               (run! (fn [[[t1 t2] item]]
-                                      (let [past? (compare/< (.atZone (or t2 t1) CHICAGO) now)
+                                      (let [past? (:past? item)
                                             t1 (.toLocalTime t1)
                                             t2 (some-> t2 .toLocalTime)]
                                         (printf "      %s%s%s: %s %s\n"

                                                  (:header item))
                                                ;; once we figure out enough elisp to stop using links this
                                                ;; can just be implicit
                                                (make-org-link item "(link)")))))))]
    (with-atomic-write-to cfg
      (when preamble
        (println preamble))
      (when (seq deadlines)
        (println "== DEADLINES ==")
        (println "(TODO: show upcoming deadlines based on the -0d cookie)")
        (doseq [item (sort-by (juxt (comp - priority) :created-at :file :line-number) deadlines)]
          (print-todo-line item {}))
        (println))
      (when (seq triage)
        (println "== TRIAGE ==")
        (doseq [item (sort-by (juxt (comp - priority) :created-at :file :line-number) triage)]
          (print-todo-line item {}))
        (println))
      (let [stats-by-date
            (into {}
                  (for [[date {:keys [todos calendar-events]}] (sort by-day)
                        :let [[total-cal-time count-without-duration] (summarize-calendar-events calendar-events)]]
                    [date
                     {:unefforted-count (->> todos
                                             (remove :effort)
                                             ;; TODO: this is calculated in two places; either make a
                                             ;; function or add a :needs-effort? attr to the map
                                             (remove #(= "t" (get (:properties %) "EFFORT_EXEMPT")))
                                             (count))
                      :total-effort (->> todos
                                         (keep :effort)
                                         (reduce #(.plus %1 %2) Duration/ZERO))
                      :total-cal-time total-cal-time
                      :count-without-duration count-without-duration}]))]
        (doseq [[date {:keys [todos calendar-events]}] (sort by-day)
                :when (compare/<= today date)
                :let [{:keys [unefforted-count total-effort total-cal-time count-without-duration]}
                      (stats-by-date date)]]
          (println (format "== %s =="
                           (if (= date today)
                             "TODAY"
                             (format-org-local-date date))))
          (when-not (.isZero total-effort)
            (println (format "  Total effort: %s" (format-effort total-effort))))
          (when-not (.isZero total-cal-time)
            (println (format "  Total calendar time: %s" (format-effort total-cal-time))))
          (when (pos? unefforted-count)
            (println (format "# (%d items with no effort estimate)" unefforted-count)))
          (when (pos? count-without-duration)
            (println (format "# (%d calendar items with no duration)" count-without-duration)))
          (print-calendar calendar-events)
          ;; TODO: we shouldn't be putting things in the calendar if they're
          ;; scheduled in the past...
          (->> todos
               (sort-by (juxt #(later-today-time % now) ;; put future things at the bottom
                              (comp - priority)
                              ;; move rep entries to the bottom for the future blocks, so that
                              ;; nonrepeating items stick out
                              (if (= date today) (constantly nil) :repeat?)
                              relevant-date
                              :file
                              :line-number))
               (run! (fn [item] (print-todo-line item {}))))
          (when (= date today)
            (println "\n\n--------------------------------------------------------------------------------")
            (println "|                                  future                                      |")
            (println "--------------------------------------------------------------------------------")
            (print-table ["date" "tot" "-e" "-d" "tot-e" "tot-d"]
                         (for [date (sort (keys by-day))
                               :when (compare/< today date)
                               :let [{:keys [unefforted-count total-effort total-cal-time count-without-duration]}
                                     (stats-by-date date)]]
                           {"date"  (format-org-local-date date)
                            "tot"   (format-effort (.plus total-effort total-cal-time))
                            "-e"    unefforted-count
                            "-d"    count-without-duration
                            "tot-e" (format-effort total-effort)
                            "tot-d" (format-effort total-cal-time)})))
          (println)))
      (println "\n\n--------------------------------------------------------------------------------")
      (println "|                                   past                                       |")
      (println "--------------------------------------------------------------------------------")
      (doseq [[date {:keys [calendar-events past-log]}] (reverse (sort by-day))
              :when (compare/<= date today)]
        (println)
        (println (format "== LOG: %s ==" (format-org-local-date date)))
        (print-calendar calendar-events)
        (doseq [item past-log]
          (print-todo-line item {:omit-effort? true}))))))

(defn all-agenda-data
  [directory now]
  (into {} (for [file (all-org-files directory)]

(defn do-once
  [{:keys [directory] :as cfg} now]
  (let [by-file (all-agenda-data directory now)]
    (write-to-agenda-file (calculate-agenda by-file cfg now) cfg)))

(defn watch
  "cfg:

  :directory        the directory containing org files
  :agenda-file      the file the agenda will be written to
  :reset-all-file   a file which, when touched, causes *all* org files
                    to be reloaded
  :refresh-file     a file which, when touched, triggers a regeneration
                    of the agenda
  :callback         (optional) function of the agenda data that is called
                    each time the agenda changes"
  [{:keys [directory reset-all-file callback] :as cfg}]
  (let [q (LinkedBlockingQueue.)]
    (run! #(.add q %) (all-org-files directory))
    (let [do-refresh-all (fn []
