         name->transient? (atom {})
         clauses (.keys (.get-or-create-index kn "know" "clauses" #js ["rule-id" "when|know|remember|forget" "clause-id" "name"]))
         fields (.keys (.get-or-create-index kn "know" "clause-fields" #js ["clause-id" "constant|variable" "key" "val"]))
+        has-aggs (.keys (.get-or-create-index kn "know" "has-agg" #js ["rule-id"]))
+        group-bys (.keys (.get-or-create-index kn "know" "group-by" #js ["rule-id" "var"]))
+        agg-overs (.keys (.get-or-create-index kn "know" "agg-over" #js ["rule-id" "in-var" "agg-fun" "out-var"]))
         rule-id->clauses (atom (into {} (for [[k vs] (group-by #(nth % 0) clauses)] [k (set (map vec vs))])))
-        clause-id->fields (atom (into {} (for [[k vs] (group-by #(nth % 0) fields)] [k (set (map vec vs))])))]
+        clause-id->fields (atom (into {} (for [[k vs] (group-by #(nth % 0) fields)] [k (set (map vec vs))])))
+        rule-id->has-agg (atom (into {} (for [[k vs] (group-by #(nth % 0) has-aggs)] [k (set (map vec vs))])))
+        rule-id->group-by (atom (into {} (for [[k vs] (group-by #(nth % 0) group-bys)] [k (set (map vec vs))])))
+        rule-id->agg-over (atom (into {} (for [[k vs] (group-by #(nth % 0) agg-overs)] [k (set (map vec vs))])))
+        sink-of (fn [rule-id clause-type clause-id name var->ix]
+                  (let [fields (make-array (count var->ix))]
+                    (doseq [[_ field-type key val] (@clause-id->fields clause-id)]
+                      (assert (= field-type "variable") [rule-id clause-id key val])
+                      (aset fields (var->ix val) key))
+                    (Sink. clause-type name fields)))]
 
     ;; rewrite clauses
     (doseq [[rule-id clauses] @rule-id->clauses]

              ;; make solver
              solver (btree/solver num-vars (into-array constraints))

              ;; make output specs
              output-kinds (into-array
                            (for [[_ clause-type clause-id name] clauses
                                  :when (not= clause-type "when")]
                              clause-type))
              output-names (into-array
                            (for [[_ clause-type clause-id name] clauses
                                  :when (not= clause-type "when")]
                              name))
              output-fields (into-array
                             (for [[_ clause-type clause-id name] clauses
                                   :when (not= clause-type "when")]
                               (let [clause-fields (get @clause-id->fields clause-id)
                                     output-fields (make-array num-vars)]
                                 (doseq [[_ field-type key val] clause-fields]
                                   (assert (= field-type "variable") [rule-id clause-id clause-fields])
                                   (aset output-fields (var->ix val) key))
                                 output-fields)))]

          ;; set up state for outputs
          (dotimes [i (alength output-kinds)]
            (let [kind (aget output-kinds i)
                  name (aget output-names i)
                  fields (aget output-fields i)
                  filtered-fields (into-array (filter #(not (nil? %)) fields))
                  transient? (identical? kind "know")]
              ;; TODO get transient? from schema instead
              (assert (not= (not transient?) (get @name->transient? name)) name)
              (swap! name->transient? assoc name transient?)
              (swap! kind->name->rules update-in [kind name] #(or % #{}))
              (.ensure-index kn "know" name filtered-fields)
              (when (false? transient?)
                (.ensure-index kn "forget" name filtered-fields)
                (.ensure-index kn "remember" name filtered-fields))))

          (swap! rule->flow assoc rule-id (Flow. solver output-kinds output-names output-fields)))))

    ;; TODO stratify
    (Flows. (clj->js (map first @rule->flow)) (clj->js @rule->flow) #js {} (clj->js @kind->name->rules) (clj->js @name->transient?))))

(ns aurora.language
  (:require [aurora.btree :as btree])
  (:require-macros [aurora.macros :refer [apush aclear set!!]]))

;; KNOWLEDGE

