    (fn[v]
      (cond
        (isa? (type v) clojure.lang.Atom) (recur (deref v))
+       (isa? (type v) clojure.core.async.impl.channels.ManyToManyChannel) :chan
        (coll? v) (valuefy v)
        (not (future? v)) v
        (future-done? v) (deref v)

(defn job-exists? [jobname]
  (@job-configs jobname))

(defn validate-job [exp cmd work-item]
  (let [jobname (str (name exp) "-" work-item "-job-template")]
    (if (job-exists? jobname)
      ""
      (format "No job for experiment type '%s' with cmd '%s' for '%s'"
              (name exp) cmd work-item))))

(defn get-jobinfo [jobname]
  (assert (@job-configs jobname)

          (srv/send-msg ws {:op :stop :payload {}} :noenvelope true))))))


(defmethod user-msg :check [msg]
  (let [params (msg :params)
        {:keys [ws user eid]} params]
    (infof "CHECK: %s" params)
    (let [vmsg (vds/validate-expexists eid)
          vmsg (if (empty? vmsg) (va/validate-sheets-exist eid) vmsg)]
      (if (not-empty vmsg)
        (send-end-msg ws {:op :validate :payload vmsg})
        (let [_ (cmn/set-exp eid)
              vmsg (va/validate-exp eid "all")]
          (if (not-empty vmsg)
            (send-end-msg ws {:op :validate :payload vmsg})
            (send-end-msg ws {:op :validate :payload "All checks pass!"})))))))


(defn msg-handler [msg]
  (let [{:keys [ws data]} msg
        {:keys [op data]} data
        {:keys [user cmd phase modifier compfile eid action]} data
        eid (if (str/ends-with? eid "/") (austr/butlast 1 eid) eid)
        eid (if (str/starts-with? eid "/") (austr/drop 1 eid) eid)
        params {:user user, :cmd op, :eid eid
                :phase phase, :action action :compfile compfile
                :modifier modifier, :ws ws}]
    (infof "MSG-HANDLER: MSG %s" (msg :data))
    (try
      (case op
