PSF: Stabilizing workspace after crash. Valid kernel/skill fixes.

This commit is contained in:
2026-04-04 20:27:39 -04:00
parent 65a14784d3
commit 7ac10d1f95
47 changed files with 25388 additions and 3235 deletions

View File

@@ -1,19 +1,3 @@
;;;; architect-logic.lisp --- Architectural automation for the PSF (Unified).
;;;; This file is TANGLED from notes/org-skill-architect.org. DO NOT EDIT MANUALLY.
(defpackage :org-skill-architect
(:use :cl :uiop :local-time)
(:export #:architect-perceive-frozen-prd
#:architect-scan-all-notes
#:trigger-skill-architect
#:neuro-skill-architect
#:architect-actuate))
(in-package :org-skill-architect)
(defun kernel-log (message &rest args)
(format t "~&[ARCHITECT] ~?" message args))
(defun architect-perceive-frozen-prd (note-path)
"Checks if a master note has a FROZEN PRD and lacks a Phase B section."
(let ((content (uiop:read-file-string note-path)))
@@ -68,7 +52,7 @@
(note-path (getf payload :path))
(blueprint-content (getf payload :content)))
(kernel-log "Appending PROTOCOL to ~a" note-path)
(org-agent:kernel-log "ARCHITECT - Appending PROTOCOL to ~a" note-path)
(with-open-file (out note-path :direction :output :if-exists :append)
(format out "~%* Phase B: Blueprint (PROTOCOL)~%:PROPERTIES:~%:STATUS: SIGNED~%:END:~%~%~a"
blueprint-content))

View File

@@ -0,0 +1,11 @@
(defun auth-api-key-get-credentials ()
(let ((key (uiop:getenv "LLM_API_KEY")))
(when key
(list :api-key key))))
(defun register-auth-provider (provider-name credential-fn)
"Register a simple API key provider in the kernel."
(org-agent:register-auth-provider provider-name credential-fn))
;; Register as the default auth provider for Gemini during transition
(register-auth-provider :gemini #'auth-api-key-get-credentials)

View File

@@ -0,0 +1,78 @@
(defvar *google-token-state* nil)
(defun auth-google-load-state ()
(let ((state-file (merge-pathnames "state/auth-google.lisp" (uiop:getenv "SYSTEM_DIR"))))
(if (uiop:file-exists-p state-file)
(setf *google-token-state* (with-open-file (in state-file) (read in)))
(setf *google-token-state* nil))))
(defun auth-google-save-state ()
(let* ((state-dir (uiop:getenv "SYSTEM_DIR"))
(state-file (merge-pathnames "state/auth-google.lisp" state-dir)))
(ensure-directories-exist state-file)
(with-open-file (out state-file :direction :output :if-exists :supersede)
(print *google-token-state* out))))
(defun auth-google-receive-code (code)
"Exchanges the manual authorization code for access and refresh tokens."
(let ((url "https://oauth2.googleapis.com/token")
(content `(("code" . ,code)
("client_id" . ,(uiop:getenv "GOOGLE_CLIENT_ID"))
("client_secret" . ,(uiop:getenv "GOOGLE_CLIENT_SECRET"))
("redirect_uri" . "urn:ietf:wg:oauth:2.0:oob")
("grant_type" . "authorization_code"))))
(handler-case
(let* ((response (dex:post url :content content))
(json (cl-json:decode-json-from-string response)))
(setf *google-token-state*
`(:access-token ,(cdr (assoc :access--token json))
:refresh-token ,(cdr (assoc :refresh--token json))
:expires-at ,(+ (get-universal-time) (cdr (assoc :expires--in json)))))
(auth-google-save-state)
(kernel-log "OAUTH - Google handshake successful.")
t)
(error (c)
(kernel-log "OAUTH ERROR - Handshake failed: ~a" c)
nil))))
(defun auth-google-refresh-token ()
"Uses the refresh_token to acquire a new access_token."
(let ((refresh-token (getf *google-token-state* :refresh-token))
(url "https://oauth2.googleapis.com/token")
(content `(("refresh_token" . ,(getf *google-token-state* :refresh-token))
("client_id" . ,(uiop:getenv "GOOGLE_CLIENT_ID"))
("client_secret" . ,(uiop:getenv "GOOGLE_CLIENT_SECRET"))
("grant_type" . "refresh_token"))))
(unless refresh-token (return-from auth-google-refresh-token nil))
(handler-case
(let* ((response (dex:post url :content content))
(json (cl-json:decode-json-from-string response)))
(setf (getf *google-token-state* :access-token) (cdr (assoc :access--token json)))
(setf (getf *google-token-state* :expires-at) (+ (get-universal-time) (cdr (assoc :expires--in json))))
(auth-google-save-state)
(kernel-log "OAUTH - Google token refreshed.")
t)
(error (c)
(kernel-log "OAUTH ERROR - Refresh failed: ~a" c)
nil))))
(defun auth-google-get-header ()
"Returns the Bearer token header, refreshing if necessary."
(unless *google-token-state* (auth-google-load-state))
(let ((expires-at (getf *google-token-state* :expires-at 0)))
(when (<= expires-at (+ (get-universal-time) 60)) ; Refresh if < 1 min left
(auth-google-refresh-token)))
(let ((token (getf *google-token-state* :access-token)))
(if token
(list :bearer-token token)
(progn
(kernel-log "OAUTH - No active Google token. Handshake required.")
(kernel-log "OAUTH - Visit this URL: ~a" (auth-google-get-url))
nil))))
(defun auth-google-get-url ()
(let ((client-id (uiop:getenv "GOOGLE_CLIENT_ID")))
(format nil "https://accounts.google.com/o/oauth2/v2/auth?client_id=~a&redirect_uri=urn:ietf:wg:oauth:2.0:oob&response_type=code&scope=https://www.googleapis.com/auth/generative-language" client-id)))
;; Register as the primary auth provider for Gemini
(org-agent:register-auth-provider :gemini #'auth-google-get-header)

View File

@@ -0,0 +1,21 @@
(defun chaos-inject-error (sensor-type)
"Injects a synthetic error into a specific sensor pipeline."
(org-agent:kernel-log "CHAOS - Injecting synthetic error into ~a sensor..." sensor-type)
(org-agent:inject-stimulus
`(:type :EVENT :payload (:sensor ,sensor-type :error "SYNTHETIC_CHAOS_ERROR"))))
(defun chaos-stress-test (action context)
"Executes a randomized stress test by injecting failures into the system."
(declare (ignore context))
(let* ((payload (getf action :payload))
(mode (or (getf payload :mode) :random))
(intensity (or (getf payload :intensity) 3)))
(org-agent:kernel-log "CHAOS - Commencing stress test (Mode: ~a, Intensity: ~a)" mode intensity)
(case mode
(:random (dotimes (i intensity)
(let ((failure-type (nth (random 3) '(:test-failure :shell-timeout :llm-error))))
(org-agent:inject-stimulus
`(:type :EVENT :payload (:sensor :chaos-injection :type ,failure-type))))))
(:shell (org-agent:inject-stimulus
`(:type :EVENT :payload (:sensor :shell-response :cmd "git push" :exit-code 128 :stderr "fatal: network unreachable")))))
(format nil "SUCCESS - Chaos stress test initiated.")))

View File

@@ -0,0 +1,19 @@
(defvar *context-stack* nil)
(defun context-push (new-context)
"Push a new context (usually a path or a plist) onto the stack."
(push new-context *context-stack*)
(kernel-log "CONTEXT - Pushed: ~a" new-context))
(defun context-pop ()
"Pop the top context from the stack."
(let ((old (pop *context-stack*)))
(kernel-log "CONTEXT - Popped: ~a" old)
old))
(defun context-resolve-path (path)
"Resolve PATH relative to the current context if it's a directory, otherwise return as is."
(let ((current (car *context-stack*)))
(if (and current (stringp current) (uiop:directory-pathname-p current))
(merge-pathnames path current)
path)))

View File

@@ -0,0 +1,79 @@
(defvar *cron-registry* nil)
(defun cron-register (name schedule-fn action-fn)
"Register a new cron task."
(push (list :name name :schedule schedule-fn :action action-fn :last-run 0) *cron-registry*))
(defun cron-trigger-loop ()
"Iterate through registered tasks and trigger those whose schedule matches."
(dolist (task *cron-registry*)
(let ((name (getf task :name))
(schedule (getf task :schedule))
(action (getf task :action)))
(when (funcall schedule)
(kernel-log "CRON - Triggering task: ~a" name)
(funcall action)
(setf (getf task :last-run) (get-universal-time))))))
(defun trigger-skill-cron (context)
(let ((type (getf context :type))
(payload (getf context :payload)))
(when (and (eq type :EVENT) (eq (getf payload :sensor) :heartbeat))
(cron-trigger-loop)
(trigger-nightly-grooming)
t)))
(defun parse-org-timestamp (ts-str)
(let ((match (nth-value 1 (cl-ppcre:scan-to-strings "<(\\d{4})-(\\d{2})-(\\d{2}).*>" ts-str))))
(if match
(encode-universal-time 0 0 0
(parse-integer (aref match 2))
(parse-integer (aref match 1))
(parse-integer (aref match 0)))
nil)))
(defun trigger-nightly-grooming ()
"Checks if the current time is within the nightly grooming window (e.g., 3:00 AM - 4:00 AM)."
(let* ((now (local-time:now))
(hour (local-time:timestamp-hour now)))
(when (= hour 3)
(kernel-log "CRON - Initiating Nightly Grooming Cycle...")
(org-agent:inject-stimulus `(:type :EVENT :payload (:sensor :grooming-cycle))))))
(defun context-get-upcoming-deadlines (&optional (days 3))
(let* ((now (get-universal-time))
(future-limit (+ now (* days 24 60 60)))
(all-headlines (org-agent:list-objects-by-type :HEADLINE))
(upcoming nil))
(dolist (obj all-headlines)
(let* ((attrs (org-agent:org-object-attributes obj))
(deadline-str (getf attrs :DEADLINE))
(deadline-time (when deadline-str (parse-org-timestamp deadline-str))))
(when (and deadline-time (< deadline-time future-limit) (> deadline-time (- now 86400)))
(push (list :title (getf attrs :TITLE) :deadline deadline-str) upcoming))))
upcoming))
(defun context-get-stalled-waiting-items (&optional (days 3))
(let* ((now (get-universal-time))
(past-limit (- now (* days 24 60 60)))
(all-headlines (org-agent:list-objects-by-type :HEADLINE))
(stalled nil))
(dolist (obj all-headlines)
(let* ((attrs (org-agent:org-object-attributes obj))
(state (getf attrs :TODO-STATE))
(last-sync (org-agent:org-object-last-sync obj)))
(when (and (equal state "WAITING") (< last-sync past-limit))
(push (list :title (getf attrs :TITLE)) stalled))))
stalled))
(defun neuro-skill-cron (context)
(let* ((upcoming (context-get-upcoming-deadlines 3))
(stalled (context-get-stalled-waiting-items 3))
(now-str (local-time:format-timestring nil (local-time:now))))
(format nil "
CURRENT TIME: ~a
UPCOMING DEADLINES (Next 3 Days): ~{~a: ~a~%~}
STALLED WAITING ITEMS (> 3 days old): ~{~a~%~}
" now-str
(loop for item in upcoming append (list (getf item :deadline) (getf item :title)))
(loop for item in stalled collect (getf item :title)))))

View File

@@ -0,0 +1,11 @@
(defun delegation-trigger (context)
"Examine CONTEXT to see if delegation is needed.
Criteria: Task complexity or explicit :delegate-to flag."
(let ((complexity (getf context :complexity 0))
(explicit-target (getf context :delegate-to)))
(or (> complexity 7) explicit-target)))
(defun delegation-actuate (task target)
"Dispatch TASK to TARGET. TARGET can be a sub-agent name or a skill keyword."
(kernel-log "DELEGATION - Actuating '~a' for task: ~a" target (getf task :title))
(org-agent:spawn-sub-agent :target target :task task))

View File

@@ -0,0 +1,20 @@
(defun get-embedding (text &key (provider :ollama))
"Retrieves the embedding vector for TEXT using specified PROVIDER."
(kernel-log "NEURO [Embedding] - Generating via ~a..." provider)
(case provider
(:ollama (get-embedding-ollama text))
(:gemini (get-embedding-gemini text))
(t (error "Unsupported embedding provider: ~a" provider))))
(defun get-embedding-ollama (text)
(let* ((url "http://localhost:11434/api/embeddings")
(payload (cl-json:encode-json-to-string `(("model" . "mxbai-embed-large") ("prompt" . ,text))))
(response (dex:post url :content payload :headers '(("Content-Type" . "application/json")))))
(cdr (assoc :embedding (cl-json:decode-json-from-string response)))))
(defun get-embedding-gemini (text)
(let* ((api-key (getf (org-agent:get-credentials :gemini) :api-key))
(url (format nil "https://generativelanguage.googleapis.com/v1beta/models/embedding-001:embedContent?key=~a" api-key))
(payload (cl-json:encode-json-to-string `(("content" . (("parts" . ((("text" . ,text))))))))))
(let ((response (dex:post url :content payload :headers '(("Content-Type" . "application/json")))))
(cdr (assoc :values (cdr (assoc :embedding (cl-json:decode-json-from-string response))))))))

View File

@@ -0,0 +1,14 @@
(defun git-status ()
"Executes git status and returns the output."
(uiop:run-program '("git" "status" "--short") :output :string))
(defun git-commit (message)
"Stages all tracked changes and commits them."
(kernel-log "GIT - Committing: ~a" message)
(uiop:run-program '("git" "add" "-u"))
(uiop:run-program `("git" "commit" "-m" ,message)))
(defun git-push ()
"Pushes to the current branch origin."
(kernel-log "GIT - Pushing to origin...")
(uiop:run-program '("git" "push")))

View File

@@ -0,0 +1,17 @@
(defun log-scan (&optional (lines 100))
"Reads the last LINES lines of the system log file."
(let ((log-file (merge-pathnames "logs/agent.log" (uiop:getenv "SYSTEM_DIR"))))
(if (uiop:file-exists-p log-file)
(uiop:run-program `("tail" "-n" ,(write-to-string lines) ,(namestring log-file)) :output :string)
"Log file not found.")))
(defun log-summarize (logs)
"Symbolic summary of LOGS focusing on errors and warnings."
(let ((lines (uiop:split-string logs :separator '(#\Newline)))
(errors 0)
(warnings 0))
(dolist (line lines)
(cond
((cl-ppcre:scan "ERROR" line) (incf errors))
((cl-ppcre:scan "WARN" line) (incf warnings))))
(format nil "Log Summary: ~a errors, ~a warnings found in scan." errors warnings)))

View File

@@ -0,0 +1,21 @@
(defun execute-gemini-request (prompt system-prompt)
(let* ((auth (org-agent:get-provider-auth :gemini))
(api-key (getf auth :api-key))
(bearer-token (getf auth :bearer-token))
(endpoint (or (getf auth :endpoint)
"https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent")))
(unless (or api-key bearer-token)
(return-from execute-gemini-request "(:type :LOG :payload (:text \"Authentication missing for Gemini\"))"))
(let* ((url (if api-key (format nil "~a?key=~a" endpoint api-key) endpoint))
(headers `(("Content-Type" . "application/json")
,@(when bearer-token `(("Authorization" . ,(format nil "Bearer ~a" bearer-token))))))
(body (cl-json:encode-json-to-string
`((contents . ((parts . ((text . ,(format nil "~a~%~%Prompt: ~a" system-prompt prompt))))))))))
(handler-case
(let* ((response (dex:post url :headers headers :content body :connect-timeout 10 :read-timeout 30))
(json (cl-json:decode-json-from-string response)))
(cdr (assoc :text (cdr (assoc :parts (car (cdr (assoc :parts (car (cdr (assoc :candidates json)))))))))))
(error (c)
(format nil "(:type :LOG :payload (:text \"Neural Engine Failure: ~a\"))" c))))))

View File

@@ -0,0 +1,32 @@
(defun get-openrouter-tiered-model (tier)
(case tier
(:powerful "anthropic/claude-3.5-sonnet")
(:fast "google/gemini-2.0-flash-001")
(:free "openrouter/auto")
(t "openrouter/auto")))
(defun execute-openrouter-request (prompt system-prompt)
(let ((api-key (uiop:getenv "OPENROUTER_API_KEY"))
(endpoint "https://openrouter.ai/api/v1/chat/completions"))
(unless api-key
(return-from execute-openrouter-request
"(:type :LOG :payload (:text \"OpenRouter API Key missing in environment\"))"))
(let* ((model (get-openrouter-tiered-model :fast))
(headers `(("Content-Type" . "application/json")
("Authorization" . ,(format nil "Bearer ~a" api-key))
("HTTP-Referer" . "https://github.com/amr/org-agent")
("X-Title" . "org-agent Sovereign Kernel")))
(body (cl-json:encode-json-to-string
`((model . ,model)
(messages . (( (role . "system") (content . ,system-prompt) )
( (role . "user") (content . ,prompt) )))))))
(handler-case
(let* ((response (dex:post endpoint :headers headers :content body :connect-timeout 10 :read-timeout 30))
(json (cl-json:decode-json-from-string response)))
;; Extract content from OpenAI-style response: choices[0].message.content
(cdr (assoc :content (cdr (assoc :message (car (cdr (assoc :choices json))))))))
(error (c)
(format nil "(:type :LOG :payload (:text \"OpenRouter Error: ~a\"))" c))))))

View File

@@ -0,0 +1,19 @@
(defun scientist-hypothesis (context)
"Neural stage: Formulates a hypothesis about a failure based on logs."
(let* ((payload (getf context :payload))
(failure-log (getf payload :text))
(project (getf payload :project)))
(org-agent:ask-neuro
(format nil "Project ~a failed with log: ~a. Formulate a 'Theory of Failure' and suggest a surgical fix." project failure-log)
:system-prompt "You are a PSF Senior Debugging Scientist. Return a Lisp plist: (:target :scientist :action :propose :hypothesis \"...\" :failure-log \"...\")")))
(defun scientist-propose-fix (action context)
"Symbolic stage: Triggers the Self-Fix agent with the formulated hypothesis."
(declare (ignore context))
(let* ((payload (getf action :payload))
(hypothesis (getf payload :hypothesis))
(failure-log (getf payload :failure-log)))
(org-agent:kernel-log "SCIENTIST - Hypothesis formulated. Triggering SELF-FIX...")
(org-agent:inject-stimulus
`(:type :EVENT :payload (:sensor :repair-request :hypothesis ,hypothesis :failure-log ,failure-log)))
(format nil "SUCCESS - Scientist proposed fix for failure.")))

View File

@@ -0,0 +1,42 @@
(defun self-fix-replace-all (string part replacement)
(with-output-to-string (out)
(loop with part-length = (length part)
for old-pos = 0 then (+ pos part-length)
for pos = (search part string :start2 old-pos)
do (write-string string out :start old-pos :end (or pos (length string)))
when pos do (write-string replacement out)
while pos)))
(defun self-fix-apply (action context)
"Applies a surgical code fix directly to the target file."
(declare (ignore context))
(let* ((payload (getf action :payload))
(target-file (getf payload :file))
(old-code (getf payload :old))
(new-code (getf payload :new)))
(org-agent:kernel-log "SELF-FIX - Attempting surgical fix on ~a..." target-file)
(if (uiop:file-exists-p target-file)
(let ((content (uiop:read-file-string target-file)))
(if (search old-code content)
(let ((new-content (self-fix-replace-all content old-code new-code)))
(with-open-file (out target-file :direction :output :if-exists :supersede)
(write-string new-content out))
(org-agent:kernel-log "SELF-FIX SUCCESS - Applied fix to ~a" target-file)
t)
(progn
(org-agent:kernel-log "SELF-FIX FAILURE - Could not find old code in ~a" target-file)
nil)))
(progn
(org-agent:kernel-log "SELF-FIX FAILURE - File not found: ~a" target-file)
nil))))
(defun neuro-skill-self-fix (context)
"Neural stage: Synthesizes a surgical code modification based on the hypothesis."
(let* ((payload (getf context :payload))
(hypothesis (getf payload :hypothesis))
(failure-log (getf payload :failure-log)))
(org-agent:ask-neuro
(format nil "Based on the hypothesis '~a' and failure '~a', provide the exact Lisp code to fix it.
Return a Lisp plist: (:target :self-fix :action :apply :file \"path/to/file.lisp\" :old \"old code\" :new \"new code\")"
hypothesis failure-log)
:system-prompt "You are the PSF Repair Actuator. You MUST return ONLY a Lisp plist.")))

View File

@@ -0,0 +1,17 @@
(defun run-tests-for-project (project-name)
"Executes the standard test suite for the given project using SBCL."
(let* ((projects-dir (or (uiop:getenv "PROJECTS_DIR") "projects/"))
(project-dir (format nil "~aorg-skill-~a/" projects-dir project-name))
(test-file (format nil "~atests/test-suite.lisp" project-dir)))
(org-agent:kernel-log "CI - Running tests for ~a..." project-name)
(if (uiop:file-exists-p test-file)
(multiple-value-bind (output error-output exit-code)
(uiop:run-program (list "sbcl" "--batch" "--load" test-file "--eval" "(uiop:quit)")
:ignore-error-status t :output :string :error-output :string)
(if (= exit-code 0)
(org-agent:kernel-log "CI SUCCESS - ~a passed all tests." project-name)
(progn
(org-agent:kernel-log "CI FAILURE - ~a failed tests with exit code ~a" project-name exit-code)
(org-agent:inject-stimulus
`(:type :EVENT :payload (:sensor :test-failure :project ,project-name :text ,output :stderr ,error-output))))))
(org-agent:kernel-log "CI ERROR - No test suite found for ~a at ~a" project-name test-file))))

View File

@@ -1,19 +1,3 @@
;;;; analyst-logic.lisp --- TDD automation for the PSF (Unified).
;;;; This file is TANGLED from notes/org-skill-tech-analyst.org. DO NOT EDIT MANUALLY.
(defpackage :org-skill-tech-analyst
(:use :cl :uiop :local-time)
(:export #:tech-analyst-perceive-signed-protocol
#:tech-analyst-scan-all-notes
#:trigger-skill-tech-analyst
#:neuro-skill-tech-analyst
#:tech-analyst-actuate))
(in-package :org-skill-tech-analyst)
(defun kernel-log (message &rest args)
(format t "~&[ANALYST] ~?" message args))
(defun tech-analyst-perceive-signed-protocol (note-path)
"Checks if a master note has a SIGNED PROTOCOL and lacks a TDD suite in the material project."
(let* ((content (uiop:read-file-string note-path))
@@ -76,7 +60,7 @@
(test-dir (format nil "~atests/" project-dir))
(test-path (format nil "~atests/test-suite.lisp" project-dir)))
(kernel-log "Actuating TDD Suite for ~a" project-name)
(org-agent:kernel-log "ANALYST - Actuating TDD Suite for ~a" project-name)
(ensure-directories-exist test-dir)
(with-open-file (out test-path :direction :output :if-exists :supersede)
(format out ";;; TDD Suite for ~a~%~a" project-name test-content))