 
     ; Root is a partition node.
     (= part/data-type (:data/type root))
-      (some->>
-        (part/update-root! store params root changes)
-        (from-partitions store params))
+      (let [result (part/update-partitions! store params [[root changes]])]
+        (if (neg? (first result))
+          (part/from-records store params (second result))
+          (from-partitions store params (second result))))
 
     ; Root is an index node.
     (= data-type (:data/type root))
-      (letfn [(store!  ; TODO: very similar to `store-index!`
+      (letfn [(store! ; TODO: delay storage like this?
                 [node]
                 (if (mdag/link? node)
                   node


;; ## Construction

(defn- store-index!
  "Recursively store index nodes from the given sequence of children. Loads
  each child to calculate the aggregate first-key, last-key, and count
  attributes. Returns persisted index node data."
  [store height children]
  (loop [record-count 0
         new-children []
         child-keys []
         last-last-key nil
         children children]
    (if (seq children)
      ; Load and update aggregate stats from next child.
      (let [child (first children)
            link-name (format "%03d" (count new-children))
            data (if (mdag/link? child)
                   (graph/get-link! store child)
                   child)
            target (cond
                     (mdag/link? child) child
                     (= 1 height) (meta child)
                     :else (meta (store-index! store (dec height) (::children data))))
            link (mdag/link link-name target)]
        (recur (int (+ record-count (::record/count data)))
               (conj new-children link)
               (conj child-keys (::record/first-key data))
               (::record/last-key data)
               (next children)))
      ; No more children, serialize node.
      (->>
        {:data/type data-type
         ::height height
         ::keys (vec (rest child-keys))
         ::children new-children
         ::record/count record-count
         ::record/first-key (first child-keys)
         ::record/last-key last-last-key}
        (mdag/store-node! store nil)
        (::node/data)))))


(defn from-partitions


;; ## Update Functions

(declare update-index-node!)

(defn- update-index-children!
  "Apply changes to a sequence of intermediate index nodes and redistribute the
  results to create a new sequence of valid indexes. Each input tuple should
  have the form `[index changes]`, and `carry` may be a forward-carried result
  vector.

  Returns a tuple containing the resulting height and a sequence of updated and
  valid (but unpersisted) index nodes at that height."
  [store params height carry inputs]
  (loop [outputs []
         carry carry
         inputs inputs]
    (if (seq inputs)
      ; Process next input node
      (let [[child changes] (first inputs)
            [rheight elements :as result]
              (update-index-node! store params carry child changes)]
        (cond
          ; Result is empty.
          (nil? result)
            (recur outputs nil (next inputs))
          ; Result elements are output-level nodes.
          (= rheight height)
            (recur (into outputs elements) nil (next inputs))
          ; Result elements are a carry.
          :else
            (recur outputs result (next inputs))))

      ; No more input nodes.
      (if (seq outputs)
        (let [outputs (if carry
                        (if (zero? height)
                          (part/carry-back store params outputs carry)
                          ('carry-back store params outputs carry))
                        outputs)]
          (if (<= ('min-limit params) (count outputs))
            ; Build one or more valid index nodes from the outputs.
            [height (mapv index-children ('split-limited ('max-limit params) outputs))]
            ; Not enough outputs to make a valid node - return for carrying.
            [(dec height) outputs]))
        ; No outputs, so return nil or direct carry.
        carry))))


(defn- update-index-node!
  [store params carry index changes]
  (if (and (nil? carry) (empty? changes))
    ; No carry or changes to apply, pass-through node.
    [(::height index) [index]]

    ; Divide up changes and apply to children.
    (let [child-inputs (map (fn [[child child-changes]]
                              [(if (mdag/link? child)
                                 (graph/get-link! store index child)
                                 child)
                               child-changes])
                            (assign-records index changes))
          child-height (dec (::height index))]
      (cond
        ; Update children as partitions.
        (zero? child-height)
          (part/update-partitions! store params carry child-inputs)
        ; Adopt carried subnodes.
        (and carry (= (first carry) child-height))
          (update-index-children!
            store params child-height
            nil (concat (map vector (second carry)) child-inputs))
        ; Carry forward into child updates.
        :else
          (update-index-children!
            store params child-height
            carry child-inputs)))))


(defn update-tree
