fix: validate slot mode on first defslot call

Add assert to reject invalid mode keywords on first registration
instead of silently storing them and only crashing later in
slot-render's ecase. Valid modes: :stack, :replace, :single-winner.
This commit is contained in:
Hermes Agent
2026-05-12 19:33:18 +00:00
parent 352f27e260
commit 9c879e7a97
2 changed files with 18 additions and 10 deletions

View File

@@ -93,8 +93,8 @@ the first call and frozen for subsequent calls:
The ~render-fn~ itself is returned so callers can use it inline. The ~render-fn~ itself is returned so callers can use it inline.
The mode parameter is accepted but only respected on the first The mode parameter is validated on first call via ~assert~ and then
registration for a slot. This prevents a later registration from frozen for subsequent calls. This prevents a later registration from
changing the slot's semantics out from under earlier registrations. changing the slot's semantics out from under earlier registrations.
#+BEGIN_SRC lisp :tangle ../src/components/slot.lisp #+BEGIN_SRC lisp :tangle ../src/components/slot.lisp
@@ -102,10 +102,14 @@ changing the slot's semantics out from under earlier registrations.
(let* ((key (string name)) (let* ((key (string name))
(slot (gethash key *slots*))) (slot (gethash key *slots*)))
(if (null slot) (if (null slot)
;; First registration — set mode and create entry ;; First registration — validate and set mode, create entry
(setf (gethash key *slots*) (progn
(list :mode mode (assert (member mode '(:stack :replace :single-winner)) ()
:entries (list (cons order render-fn)))) "Invalid slot mode: ~S (use :stack, :replace, or :single-winner)"
mode)
(setf (gethash key *slots*)
(list :mode mode
:entries (list (cons order render-fn)))))
;; Existing slot — respect frozen mode ;; Existing slot — respect frozen mode
(let ((entries (getf slot :entries))) (let ((entries (getf slot :entries)))
(ecase (getf slot :mode) (ecase (getf slot :mode)

View File

@@ -8,10 +8,14 @@ Each entry: (:mode <mode> :entries <(order . render-fn) list>).")
(let* ((key (string name)) (let* ((key (string name))
(slot (gethash key *slots*))) (slot (gethash key *slots*)))
(if (null slot) (if (null slot)
;; First registration — set mode and create entry ;; First registration — validate and set mode, create entry
(setf (gethash key *slots*) (progn
(list :mode mode (assert (member mode '(:stack :replace :single-winner)) ()
:entries (list (cons order render-fn)))) "Invalid slot mode: ~S (use :stack, :replace, or :single-winner)"
mode)
(setf (gethash key *slots*)
(list :mode mode
:entries (list (cons order render-fn)))))
;; Existing slot — respect frozen mode ;; Existing slot — respect frozen mode
(let ((entries (getf slot :entries))) (let ((entries (getf slot :entries)))
(ecase (getf slot :mode) (ecase (getf slot :mode)