         [plan index-b] (add-flow plan (->Index nodes-b key-ixes-b))
         [plan lookup-a] (add-flow plan (->Lookup [index-a] index-b key-ixes-a val-ixes-a))
         [plan lookup-b] (add-flow plan (->Lookup [index-b] index-a key-ixes-b val-ixes-b))]
-    [plan [lookup-a lookup-b] (distinct (concat vars-a vars-b)) (union computes-applied-a computes-applied-b)]))
-
-(comment
+    [plan [lookup-a lookup-b] (distinct (concat vars-a vars-b))]))
+
+(comment
+
   (let [plan empty-flow-plan
-        [plan nodes-a vars-a] (recall->nodes plan (Recall. :known&pretended (->edge 'x 'y)))
-        [plan nodes-b vars-b] (recall->nodes plan (Recall. :known (->connected 'y 'z)))
-        [plan nodes-c vars-c computes-applied] (join->nodes plan nodes-a vars-a #{} nodes-b vars-b #{})]
+        [plan nodes-a vars-a] (add-clause plan nil nil (Recall. :known&pretended (->edge 'x 'y)))
+        [plan nodes-b vars-b] (add-clause plan nil nil (Recall. :known (->connected 'y 'z)))
+        [plan nodes-c vars-c] (join-clauses plan nodes-a vars-a #{} nodes-b vars-b #{})]
     [plan nodes-c vars-c])
   )
-
-;; RULES
-
-(defrecord Rule [clauses])
-
-;; Correctness: Each clause must appear at least once in the plan
-;; Heuristic: Each Recall clause is used at most once in the plan
-;; Heuristic: Each Filter/Let clause is used at most once per path in the plan
 
 (defn add-rule [plan rule]
   (let [recalls (filter #(= Recall (type %)) (:clauses rule))
         computes (set (filter #(= Compute (type %)) (:clauses rule)))
         outputs (filter #(= Output (type %)) (:clauses rule))
         main-plan (atom plan)
-        nodes&vars&computes-applied (for [recall recalls]
-                                      (let [plan @main-plan
-                                            [plan node vars] (recall->nodes plan recall)
-                                            [plan node vars computes-applied] (computes->nodes plan node vars computes)]
-                                        (reset! main-plan plan)
-                                        [node vars computes-applied]))
-        [nodes vars computes-applied] (reduce (fn [[nodes-a vars-a computes-applied-a] [nodes-b vars-b computes-applied-b]]
-                                               (let [plan @main-plan
-                                                     [plan nodes vars computes-applied-old] (join->nodes plan nodes-a vars-a computes-applied-a nodes-b vars-b computes-applied-b)
-                                                     [plan nodes vars computes-applied-new] (computes->nodes plan nodes vars (difference computes computes-applied-old))]
-                                                 (reset! main-plan plan)
-                                                 [nodes vars (union computes-applied-old computes-applied-new)]))
-                                              nodes&vars&computes-applied)]
-    (assert (= computes-applied (set computes)) (str "Could not apply " (pr-str (difference (set computes) computes-applied))))
+        nodes&vars (for [recall recalls]
+                     (let [[plan node vars] (add-computes (add-clause @main-plan nil nil recall) computes)]
+                       (reset! main-plan plan)
+                       [node vars]))
+        [nodes vars] (reduce (fn [[nodes-a vars-a] [nodes-b vars-b]]
+                               (let [computes-unapplied (difference computes
+                                                                    (filter #(add-clause @main-plan nodes-a vars-a %) computes)
+                                                                    (filter #(add-clause @main-plan nodes-b vars-b %) computes))
+                                     [plan nodes vars] (add-computes
+                                                        (join-clauses @main-plan nodes-a vars-a nodes-b vars-b)
+                                                        computes-unapplied)]
+                                 (reset! main-plan plan)
+                                 [nodes vars]))
+                             nodes&vars)
+        computes-unapplied (filter #(not (add-clause @main-plan nodes vars %)) computes)]
+    (assert (empty? computes-unapplied) (str "Could not apply " (pr-str computes-unapplied) " to " (pr-str vars)))
     (doseq [output outputs]
-      (reset! main-plan (output->nodes @main-plan nodes vars output)))
+      (let [[plan node vars] (add-clause @main-plan nodes vars output)]
+        (reset! main-plan plan)))
     @main-plan))
 
 (defn add-rules [plan rules]

                  (let [sink (make-array (alength var-ixes))]
                    (dotimes [i (alength var-ixes)]
                      (aset sink i (aget source (aget var-ixes i))))
                    (Fact. nil sink)))))))))))

(comment
  (deffact eg "[a] has a [b] with a [c]")

  (fixpoint! state-0)
  (for [state (take 5 (iterate #(tick&fixpoint plan %) state-0))]
    (count (get-facts state :known))))

  (let [rules [(Rule. [
                       (aurora.language.Recall. :known&pretended, (js/aurora.language.fact :http/response #js ['content "google" 'some]))
                       (aurora.language.Output. :remembered (js/aurora.language.fact "1df7454c_069e_40ab_b117_b8d43212b473" #js ['value74]))
                       (aurora.language.Output. :forgotten (js/aurora.language.fact "1df7454c_069e_40ab_b117_b8d43212b473" #js ['value]))
                       (aurora.language.Recall. :known&pretended, (js/aurora.language.fact "1df7454c_069e_40ab_b117_b8d43212b473" #js ['value]))
                       (aurora.language.Compute. (->Let 'value74  #{'value} "value + \"hey\""))])]
        plan (add-rules empty-flow-plan rules)
        state (flow-plan->flow-state plan)]
    (add-facts state :known [(fact. "1df7454c_069e_40ab_b117_b8d43212b473" #js ["Click me"])])
    (add-facts state :pretended [(fact. :http/response #js ["yo" "google" 1234])])
    (fixpoint! state)
    (-> state
        (get-facts :known)
        first
        (.-values))
  )
  )
