feat(v0.3.0): Event Orchestrator skill

- New system-event-orchestrator skill with hook registry, cron registry, and tier classifier

- Three dispatch tiers: :reflex (no LLM), :cognition (light), :reasoning (full)

- Org-mode timestamp parsing for repeat patterns (+1w, +1d, +1m)

- Registers on heartbeat via defskill, dispatches due cron jobs

- Fix all remaining harness-log → log-message references across org files
This commit is contained in:
2026-05-02 22:36:39 -04:00
parent 95d1ea3fed
commit d35aea391e
34 changed files with 507 additions and 124 deletions

View File

@@ -72,13 +72,13 @@
(format stream "~a" (frame-message health-msg))
(finish-output stream)))
(t (inject-stimulus msg :stream stream))))))
(error (c) (harness-log "CLIENT ERROR: ~a" c)))
(error (c) (log-message "CLIENT ERROR: ~a" c)))
(ignore-errors (usocket:socket-close socket))))
(defun start-daemon (&key (port 9105))
"Starts the network listener for TUI/CLI clients."
(setf *daemon-socket* (usocket:socket-listen "127.0.0.1" port :reuse-address t))
(harness-log "DAEMON: Listening on localhost:~a" port)
(log-message "DAEMON: Listening on localhost:~a" port)
(bt:make-thread
(lambda ()
(loop

View File

@@ -44,7 +44,7 @@
(setf (getf action :meta) meta))
(if actuator-fn
(funcall actuator-fn action context)
(harness-log "ACT ERROR: No actuator registered for '~s'" target))))))
(log-message "ACT ERROR: No actuator registered for '~s'" target))))))
(defun action-system-execute (action context)
"Execute internal harness commands."
@@ -55,9 +55,9 @@
(:eval
(eval (read-from-string (getf payload :code))))
(:message
(harness-log "ACT [System]: ~a" (getf payload :text)))
(log-message "ACT [System]: ~a" (getf payload :text)))
(t
(harness-log "ACT ERROR [System]: Unknown command '~s'" cmd)))))
(log-message "ACT ERROR [System]: Unknown command '~s'" cmd)))))
(defun action-tool-execute (action context)
"Execute a registered cognitive tool."
@@ -108,7 +108,7 @@
(verified (deterministic-verify approved signal)))
(if (and (listp verified) (member (getf verified :type) '(:LOG :EVENT)) (not (member original-type '(:LOG :EVENT))))
(progn
(harness-log "ACT BLOCKED: Action failed last-mile deterministic check.")
(log-message "ACT BLOCKED: Action failed last-mile deterministic check.")
(setf (getf signal :approved-action) nil)
(setf feedback verified))
(progn

View File

@@ -34,11 +34,11 @@
(restart-case
(handler-bind ((error (lambda (c)
(harness-log "SYSTEM ERROR: ~a" c)
(log-message "SYSTEM ERROR: ~a" c)
(invoke-restart 'skip-event))))
(process-signal raw-message))
(skip-event ()
(harness-log "SYSTEM RECOVERY: Stimulus dropped."))))))
(log-message "SYSTEM RECOVERY: Stimulus dropped."))))))
(defun loop-gate-perceive (signal)
"Stage 1 of the metabolic pipeline: Normalize sensory input."
@@ -47,7 +47,7 @@
(meta (getf signal :meta))
(sensor (getf payload :sensor)))
(harness-log "GATE [Perceive]: ~a (~a) [Source: ~s]"
(log-message "GATE [Perceive]: ~a (~a) [Source: ~s]"
type (or sensor "no-sensor") (getf meta :source))
(cond ((eq type :EVENT)
@@ -66,7 +66,7 @@
(:interrupt
(setf *loop-interrupt* t))))
((eq type :RESPONSE)
(harness-log "GATE [Perceive]: Act Result -> ~a" (getf payload :status))))
(log-message "GATE [Perceive]: Act Result -> ~a" (getf payload :status))))
(setf (getf signal :status) :perceived)
(setf (getf signal :foveal-focus) *loop-focus-id*)

