Files
passepartout/literate/perceive.org
Amr Gharbeia dd3873cd5e
Some checks failed
Deploy-Agent-V15-Stdin / JOB-V15-STDIN (push) Failing after 2s
DOCS: Systematic overhaul of Literate source (Granularity & Technical Reasoning)
2026-04-21 11:49:58 -04:00

5.0 KiB

Stage 1: Perceive (perceive.lisp)

Stage 1: Perceive (perceive.lisp)

Architectural Intent: Sensory Ingestion

The Perceive stage is the "sensory cortex" of the OpenCortex. Its primary responsibility is to take raw, unstructured stimuli from the outside world—whether from a TCP socket, a system interrupt, or a background heartbeat—and normalize them into high-fidelity internal Signals.

Normalization is critical because it shields the subsequent reasoning and actuation stages from the messiness of different transport protocols. Whether a message arrives via a TUI, a Signal bot, or an internal timer, the core "Brain" perceives a consistent Lisp property list.

Pipeline Initialization

Ensuring we are in the correct namespace for sensory processing.

(in-package :opencortex)

Sensory Concurrency (Async Sensors)

To maintain the agent's responsiveness, we distinguish between "Fast" and "Slow" sensors. Sensors that require extensive processing or external API calls are routed to asynchronous threads to prevent blocking the main metabolic pipeline.

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

Foveal Focus (User Context)

The system tracks the user's current point of interaction (the "foveal focus"). This provides immediate situational awareness to the reasoning engine, allowing it to prioritize the data the human is currently looking at.

(defvar *foveal-focus-id* nil
  "The Org ID of the node the user is currently interacting with.")

Primary Ingress

Stimulus Injection (inject-stimulus)

The inject-stimulus function is the universal gateway into the OpenCortex mind. It performs two critical tasks:

  1. Envelope Wrapping: Ensures that every raw message is wrapped in a :META envelope, preserving the source and session information.
  2. Dispatching: Determines whether to run the metabolism synchronously or in a new thread.
(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))
         (meta (getf raw-message :meta))
         (async-p (or (getf payload :async-p) (member sensor *async-sensors*))))
    
    ;; Ensure META exists and contains the stream if provided
    (unless meta (setf meta (list :SOURCE :SYSTEM :SESSION-ID "internal")))
    (when stream (setf (getf meta :reply-stream) stream))
    (setf (getf raw-message :meta) meta)

    (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.~%"))))))

The Perceive Stage

Perception Gate (perceive-gate)

The first official stage of the metabolic loop. It performs "Pre-Cognitive" work:

  1. Logging: Recording the arrival of the signal.
  2. State Sync: If the signal contains an AST update (e.g., from Emacs), it immediately updates the in-memory graph.
  3. Merkle Checkpointing: Before modifying memory, it creates a snapshot to allow for emergency rollbacks.
(defun perceive-gate (signal)
  "Initial processing: Normalizes raw stimuli and updates memory."
  (let* ((payload (getf signal :payload))
         (type (getf signal :type))
         (meta (getf signal :meta))
         (sensor (getf payload :sensor)))
    (harness-log "GATE [Perceive]: ~a (~a) [Source: ~s]" type (or sensor "no-sensor") (getf meta :source))
    
    (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))