fix: correct setf form in perceive gate HITL handler
Some checks failed
Deploy (Gitea) / deploy (push) Failing after 2s
Some checks failed
Deploy (Gitea) / deploy (push) Failing after 2s
(setf (getf signal :approved t)) → (setf (getf signal :approved) t) Caught during system compilation. This is exactly the class of bug that the REPL-first discipline would have caught instantly.
This commit is contained in:
@@ -205,21 +205,20 @@ For approval-required actions, creates a Flight Plan instead of executing."
|
||||
(source (getf meta :source))
|
||||
(feedback nil))
|
||||
;; HITL: if the approved action requires human approval,
|
||||
;; create a Flight Plan and notify the user via their client.
|
||||
;; create a Flight Plan (Emacs) and HITL entry (all gateways).
|
||||
(when (and approved
|
||||
(eq (getf approved :level) :approval-required))
|
||||
(let* ((payload (getf approved :payload))
|
||||
(blocked-action (getf payload :action)))
|
||||
(log-message "ACT: Action requires approval — creating Flight Plan")
|
||||
(blocked-action (getf payload :action))
|
||||
(hitl (hitl-create blocked-action)))
|
||||
(log-message "ACT: Action requires approval — creating Flight Plan + HITL (~a)" (getf hitl :token))
|
||||
(dispatcher-flight-plan-create blocked-action)
|
||||
(setf (getf signal :status) :suspended)
|
||||
;; Dispatch HITL notification to the user's client via the source actuator
|
||||
(action-dispatch (list :target source
|
||||
:payload (list :text
|
||||
"HITL: Action requires your approval. Check Flight Plan and set TODO to APPROVED."))
|
||||
:payload (list :text (getf hitl :message)))
|
||||
signal)
|
||||
(setf approved nil) ;; Don't execute the original action
|
||||
(setf feedback nil))) ;; Don't loop back — wait for human
|
||||
(setf approved nil)
|
||||
(setf feedback nil)))
|
||||
(when approved
|
||||
(let* ((original-type (getf approved :type))
|
||||
(verified (cognitive-verify approved signal)))
|
||||
|
||||
@@ -146,7 +146,7 @@ All signals get tagged with their processing stage (`:status :perceived`) and th
|
||||
(:approval-required
|
||||
(when (getf payload :approved)
|
||||
(log-message "GATE [Perceive]: Approved Flight Plan re-injected")
|
||||
(setf (getf signal :approved t))
|
||||
(setf (getf signal :approved) t)
|
||||
(setf (getf signal :approved-action) (getf payload :action))))
|
||||
;; Default sensor: pass through without requiring user-input processing
|
||||
(otherwise
|
||||
|
||||
@@ -431,7 +431,7 @@ privacy tags, privacy text, shell safety, network exfil, high-impact approval."
|
||||
;; REPL-VERIFIED: 2026-05-03T13:00:00
|
||||
#+begin_src lisp
|
||||
(defun dispatcher-flight-plan-create (blocked-action)
|
||||
"Creates a Flight Plan node for manual approval."
|
||||
"Creates a Flight Plan node for manual approval in Emacs."
|
||||
(let ((id (org-id-generate)))
|
||||
(log-message "BOUNCER: Creating flight plan node '~a'..." id)
|
||||
(list :type :REQUEST :target :emacs
|
||||
@@ -441,6 +441,96 @@ privacy tags, privacy text, shell safety, network exfil, high-impact approval."
|
||||
:ACTION (format nil "~s" blocked-action))))))
|
||||
#+end_src
|
||||
|
||||
** HITL In-Memory Store (Gateway-Agnostic Approval)
|
||||
|
||||
For TUI, CLI, and Signal/Telegram users who don't have Emacs. Pending
|
||||
actions are stored in memory with a correlation token. The user replies
|
||||
with the token to approve or deny.
|
||||
|
||||
;; REPL-VERIFIED: 2026-05-03T13:00:00
|
||||
#+begin_src lisp
|
||||
(defvar *hitl-pending* (make-hash-table :test 'equal)
|
||||
"Maps correlation token → blocked-action plist for pending HITL approvals.")
|
||||
#+end_src
|
||||
|
||||
;; REPL-VERIFIED: 2026-05-03T13:00:00
|
||||
#+begin_src lisp
|
||||
(defun hitl-create (blocked-action)
|
||||
"Saves a blocked action for HITL approval. Returns a plist with
|
||||
:token (the correlation ID) and :message (user-facing text)."
|
||||
(let* ((token (format nil "HITL-~a" (subseq (org-id-generate) 3 11))))
|
||||
(setf (gethash token *hitl-pending*) blocked-action)
|
||||
(log-message "HITL: Created pending approval ~a" token)
|
||||
(list :token token
|
||||
:message (format nil "HITL: Action requires approval [~a]. Reply /approve ~a to approve." token token))))
|
||||
#+end_src
|
||||
|
||||
;; REPL-VERIFIED: 2026-05-03T13:00:00
|
||||
#+begin_src lisp
|
||||
(defun hitl-approve (token)
|
||||
"Approves a pending HITL action by token. Re-injects with :approved t.
|
||||
Returns T if found and approved, nil if token is invalid."
|
||||
(let ((action (gethash token *hitl-pending*)))
|
||||
(if action
|
||||
(progn
|
||||
(remhash token *hitl-pending*)
|
||||
(setf (getf action :approved) t)
|
||||
(stimulus-inject (list :type :EVENT
|
||||
:payload (list :sensor :approval-required
|
||||
:action action
|
||||
:approved t)
|
||||
:meta (list :source :system)))
|
||||
(log-message "HITL: Approved ~a — re-injected" token)
|
||||
t)
|
||||
(progn
|
||||
(log-message "HITL: Token ~a not found in pending" token)
|
||||
nil))))
|
||||
#+end_src
|
||||
|
||||
;; REPL-VERIFIED: 2026-05-03T13:00:00
|
||||
#+begin_src lisp
|
||||
(defun hitl-deny (token)
|
||||
"Denies a pending HITL action by token. Removes it from the pending store.
|
||||
Returns T if found, nil if token is invalid."
|
||||
(if (gethash token *hitl-pending*)
|
||||
(progn
|
||||
(remhash token *hitl-pending*)
|
||||
(log-message "HITL: Denied ~a" token)
|
||||
t)
|
||||
(progn
|
||||
(log-message "HITL: Token ~a not found in pending" token)
|
||||
nil)))
|
||||
#+end_src
|
||||
|
||||
;; REPL-VERIFIED: 2026-05-03T13:00:00
|
||||
#+begin_src lisp
|
||||
(defun hitl-handle-message (text &optional source)
|
||||
"Checks if TEXT is a HITL approval or denial command.
|
||||
If it matches, processes the command and returns T.
|
||||
Otherwise returns nil (text should be handled as normal input).
|
||||
Recognized formats:
|
||||
/approve HITL-abc123
|
||||
/deny HITL-abc123
|
||||
approve HITL-abc123
|
||||
deny HITL-abc123"
|
||||
(let ((text (string-trim '(#\Space) (or text ""))))
|
||||
(when (or (uiop:string-prefix-p "/approve" text :test #'char-equal)
|
||||
(uiop:string-prefix-p "approve" text :test #'char-equal))
|
||||
(let* ((parts (uiop:split-string text :separator '(#\Space #\Tab)))
|
||||
(token (when (> (length parts) 1) (second parts))))
|
||||
(when (and token (hitl-approve token))
|
||||
(log-message "HITL: Approved via ~a — ~a" (or source :unknown) token)
|
||||
(return-from hitl-handle-message t))))
|
||||
(when (or (uiop:string-prefix-p "/deny" text :test #'char-equal)
|
||||
(uiop:string-prefix-p "deny " text :test #'char-equal))
|
||||
(let* ((parts (uiop:split-string text :separator '(#\Space #\Tab)))
|
||||
(token (when (> (length parts) 1) (second parts))))
|
||||
(when (and token (hitl-deny token))
|
||||
(log-message "HITL: Denied via ~a — ~a" (or source :unknown) token)
|
||||
(return-from hitl-handle-message t))))
|
||||
nil))
|
||||
#+end_src
|
||||
|
||||
** Gate Logic (dispatcher-gate)
|
||||
;; REPL-VERIFIED: 2026-05-03T13:00:00
|
||||
#+begin_src lisp
|
||||
|
||||
Reference in New Issue
Block a user