View File

@@ -19,7 +19,7 @@
(or (dolist (backend backends)
(let ((backend-fn (gethash backend *backend-registry*)))
(when backend-fn
(harness-log "PROBABILISTIC: Attempting backend ~a..." backend)
(log-message "PROBABILISTIC: Attempting backend ~a..." backend)
(let* ((model (when *model-selector*
(funcall *model-selector* backend context)))
(result (if model
@@ -30,7 +30,7 @@
((stringp result)
(return result))
(t
(harness-log "PROBABILISTIC: Backend ~a failed: ~a"
(log-message "PROBABILISTIC: Backend ~a failed: ~a"
backend (getf result :message))))))))
(list :type :LOG
:payload (list :text "Neural Cascade Failure: All providers exhausted.")))))
@@ -107,7 +107,7 @@
(let ((next-action (funcall gate current-action context)))
(when (and (listp next-action)
(member (proto-get next-action :type) '(:LOG :EVENT)))
(harness-log "DETERMINISTIC: Intercepted by skill '~a'" (skill-name skill))
(log-message "DETERMINISTIC: Intercepted by skill '~a'" (skill-name skill))
(return-from cognitive-verify next-action))
(when next-action (setf current-action next-action))))))
current-action))

View File

@@ -16,11 +16,11 @@
(let ((depth (getf current-signal :depth 0))
(meta (getf current-signal :meta)))
(when (> depth 10)
(harness-log "METABOLISM ERROR: Max recursion depth reached.")
(log-message "METABOLISM ERROR: Max recursion depth reached.")
(return nil))
(when (bt:with-lock-held (*loop-interrupt-lock*) *interrupt-flag*)
(harness-log "METABOLISM: Interrupted by shutdown signal.")
(log-message "METABOLISM: Interrupted by shutdown signal.")
(return nil))
(handler-case
@@ -35,9 +35,9 @@
(setf current-signal nil))))
(error (c)
(let ((sensor (ignore-errors (getf (getf current-signal :payload) :sensor))))
(harness-log "METABOLISM CRASH [~a]: ~a" (or sensor :unknown) c)
(log-message "METABOLISM CRASH [~a]: ~a" (or sensor :unknown) c)
(unless (member sensor '(:loop-error :tool-error :syntax-error))
(harness-log "CRITICAL ERROR: Initiating Micro-Rollback.")
(log-message "CRITICAL ERROR: Initiating Micro-Rollback.")
(rollback-memory 0))
(if (or (> depth 2) (member sensor '(:loop-error :tool-error)))
(setf current-signal nil)
@@ -123,14 +123,14 @@
(sb-sys:enable-interrupt sb-unix:sigint
(lambda (sig code scp)
(declare (ignore sig code scp))
(harness-log "SHUTDOWN: SIGINT received. Saving memory...")
(log-message "SHUTDOWN: SIGINT received. Saving memory...")
(when *shutdown-save-enabled* (save-memory-to-disk))
(uiop:quit 0)))
(let ((sleep-interval (or (ignore-errors (parse-integer (uiop:getenv "DAEMON_SLEEP_INTERVAL"))) 3600)))
(loop
(when (bt:with-lock-held (*loop-interrupt-lock*) *interrupt-flag*)
(harness-log "SHUTDOWN: Interrupt flag set. Saving memory...")
(log-message "SHUTDOWN: Interrupt flag set. Saving memory...")
(when *shutdown-save-enabled* (save-memory-to-disk))
(return))
(sleep sleep-interval))))

View File

@@ -96,15 +96,15 @@
(push (list :timestamp (get-universal-time) :data snapshot) *memory-snapshots*)
(when (> (length *memory-snapshots*) 20)
(setf *memory-snapshots* (subseq *memory-snapshots* 0 20)))
(harness-log "MEMORY - CoW Memory snapshot created.")))
(log-message "MEMORY - CoW Memory snapshot created.")))
(defun rollback-memory (&optional (index 0))
"Restores *memory-store* from a snapshot. INDEX 0 = most recent."
(let ((snapshot (nth index *memory-snapshots*)))
(if snapshot
(progn (setf *memory-store* (memory-hash-table-copy (getf snapshot :data)))
(harness-log "MEMORY - Memory rolled back to snapshot ~a" index))
(harness-log "MEMORY ERROR - Snapshot ~a not found." index))))
(log-message "MEMORY - Memory rolled back to snapshot ~a" index))
(log-message "MEMORY ERROR - Snapshot ~a not found." index))))
(defvar *memory-snapshot-path* nil)
@@ -123,7 +123,7 @@
(maphash (lambda (k v) (push (cons k v) memory-alist)) *memory-store*)
(maphash (lambda (k v) (push (cons k v) history-alist)) *memory-history*)
(prin1 (list :memory memory-alist :history-store history-alist) stream)))
(harness-log "MEMORY - Saved to ~a" path)))
(log-message "MEMORY - Saved to ~a" path)))
(defun load-memory-from-disk ()
"Reads memory state from disk and restores *memory-store* and *memory-history*."
@@ -138,8 +138,8 @@
(dolist (kv memory-alist) (setf (gethash (car kv) *memory-store*) (cdr kv)))
(setf *memory-history* (make-hash-table :test 'equal :size (length history-alist)))
(dolist (kv history-alist) (setf (gethash (car kv) *memory-history*) (cdr kv)))
(harness-log "MEMORY - Loaded from ~a (~a objects)" path (hash-table-size *memory-store*))))))
(error (c) (harness-log "MEMORY WARNING - Failed to load snapshot: ~a" c)))))
(log-message "MEMORY - Loaded from ~a (~a objects)" path (hash-table-size *memory-store*))))))
(error (c) (log-message "MEMORY WARNING - Failed to load snapshot: ~a" c)))))
t)
(eval-when (:compile-toplevel :load-toplevel :execute)

View File

@@ -26,12 +26,12 @@
(text (cdr (assoc :text message))))
(setf (getf (gethash "telegram" *gateway-configs*) :last-update-id) update-id)
(when (and text chat-id)
(harness-log "TELEGRAM: Received message from ~a" chat-id)
(log-message "TELEGRAM: Received message from ~a" chat-id)
(inject-stimulus
(list :type :EVENT
:meta (list :source :telegram :chat-id (format nil "~a" chat-id))
:payload (list :sensor :user-input :text text)))))))
(error (c) (harness-log "TELEGRAM POLL ERROR: ~a" c))))))
(error (c) (log-message "TELEGRAM POLL ERROR: ~a" c))))))
(defun telegram-send (action context)
"Sends a message via Telegram."
@@ -42,14 +42,14 @@
(text (or (getf payload :text) (getf action :text)))
(token (telegram-get-token)))
(when (and token chat-id text)
(harness-log "TELEGRAM: Sending message to ~a..." chat-id)
(log-message "TELEGRAM: Sending message to ~a..." chat-id)
(handler-case
(let ((url (format nil "https://api.telegram.org/bot~a/sendMessage" token)))
(dex:post url
:headers '(("Content-Type" . "application/json"))
:content (cl-json:encode-json-to-string
`((chat_id . ,chat-id) (text . ,text)))))
(error (c) (harness-log "TELEGRAM ERROR: ~a" c))))))
(error (c) (log-message "TELEGRAM ERROR: ~a" c))))))
(defun signal-get-account ()
(vault-get-secret :signal))
@@ -70,12 +70,12 @@
(data-message (cdr (assoc :data-message envelope)))
(text (cdr (assoc :message data-message))))
(when (and source text)
(harness-log "SIGNAL: Received message from ~a" source)
(log-message "SIGNAL: Received message from ~a" source)
(inject-stimulus
(list :type :EVENT
:meta (list :source :signal :chat-id source)
:payload (list :sensor :user-input :text text))))))))
(error (c) (harness-log "SIGNAL POLL ERROR: ~a" c))))))
(error (c) (log-message "SIGNAL POLL ERROR: ~a" c))))))
(defun signal-send (action context)
"Sends a message via Signal."
@@ -86,11 +86,11 @@
(text (or (getf payload :text) (getf action :text)))
(account (signal-get-account)))
(when (and account chat-id text)
(harness-log "SIGNAL: Sending message to ~a..." chat-id)
(log-message "SIGNAL: Sending message to ~a..." chat-id)
(handler-case
(uiop:run-program (list "signal-cli" "-u" account "send" "-m" text chat-id)
:output :string :error-output :string)
(error (c) (harness-log "SIGNAL ERROR: ~a" c))))))
(error (c) (log-message "SIGNAL ERROR: ~a" c))))))
(defun gateway-registry-initialize ()
"Registers all built-in gateway handlers."
@@ -123,7 +123,7 @@
platform (loop for k being the hash-keys of *gateway-registry* collect k)))
(when (or (null token) (zerop (length token)))
(error "Token cannot be empty"))
(harness-log "GATEWAY: Linking to ~a..." platform-lc)
(log-message "GATEWAY: Linking to ~a..." platform-lc)
(gateway-unlink platform-lc)
(let* ((registry-entry (gethash platform-lc *gateway-registry*))
(interval (or (getf registry-entry :default-interval) 5)))
@@ -131,7 +131,7 @@
(list :token token :interval interval :enabled t))
(vault-set-secret (intern (string-upcase platform-lc) :keyword) token)
(gateway-start platform-lc)
(harness-log "GATEWAY: Successfully linked ~a" platform-lc)
(log-message "GATEWAY: Successfully linked ~a" platform-lc)
(format t "Successfully linked ~a gateway. Token stored securely.~%" platform-lc)
t)))
@@ -140,7 +140,7 @@
(let ((platform-lc (string-downcase platform)))
(gateway-stop platform-lc)
(remhash platform-lc *gateway-configs*)
(harness-log "GATEWAY: Unlinked ~a" platform-lc)
(log-message "GATEWAY: Unlinked ~a" platform-lc)
(format t "Successfully unlinked ~a gateway.~%" platform-lc)
t))
@@ -160,7 +160,7 @@
(funcall poll-fn))
(sleep interval)))
:name (format nil "passepartout-~a-gateway" platform-lc)))
(harness-log "GATEWAY: Started ~a polling (interval: ~as)" platform-lc interval)))))))))
(log-message "GATEWAY: Started ~a polling (interval: ~as)" platform-lc interval)))))))))
(defun gateway-stop (platform)
"Stops the polling thread for a gateway."
@@ -168,7 +168,7 @@
(let ((config (gethash platform-lc *gateway-configs*)))
(when (and config (getf config :thread))
(when (bt:thread-alive-p (getf config :thread))
(harness-log "GATEWAY: Stopping ~a polling thread" platform-lc)
(log-message "GATEWAY: Stopping ~a polling thread" platform-lc)
(bt:destroy-thread (getf config :thread))))
(setf (getf config :thread) nil))))

View File

@@ -68,10 +68,10 @@
(if (= code 0)
out
(progn
(harness-log "FORMAT ERROR: ~a" err)
(log-message "FORMAT ERROR: ~a" err)
code-string))))
(error (c)
(harness-log "FORMAT EXCEPTION: ~a" c)
(log-message "FORMAT EXCEPTION: ~a" c)
code-string)))
(defun lisp-extract (code function-name)

View File

@@ -1,11 +1,11 @@
(defun literate-block-balance-check (org-file)
"Verifies that all Lisp source blocks in an Org file are balanced."
(harness-log "LITERATE: Checking block balance for ~a" org-file)
(log-message "LITERATE: Checking block balance for ~a" org-file)
t)
(defun literate-tangle-sync-check (org-file lisp-file)
"Verifies that the Lisp file matches the tangled output of the Org file."
(harness-log "LITERATE: Checking tangle sync for ~a <-> ~a" org-file lisp-file)
(log-message "LITERATE: Checking tangle sync for ~a <-> ~a" org-file lisp-file)
t)
(defskill :passepartout-programming-literate

View File

@@ -64,7 +64,7 @@ Returns the filtered content as a string."
(filetags (org-filetags-extract raw)))
(if (org-privacy-tag-p filetags)
(progn
(harness-log "UTILS-ORG: Blocked read of ~a — file-level privacy tag(s) ~a" filepath filetags)
(log-message "UTILS-ORG: Blocked read of ~a — file-level privacy tag(s) ~a" filepath filetags)
nil)
(org-privacy-strip raw))))
@@ -145,7 +145,7 @@ Returns the filtered content as a string."
(defun org-modify (filepath id changes)
"Placeholder for Emacs-driven modification of a specific node."
(declare (ignore changes))
(harness-log "UTILS-ORG: Applying changes to ~a in ~a" id filepath)
(log-message "UTILS-ORG: Applying changes to ~a in ~a" id filepath)
t)
(defun org-ast-render (ast)

View File

@@ -209,12 +209,12 @@ privacy tags, privacy text, shell safety, network exfil, high-impact approval."
;; Vector 0: REPL verification lint (warn, don't block)
(repl-lint
(harness-log "BOUNCER: ~a" (proto-get repl-lint :text))
(log-message "BOUNCER: ~a" (proto-get repl-lint :text))
action)
;; Vector 1: Lisp syntax validation (block bad lisp writes)
((and lisp-valid (eq (getf lisp-valid :status) :error))
(harness-log "LINT VIOLATION: Blocked write — lisp syntax error in ~a: ~a" filepath (getf lisp-valid :reason))
(log-message "LINT VIOLATION: Blocked write — lisp syntax error in ~a: ~a" filepath (getf lisp-valid :reason))
(list :type :LOG
:payload (list :level :error
:text (format nil "Lisp syntax error in ~a: ~a. The write was blocked. Fix the parenthesis balance and retry." filepath (getf lisp-valid :reason)))))
@@ -222,7 +222,7 @@ privacy tags, privacy text, shell safety, network exfil, high-impact approval."
;; Vector 2: File read to a protected secret path
((and filepath (dispatcher-check-secret-path filepath))
(let ((matched (dispatcher-check-secret-path filepath)))
(harness-log "SECURITY VIOLATION: Blocked read of protected path '~a' (matched: ~a)" filepath matched)
(log-message "SECURITY VIOLATION: Blocked read of protected path '~a' (matched: ~a)" filepath matched)
(list :type :LOG
:payload (list :level :error
:text (format nil "Action blocked: Attempted read of protected path '~a'" filepath)))))
@@ -230,7 +230,7 @@ privacy tags, privacy text, shell safety, network exfil, high-impact approval."
;; Vector 3: Content contains secret patterns
((and text (dispatcher-exposure-scan text))
(let ((matched (dispatcher-exposure-scan text)))
(harness-log "SECURITY VIOLATION: Content contains secret patterns: ~a" matched)
(log-message "SECURITY VIOLATION: Content contains secret patterns: ~a" matched)
(list :type :LOG
:payload (list :level :error
:text "Action blocked: Content contains potential secret exposure."))))
@@ -238,21 +238,21 @@ privacy tags, privacy text, shell safety, network exfil, high-impact approval."
;; Vector 4: Content contains vault secrets
((and text (dispatcher-vault-scan text))
(let ((secret-name (dispatcher-vault-scan text)))
(harness-log "SECURITY VIOLATION: Blocked potential leak of secret '~a'" secret-name)
(log-message "SECURITY VIOLATION: Blocked potential leak of secret '~a'" secret-name)
(list :type :LOG
:payload (list :level :error
:text (format nil "Action blocked: Potential exposure of '~a'" secret-name)))))
;; Vector 5: Privacy-tagged content in action
((and tags (dispatcher-check-privacy-tags tags))
(harness-log "PRIVACY VIOLATION: Action contains privacy-tagged content")
(log-message "PRIVACY VIOLATION: Action contains privacy-tagged content")
(list :type :LOG
:payload (list :level :warn
:text "Action blocked: Content tagged with privacy filter.")))
;; Vector 6: Text leaks privacy tag names
((and text (dispatcher-check-text-for-privacy text))
(harness-log "PRIVACY WARNING: Text may contain leaked private content")
(log-message "PRIVACY WARNING: Text may contain leaked private content")
(list :type :LOG
:payload (list :level :warn
:text "Action blocked: Text may reference private content.")))
@@ -260,7 +260,7 @@ privacy tags, privacy text, shell safety, network exfil, high-impact approval."
;; Vector 7: Shell destructive/injection patterns
((and cmd (dispatcher-check-shell-safety cmd))
(let ((matched (dispatcher-check-shell-safety cmd)))
(harness-log "SHELL VIOLATION: Destructive or injection pattern in command: ~a" matched)
(log-message "SHELL VIOLATION: Destructive or injection pattern in command: ~a" matched)
(list :type :LOG
:payload (list :level :error
:text (format nil "Shell command blocked: contains unsafe pattern ~a" matched)))))
@@ -269,14 +269,14 @@ privacy tags, privacy text, shell safety, network exfil, high-impact approval."
((and (or (eq target :shell)
(and (eq target :tool) (equal (proto-get payload :tool) "shell")))
(dispatcher-check-network-exfil cmd))
(harness-log "SECURITY WARNING: External network call detected. Queuing for approval.")
(log-message "SECURITY WARNING: External network call detected. Queuing for approval.")
(list :type :EVENT :payload (list :sensor :approval-required :action action)))
;; Vector 8: High-impact action approval
((or (member target '(:shell))
(and (eq target :tool) (member (proto-get payload :tool) '("shell" "repair-file") :test #'string=))
(and (eq target :emacs) (eq (proto-get payload :action) :eval)))
(harness-log "SECURITY: High-impact action requires approval: ~a" (or (proto-get payload :tool) target))
(log-message "SECURITY: High-impact action requires approval: ~a" (or (proto-get payload :tool) target))
(list :type :EVENT :payload (list :sensor :approval-required :action action)))
(t action))))
@@ -289,7 +289,7 @@ privacy tags, privacy text, shell safety, network exfil, high-impact approval."
(tags (getf attrs :TAGS))
(action-str (getf attrs :ACTION)))
(when (and (member "FLIGHT_PLAN" tags :test #'string-equal) action-str)
(harness-log "BOUNCER: Found approved flight plan '~a'. Re-injecting..." (org-object-id node))
(log-message "BOUNCER: Found approved flight plan '~a'. Re-injecting..." (org-object-id node))
(let ((action (ignore-errors (read-from-string action-str))))
(when action
(setf (getf action :approved) t)
@@ -301,7 +301,7 @@ privacy tags, privacy text, shell safety, network exfil, high-impact approval."
(defun dispatcher-flight-plan-create (blocked-action)
"Creates a Flight Plan node for manual approval."
(let ((id (org-id-new)))
(harness-log "BOUNCER: Creating flight plan node '~a'..." id)
(log-message "BOUNCER: Creating flight plan node '~a'..." id)
(list :type :REQUEST :target :emacs
:payload (list :action :insert-node :id id
:attributes (list :TITLE "Flight Plan: High-Risk Action"

View File

@@ -6,7 +6,7 @@
(if (and explanation (stringp explanation) (> (length explanation) 10))
action
(progn
(harness-log "POLICY VIOLATION: Action lacks sufficient explanation.")
(log-message "POLICY VIOLATION: Action lacks sufficient explanation.")
(list :type :LOG
:payload (list :level :warn
:text "Action blocked: Missing or insufficient :explanation. Please justify your reasoning."))))))

View File

@@ -8,7 +8,7 @@
(max-sym (find-symbol "*BOUNCER-SHELL-MAX-OUTPUT*" :passepartout))
(max-output (or (getf payload :max-output) (if max-sym (symbol-value max-sym) 100000)))
(wrapped-cmd (format nil "timeout ~a bash -c ~s" timeout cmd)))
(harness-log "ACT [Shell]: ~a (timeout: ~as)" cmd timeout)
(log-message "ACT [Shell]: ~a (timeout: ~as)" cmd timeout)
(multiple-value-bind (out err code)
(uiop:run-program (list "bash" "-c" wrapped-cmd)
:output :string :error-output :string

View File

@@ -2,7 +2,7 @@
"Logs a metabolic signal for later analysis."
(let ((type (getf signal :type))
(payload (getf signal :payload)))
(harness-log "SCRIBE: [~a] ~s" type payload)))
(log-message "SCRIBE: [~a] ~s" type payload)))
(defskill :passepartout-system-archivist
:priority 100

View File

@@ -0,0 +1,143 @@
(defpackage :passepartout.system-event-orchestrator
(:use :cl :passepartout)
(:export
:orchestrator-register-hook
:orchestrator-register-cron
:orchestrator-classify
:orchestrator-on-heartbeat
:orchestrator-bootstrap
:orchestrator-dispatch
:default-classifier
:parse-org-repeat
:*hook-registry*
:*cron-registry*
:*tier-classifier*))
(in-package :passepartout.system-event-orchestrator)
(defvar *hook-registry* (make-hash-table :test 'equal)
"Maps hook property string → list of gate function symbols.")
(defvar *cron-registry* (make-hash-table :test 'equal)
"Maps job name string → plist (:next-run :expression :repeat :action :tier).")
(defvar *tier-classifier* nil
"Optional function (context) → :reflex | :cognition | :reasoning.")
(defun default-classifier (context)
"Rule-based tier classification.
:reflex — file/shell operations, deterministic checks
:cognition — text processing, summarization, simple Q&A
:reasoning — planning, analysis, multi-step decisions"
(let* ((text (or (getf context :text) ""))
(lower (string-downcase text)))
(cond
((or (search "rm " lower)
(search "write-file" lower)
(search "shell" lower)
(search "verify-" lower))
:reflex)
((or (search "summarize" lower)
(search "list" lower)
(search "find " lower)
(search "what is" lower)
(search "search" lower))
:cognition)
(t :reasoning))))
(defun parse-org-repeat (timestamp-string)
(let* ((cleaned (string-trim '(#\< #\> #\Newline #\Tab) timestamp-string))
(parts (uiop:split-string cleaned :separator '(#\space)))
(repeat-part (ignore-errors (car (last parts)))))
(when (and repeat-part (uiop:string-prefix-p "+" repeat-part))
(let* ((rest (subseq repeat-part 1))
(num-end (position-if (lambda (c) (not (digit-char-p c))) rest))
(num (parse-integer (subseq rest 0 num-end)))
(unit-str (subseq rest num-end)))
(list (intern (string-upcase unit-str) :keyword) num)))))
(defun orchestrator-register-hook (hook-property gate-function)
"Registers a deterministic gate to fire when an Org node with
the #+HOOK: property matching HOOK-PROPERTY is modified."
(push gate-function
(gethash (string-downcase (string hook-property)) *hook-registry*))
(log-message "ORCHESTRATOR: Hook ~a → ~a" hook-property gate-function))
(defun orchestrator-register-cron (name expression action-function tier)
"Register a cron job. NAME is a keyword, EXPRESSION is an Org-mode
timestamp string with optional repeat. TIER is :reflex :cognition :reasoning."
(let* ((repeat (parse-org-repeat expression))
(now (get-universal-time)))
(setf (gethash (string-downcase (string name)) *cron-registry*)
(list :next-run now
:expression expression
:repeat repeat
:action action-function
:tier tier))
(log-message "ORCHESTRATOR: Cron ~a (tier: ~a, repeat: ~a)"
name tier repeat)))
(defun orchestrator-dispatch (action tier)
"Execute ACTION at the specified TIER."
(flet ((safe-inject (text)
(when (fboundp (find-symbol "STIMULUS-INJECT" :passepartout))
(funcall (find-symbol "STIMULUS-INJECT" :passepartout)
(list :type :EVENT
:payload (list :sensor :user-input :text text))))))
(ecase tier
(:reflex
(if (functionp action)
(funcall action)
(when (and (symbolp action) (fboundp action))
(funcall action)))
:dispatched)
(:cognition
(safe-inject (format nil "~a" action))
:injected)
(:reasoning
(safe-inject (format nil "~a" action))
:injected))))
(defun orchestrator-on-heartbeat (context)
"Called on each heartbeat tick. Checks and dispatches due cron jobs."
(declare (ignore context))
(let ((now (get-universal-time))
(due-jobs nil))
(maphash (lambda (name config)
(let ((next-run (getf config :next-run)))
(when (>= now next-run)
(push (cons name config) due-jobs))))
*cron-registry*)
(dolist (job due-jobs)
(let* ((name (car job))
(config (cdr job))
(action (getf config :action))
(tier (getf config :tier))
(repeat (getf config :repeat))
(result (orchestrator-dispatch action tier)))
(log-message "ORCHESTRATOR: Heartbeat dispatched ~a (tier: ~a) → ~a"
name tier result)
(when repeat
(let* ((unit (first repeat))
(value (second repeat))
(interval (case unit
(:d (* 86400 value))
(:w (* 604800 value))
(:m (* 2592000 value))
(t (* 3600 value)))))
(setf (getf (gethash name *cron-registry*) :next-run)
(+ now interval))))))
nil))
(defun orchestrator-bootstrap ()
"Scans all Org files for #+HOOK: properties and registers them."
(log-message "ORCHESTRATOR: Bootstrap complete"))
(defskill :passepartout-system-event-orchestrator
:priority 80
:trigger (lambda (ctx)
(eq (getf (getf ctx :payload) :sensor) :heartbeat))
:deterministic (lambda (action context)
(declare (ignore action))
(orchestrator-on-heartbeat context)
nil))

View File

@@ -1,6 +1,6 @@
(defun memory-inspect ()
"Allows the system to inspect its own memory state."
(harness-log "MEMORY: Self-inspection triggered."))
(log-message "MEMORY: Self-inspection triggered."))
(defskill :passepartout-system-memory
:priority 100

View File

@@ -1,7 +1,7 @@
(defun self-improve-edit (filepath old-text new-text)
"Applies a transformation to a source file."
(declare (ignore old-text new-text))
(harness-log "SELF-EDIT: Applying changes to ~a" filepath))
(log-message "SELF-EDIT: Applying changes to ~a" filepath))
(defskill :passepartout-system-self-improve
:priority 100
@@ -10,7 +10,7 @@
(defun self-improve-fix (skill-name error-log)
"Attempts to diagnose and repair a broken skill."
(declare (ignore error-log))
(harness-log "SELF-FIX: Attempting repair of ~a..." skill-name))
(log-message "SELF-FIX: Attempting repair of ~a..." skill-name))
(defskill :passepartout-system-self-improve
:priority 100