Proactive doctor, setup wizard, and TUI fixes
Some checks failed
Deploy-Agent-V15-Stdin / JOB-V15-STDIN (push) Failing after 3s

BREAKING CHANGES / KNOWN ISSUES:
- 8 skills have syntax errors causing loader warnings:
  org-skill-bouncer, org-skill-config-manager, org-skill-credentials-vault,
  org-skill-engineering-standards, org-skill-gardener, org-skill-homoiconic-memory,
  org-skill-peripheral-vision, org-skill-policy
- These skills fail to load but don't block system operation
- TUI works despite these errors

FEATURES ADDED:

1. Proactive Doctor System
   - Doctor runs automatically on daemon startup
   - Health check runs before accepting connections
   - Adds /health endpoint for health status queries
   - *system-health* variable tracks: :healthy, :degraded, :unhealthy, :unknown

2. Error Handling (Option B - Debugger Hook)
   - TUI and CLI now run doctor diagnostics on errors
   - Shows "Run opencortex doctor" message on crash
   - Suggests repair commands after failures

3. Interactive Setup Wizard (org-skill-config-manager)
   - Full wizard implemented in config-manager skill:
     * LLM provider configuration (OpenAI, Anthropic, OpenRouter, Groq, Gemini, Ollama)
     * Gateway linking (Slack, Discord)
     * Memory settings (auto-save interval, history retention)
     * Network settings (timeout, proxy)
   - Saves to ~/.config/opencortex/.env (KEY=VALUE format)
   - CLI integration: opencortex setup, setup --add-provider, setup --link

4. CLI Enhancements
   - doctor --watch: Background health monitoring (60s interval)
   - doctor --fix: Interactive repair (falls back to full setup if core files missing)
   - setup command runs wizard or delegates to setup_system

5. TUI Fixes
   - Inlined message formatting to avoid dependency issues
   - Added error handling in handle-return
   - Cleaner error messages

6. Thin Harness Compliance
   - Removed doctor from harness (now in org-skill-diagnostics skill)
   - XDG directories: only .lisp in harness, .org kept in skills for loader
This commit is contained in:
2026-04-29 12:58:09 -04:00
parent 31d3a52aeb
commit c0d3f066e8
8 changed files with 805 additions and 102 deletions

View File

@@ -83,6 +83,17 @@ The ~communication.lisp~ module defines the low-level transport and framing logi
(cond
((eq msg :eof) (return))
((eq msg :error) (return))
((eq (getf msg :type) :health-check)
;; Handle health check request
(let ((health-msg (list :type :health-response
:status (or (and (boundp 'opencortex::*system-health*)
(symbol-value 'opencortex::*system-health*))
:unknown)
:checked-p (or (and (boundp 'opencortex::*health-check-ran*)
(symbol-value 'opencortex::*health-check-ran*))
nil))))
(format stream "~a" (frame-message health-msg))
(finish-output stream)))
(t (inject-stimulus msg :stream stream))))))
(error (c) (harness-log "CLIENT ERROR: ~a" c)))
(ignore-errors (usocket:socket-close socket))))

View File

@@ -96,6 +96,44 @@ The Metabolic Loop is the fundamental rhythm of OpenCortex: the continuous proce
(defvar *shutdown-save-enabled* t)
#+end_src
** Health Status
#+begin_src lisp
(defvar *system-health* :unknown
"Current system health status: :healthy, :degraded, :unhealthy, or :unknown.")
(defvar *health-check-ran* nil
"Flag indicating if initial health check has completed.")
#+end_src
** Proactive Doctor
#+begin_src lisp
(defun run-startup-health-check ()
"Runs the doctor diagnostics on startup. Returns health status."
(format t "~%")
(format t "==================================================~%")
(format t " DOCTOR: Running Startup Health Check~%")
(format t "==================================================~%")
(handler-case
(progn
(when (fboundp 'doctor-run-all)
(let ((result (doctor-run-all :auto-install nil)))
(setf *health-check-ran* t)
(if result
(progn
(setf *system-health* :healthy)
(format t "DAEMON: Health check passed. Starting services.~%"))
(progn
(setf *system-health* :degraded)
(format t "DAEMON: Health check found issues.~%")
(format t " Run 'opencortex doctor --fix' to repair.~%")))))
(setf *health-check-ran* t))
(error (c)
(format t "DOCTOR ERROR: ~a~%" c)
(setf *system-health* :unhealthy)
(setf *health-check-ran* t)))
(format t "==================================================~%~%"))
#+end_src
** Main Entry Point (main)
#+begin_src lisp
(defun main ()
@@ -108,16 +146,20 @@ The Metabolic Loop is the fundamental rhythm of OpenCortex: the continuous proce
(load-memory-from-disk)
(initialize-actuators)
(initialize-all-skills)
;; Run proactive doctor before starting services
(run-startup-health-check)
(start-heartbeat)
(start-daemon)
#+sbcl
(sb-sys:enable-interrupt sb-unix:sigint
(lambda (sig code scp)
(declare (ignore sig code scp))
(harness-log "SHUTDOWN: SIGINT received. Saving memory...")
(when *shutdown-save-enabled* (save-memory-to-disk))
(uiop:quit 0)))
(lambda (sig code scp)
(declare (ignore sig code scp))
(harness-log "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

View File

@@ -38,7 +38,7 @@ The OpenCortex TUI Client is a standalone Common Lisp application built on **Cro
#+begin_src lisp
(in-package :cl-user)
(defpackage :opencortex.tui
(:use :cl :croatoan)
(:use :cl :croatoan :usocket)
(:export :main))
(in-package :opencortex.tui)
#+end_src
@@ -108,13 +108,18 @@ The OpenCortex TUI Client is a standalone Common Lisp application built on **Cro
(when (> (length cmd) 0)
(enqueue-msg (format nil "⬆ ~a" cmd))
(handler-case
(when (and stream (open-stream-p stream))
(format stream "~a" (opencortex:frame-message (list :TYPE :EVENT
:META (list :SOURCE :tui)
:PAYLOAD (list :SENSOR :user-input :TEXT cmd))))
(finish-output stream))
(progn
(when (and stream (open-stream-p stream))
(let* ((msg (list :TYPE :EVENT
:META (list :SOURCE :tui)
:PAYLOAD (list :SENSOR :user-input :TEXT cmd)))
(payload (format nil "~s" msg))
(len (length payload)))
(format stream "~6,'0x~a" len payload)
(finish-output stream)))
(enqueue-msg "✓ Sent"))
(error (c)
(declare (ignore c))
(format t "Send error: ~a~%" c)
(enqueue-msg "ERROR: Connection to daemon lost.")
(setf *is-running* nil))))
(when (string= cmd "/exit") (setf *is-running* nil))