         current-pr (when (= (get components 3) "pull") {:repo repo :pr pr})]
     (swap! state assoc :current-pr current-pr)))
 
-(defn ^:export watch []
+(defn ^:export watch
+  "Run `main` once, then watch for DOM mutations in the PJAX container and run `main` when
+  it changes"
+  []
   (let [is-valid-mutation? #(and (= (.-type %) "childList") (not-empty (.-addedNodes %)))
         observer (js/MutationObserver. #(when (some is-valid-mutation? %) (main)))]
     (.observe observer pjax-wrapper #js{:childList true :attributes false :characterData false})

(defn- is? [type] (fn [item] (= (:type item) type)))
(defn- child-text [parent selector] (.-textContent (.querySelector parent selector)))

(defn- set-text! [selector text]
  (set! (.-textContent (js/document.querySelector selector)) text))

(defn- commit-sha [element]
  (last (string/split (.getAttribute (.querySelector element ".commit-id") "href") "/")))

(defn- commit-shas [item]
  (if-let [element (:element item)]
    (map commit-sha (.querySelectorAll (:element item) ".commit"))
    []))

(defn- item-type [creator item]
  (let [classes (.-classList item)]
    (cond (.contains classes "discussion-commits") "commit-block"
          (.contains classes "discussion-item-assigned") "assigned"

  (fn [index element]
    {:type (item-type creator element) :element element :index index}))

(defn- commit-info
  "On a pull request, get all of the discussion items and their types, returning a map of
  the last reviewed commit and the commits since"
  []
  (let [elements (js/document.querySelectorAll ".js-discussion .timeline-comment-wrapper, .js-discussion .discussion-item")
        creator (child-text (.item elements 0) ".author")
        items (map-indexed (annotated-element creator) elements)
