#+TITLE: Stage 1: Perceive (perceive.lisp)
#+AUTHOR: Amr
#+FILETAGS: :harness:perceive:
#+STARTUP: content

* Stage 1: Perceive (perceive.lisp)
** Architectural Intent: Sensory Ingestion
The Perceive stage is the "sensory cortex" of the OpenCortex. It takes raw stimuli from the outside world (keyboard events, chat messages, heartbeats, or system interrupts) and normalizes them into internal **Signals**.

** Async Sensor Routing
To prevent blocking the main pipeline, certain sensors (like user commands or chat messages) are processed asynchronously in their own threads.

#+begin_src lisp :tangle ../src/perceive.lisp
(in-package :opencortex)

(defvar *async-sensors* '(:chat-message :delegation :user-command)
  "List of sensors that should be processed asynchronously to avoid blocking gateways.")
#+end_src

** Foveal Focus State
The system tracks the user's current point of interaction to provide context to the reasoning engine.

#+begin_src lisp :tangle ../src/perceive.lisp
(defvar *foveal-focus-id* nil
  "The Org ID of the node the user is currently interacting with.")
#+end_src

** Stimulus Injection
The entry point for raw messages. It determines if the signal should be processed synchronously or asynchronously.

#+begin_src lisp :tangle ../src/perceive.lisp
(defun inject-stimulus (raw-message &key stream (depth 0))
  "Enqueues a raw message into the reactive signal pipeline."
  (let* ((payload (getf raw-message :payload)) 
         (sensor (getf payload :sensor))
         (async-p (or (getf payload :async-p) (member sensor *async-sensors*))))
    (when stream (setf (getf raw-message :reply-stream) stream))
    (if async-p 
        (bt:make-thread 
         (lambda () 
           (restart-case (handler-bind ((error (lambda (c) (harness-log "ASYNC ERROR: ~a" c) (invoke-restart 'skip-event))))
                           (process-signal raw-message)) 
             (skip-event () nil))) 
         :name "opencortex-async-task")
        (restart-case (handler-bind ((error (lambda (c) (harness-log "SYSTEM ERROR: ~a" c) (invoke-restart 'skip-event)))) 
                        (process-signal raw-message))
          (skip-event () (harness-log "SYSTEM RECOVERY: Stimulus dropped.~%"))))))
#+end_src

** The Perceive Gate
The initial stage of the metabolic loop. It logs the signal, performs selective memory snapshots, and updates the Memory graph based on incoming AST updates.

#+begin_src lisp :tangle ../src/perceive.lisp
(defun perceive-gate (signal)
  "Initial processing: Normalizes raw stimuli and updates memory."
  (let* ((payload (getf signal :payload))
         (type (getf signal :type))
         (sensor (getf payload :sensor)))
    (harness-log "GATE [Perceive]: ~a (~a)" type (or sensor "no-sensor"))
    
    (cond ((eq type :EVENT)
           (case sensor
             (:buffer-update 
              (let ((ast (getf payload :ast))) 
                (when ast 
                  (snapshot-memory)
                  (ingest-ast ast))))
             (:point-update 
              (let ((element (getf payload :element))) 
                (when element 
                  (snapshot-memory)
                  (setf *foveal-focus-id* (ignore-errors (getf element :id)))
                  (ingest-ast element))))
             (:interrupt 
              (bt:with-lock-held (*interrupt-lock*) (setf *interrupt-flag* t)))))
          ((eq type :RESPONSE)
           (harness-log "GATE [Perceive]: Act Result -> ~a" (getf payload :status))))
           
    (setf (getf signal :status) :perceived)
    (setf (getf signal :foveal-focus) *foveal-focus-id*)
    signal))
#+end_src
