         (cl-sort (delete-dups packages) #'string-lessp :key #'car)))))
 
 (defun doom-cli--ci--lint (commits)
-  (let ((errors? 0)
-        (warnings? 0))
+  (let ((warnings 0)
+        (failures 0))
     (print! (start "Linting %d commits" (length commits)))
     (print-group!
-     (dolist (commit commits)
-       (let* ((plist (doom-cli--parse-commit (cdr commit)))
-              (subject (plist-get plist :subject))
-              warnings errors)
-         (unless (string-match-p "^\\(?:\\(?:fixup\\|squash\\)!\\|FIXUP\\|WIP\\) "
-                                 subject)
-           (dolist (fn doom-cli-commit-rules)
-             (pcase (apply fn plist)
-               (`(,type . ,msg)
-                (push msg (if (eq type 'error) errors warnings)))))
-           (if (and (null errors) (null warnings))
-               (print! (success "%s %s") (substring (car commit) 0 7) subject)
-             (print! (start "%s %s") (substring (car commit) 0 7) subject))
+     (pcase-dolist (`(,ref . ,commitmsg) commits)
+       (let* ((commit   (doom-cli--parse-commit commitmsg))
+              (shortref (substring ref 0 7))
+              (subject  (plist-get commit :subject)))
+         (letf! ((defun skip! (reason &rest args)
+                   (print! (warn "Skipped because: %s") (apply #'format reason args))
+                   (cl-return-from 'linter))
+                 (defun warn! (reason &rest args)
+                   (cl-incf warnings)
+                   (print! (warn "%s") (apply #'format reason args)))
+                 (defun fail! (reason &rest args)
+                   (cl-incf failures)
+                   (print! (error "%s") (apply #'format reason args))))
+           (print! (start "%s %s") shortref subject)
            (print-group!
-            (when errors
-              (cl-incf errors?)
-              (dolist (e (reverse errors))
-                (print! (error "%s" e))))
-            (when warnings
-              (cl-incf warnings?)
-              (dolist (e (reverse warnings))
-                (print! (warn "%s" e)))))))))
-    (when (> warnings? 0)
-      (print! (warn "Warnings: %d") warnings?))
-    (when (> errors? 0)
-      (print! (error "Failures: %d") errors?))
-    (if (not (or (> errors? 0) (> warnings? 0)))
-        (print! (success "There were no issues!"))
-      (terpri)
-      (print! "See https://docs.doomemacs.org/latest/#/developers/conventions/git-commits for details")
-      (when (> errors? 0)
-        (throw 'exit 1)))))
-
-(defun doom-cli--ci--read-commits ()
-  (let (commits)
-    (while (re-search-backward "^commit \\([a-z0-9]\\{40\\}\\)" nil t)
-      (push (cons (match-string 1)
-                  (replace-regexp-in-string
-                   "^    " ""
-                   (save-excursion
-                     (buffer-substring-no-properties
-                      (search-forward "\n\n")
-                      (if (re-search-forward "\ncommit \\([a-z0-9]\\{40\\}\\)" nil t)
-                          (match-beginning 0)
-                        (point-max))))))
-            commits))
-    commits))
+            (cl-block 'linter
+              (mapc (doom-rpartial #'apply commit)
+                    doom-cli-commit-rules)))))))
+    (let ((issues (+ warnings failures)))
+      (if (= issues 0)
+          (print! (success "There were no issues!"))
+        (if (> warnings 0) (print! (warn "Warnings: %d" warnings)))
+        (if (> failures 0) (print! (warn "Failures: %d" failures)))
+        (print! "\nSee https://docs.doomemacs.org/-/conventions/git-commits")
+        (unless (zerop failures)
+          (throw 'exit 1)))
+      t)))
 
 (defun doom-cli--ci-lint-commits (from &optional to)
   (with-temp-buffer
     (insert
      (cdr (doom-call-process
            "git" "log"
-           (format "%s..%s" from (or to "HEAD")))))
-    (doom-cli--ci--lint (doom-cli--ci--read-commits))))
+           (format "%s...%s" from (or to (concat from "~1"))))))
+    (doom-cli--ci--lint
+     (let (commits)
+       (while (re-search-backward "^commit \\([a-z0-9]\\{40\\}\\)" nil t)
+         (push (cons (match-string 1)
+                     (replace-regexp-in-string
+                      "^    " ""
+                      (save-excursion
+                        (buffer-substring-no-properties
+                         (search-forward "\n\n")
+                         (if (re-search-forward "\ncommit \\([a-z0-9]\\{40\\}\\)" nil t)
+                             (match-beginning 0)
+                           (point-max))))))
+               commits))
+       commits))))

  :summary
    (String) Contains the summary following the type and scopes. e.g. In
    'feat(org): fix X, Y, and Z' the summary is 'fix X, Y, and Z.
  :type
    (Symbol) The type of commit this is. E.g. `feat', `fix', `bump', etc.

Each function should return nothing if there was no error, otherwise return a
cons cell whose CAR is the type of incident as a symbol (one of `error' or
`warn') and whose CDR is an explanation (string) for the result.

Note: warnings are not considered failures.")


                (when (/= (length (match-string 1)) 12)
                  (push (match-string 0) refs)))
              (when refs
                (fail! "%d commit hash(s) not 12 characters long: %s"
                       (length refs) (string-join (nreverse refs) ", "))))))

        ;; TODO Add bump validations for revert: type.

        (fn! (&key body trailers)
          "Validate commit trailers."
          (let* ((keys   (mapcar #'car doom-cli-commit-trailer-keys))
                 (key-re (regexp-opt keys t))
                 (lines
                  ;; Scan BODY because invalid trailers won't be in TRAILERS.
                  (save-match-data
                    (and (string-match "\n\\(\n[a-zA-Z-]+:? [^ ][^\n]+\\)+\n*\\'" body)
                         (split-string (match-string 0 body) "\n" t)))))
            (dolist (line lines)
              (unless (string-match-p (concat "^" key-re ":? [^ ]") line)
                (fail! "Found %S, expected one of: %s"
                       (truncate-string-to-width (string-trim line) 16 nil nil "…")
                       (string-join keys ", ")))
              (when (and (string-match "^[^a-zA-Z-]+:? \\(.+\\)$" line)
                         (string-match-p " " (match-string 1 line)))
                (fail! "%S has multiple references, but should only have one per line"
                       (truncate-string-to-width (string-trim line) 20 nil nil "…")))
              (when (or (string-match (concat "^" key-re "\\(?:e?[sd]\\|ing\\)? [^ ]") line)
                        (string-match (concat "^\\([a-zA-Z-]+\\) [^ \n]+$") line))
                (fail! "%S missing colon after %S"
                       (truncate-string-to-width (string-trim line) 16 nil nil "…")
                       (match-string 1 line))))
            (pcase-dolist (`(,key . ,value) trailers)
              (if (string-match-p " " value)
                  (fail! "Found %S, but only one value allowed per trailer"
                         (truncate-string-to-width (concat key ": " value) 20 nil nil "…"))
                (when-let (allowed-types (cdr (assoc key doom-cli-commit-trailer-keys)))
                  (or (cl-loop for type in allowed-types
                               if (cdr (assq type doom-cli-commit-trailer-types))
                               if (string-match-p it value)
                               return t)
                      (fail! "%S expects one of %s, but got %S"
                             key allowed-types value)))))))

        ;; TODO Check that bump/revert SUBJECT list: 1) valid modules and 2)
        ;;      modules whose files are actually being touched.
