anchor.diff:      (bt:destroy-thread *chat-resource-thread*))
     (setf *websocket-thread* nil
           *chat-resource-thread* nil))
  -
  -
  -;;; User actions
  -;; (defstruct (user-action (:constructor make-user-action (user action dialogue
  -;;                                                         &optional (timestamp (get-universal-time)))))
  -;;   user timestamp action dialogue)
  -
  -;; (defun render-user-action-to-json (user-action)
  -;;   (let ((action (user-action-action user-action))
  -;;         (dialogue (user-action-dialogue user-action))
  -;;         (user (user-action-user user-action)))
  -;;     (jsown:to-json
  -;;      `("user-action"
  -;;        (:obj ("action" . ,action)
  -;;              ("character" . ,user)
  -;;              ("dialogue" . ,dialogue))))))
  -
  -
  -;; (defun send-user-action (client user-action)
  -;;   (when-let ((scene-id (session-value 'scene-id (client-session client))))
  -;;     (save-user-action scene-id user-action))
  -;;   (client-write client (render-user-action-to-json user-action)))
  -
  -;; (defun broadcast-user-action (res action)
  -;;   (maphash-values (rcurry #'send-user-action action)
  -;;                   (slot-value res 'clients)))
  
positive.code:            (lambda ,lambda-list
               ,@body))))
  
  (defun process-client-message (res client raw-message)
    (logit "Client message: ~A" raw-message)
    (when-let (message (handler-case (jsown:parse raw-message)
                         (error (e) (logit "Error while parsing client message '~A': ~A" raw-message e))))
  
negative.code:     (logit "Client successfully validated: ~S" client)
      client))
  
  ;;;
  ;;; Client messages
  ;;;
  