Files
passepartout/org/system-diagnostics.org
Amr Gharbeia 791a0f9c3b
Some checks failed
Deploy (Gitea) / deploy (push) Failing after 3s
passepartout: v0.4.2 Structured Output
- json-alist-to-plist: JSON alist-to-keyword-plist converter (core-loop-reason)
- provider-openai-request: accept :tools parameter, build tool definitions
  in request body, parse tool_calls from response (system-model-provider)
- think(): build tools from cognitive-tool-registry, pass to backend cascade,
  handle :tool-calls response via json-alist-to-plist (core-loop-reason)
- backend-cascade-call: accept and propagate :tools parameter
- Diagnostics: remove nc/socat from required binaries — health check passes
- Version: 0.4.0 -> 0.4.2 across handshake, ASDF, README badge
2026-05-07 17:39:08 -04:00

12 KiB

SKILL: Diagnostics (org-skill-diagnostics.org)

Why a Doctor?

The Diagnostics skill is the self-knowledge of Passepartout. It answers "Is everything working?" by checking dependencies, environment variables, and LLM connectivity. Unlike the harness-level Doctor (which runs at boot and on CLI demand), this skill provides the Lisp-level diagnostic functions — defining what "healthy" means: which binaries must be present, which directories must exist, which API keys should be configured.

Phase A: Demand (Thinking)

Why a Doctor?

The Doctor transforms opaque startup failures into actionable engineering reports. It ensures the Brain never attempts to boot in a compromised state.

Detection Invariant

Binary detection must use shell probing (`which`) to account for varying `$PATH` inheritance between interactive and headless sessions.

Phase B: Contract

  1. (diagnostics-dependencies-check): probes PATH for every binary in *diagnostics-binaries*. Returns T if all found, NIL if any missing. Side-effect: populates *doctor-missing-deps*.
  2. (diagnostics-env-check): validates XDG directories exist. Returns T if all critical dirs present, NIL otherwise.
  3. (diagnostics-run-all &key auto-install): orchestrates 1-3. Returns a plist with :deps, :env, :llm keys. Respects :auto-install nil.

Phase C: Implementation (Build)

Package Context

(in-package :passepartout)

Global Configuration

;; REPL-VERIFIED: 2026-05-03T13:00:00

(defvar *diagnostics-binaries* '("sbcl" "emacs" "git")
  "List of external binaries required for full system operation.")

diagnostics-package-map

;; REPL-VERIFIED: 2026-05-03T13:00:00

(defvar *diagnostics-package-map*
  '(("sbcl" . "sbcl")
    ("emacs" . "emacs")
    ("git" . "git")
    ("curl" . "curl")
    ("rlwrap" . "rlwrap"))
  "Map binary names to apt package names.")

doctor-missing-deps

;; REPL-VERIFIED: 2026-05-03T13:00:00

(defvar *doctor-missing-deps* nil
  "List of missing dependencies populated by diagnostics-dependencies-check.")

doctor-auto-install

;; REPL-VERIFIED: 2026-05-03T13:00:00

(defvar *doctor-auto-install* t
  "When T, doctor will attempt to install missing dependencies automatically.")

#+end_src

Dependency Verification

;; REPL-VERIFIED: 2026-05-03T13:00:00

(defun diagnostics-dependencies-check ()
  "Verifies that required external binaries are available in the PATH via shell probe."
  (setf *doctor-missing-deps* nil)
  (let ((all-ok t))
    (format t "DOCTOR: Checking system dependencies...~%")
    (dolist (dep *diagnostics-binaries*)
      (let ((path (ignore-errors
                    (uiop:run-program (list "which" dep)
                                      :output :string :ignore-error-status t))))
        (if (and path (> (length path) 0))
            (format t "  [OK] Found ~a~%" dep)
            (progn
              (format t "  [FAIL] Missing binary: ~a~%" dep)
              (push dep *doctor-missing-deps*)
              (setf all-ok nil)))))
    (when (and all-ok (null *doctor-missing-deps*))
      (format t "DOCTOR: All dependencies satisfied.~%"))
    all-ok))

Auto-Install Dependencies

;; REPL-VERIFIED: 2026-05-03T13:00:00

(defun diagnostics-dependencies-install ()
  "Attempts to install missing system dependencies via apt."
  (when (null *doctor-missing-deps*)
    (format t "DOCTOR: No missing dependencies to install.~%")
    (return-from diagnostics-dependencies-install t))

  (format t "DOCTOR: Attempting to install ~a missing dependencies...~%" (length *doctor-missing-deps*))

  (let ((packages (remove-duplicates
                   (mapcar (lambda (dep)
                             (or (cdr (assoc dep *diagnostics-package-map* :test #'string=))
                                 dep))
                           *doctor-missing-deps*)
                   :test #'string=)))
    (format t "DOCTOR: Packages to install: ~a~%" packages)

    (let ((cmd (format nil "apt-get install -y ~{~a~^ ~}" packages)))
      (format t "DOCTOR: Running: ~a~%" cmd)
      (handler-case
          (let ((output (uiop:run-program cmd
                                           :output :string
                                           :error-output :string
                                           :external-format :utf-8)))
            (if (zerop (uiop:run-program (format nil "which ~a" (car *doctor-missing-deps*))
                                          :ignore-error-status t))
                (progn
                  (format t "DOCTOR: Dependencies installed successfully.~%")
                  (setf *doctor-missing-deps* nil)
                  t)
                (progn
                  (format t "DOCTOR: Installation failed. Output: ~a~%" output)
                  nil)))
        (error (c)
          (format t "DOCTOR: Installation error: ~a~%" c)
          nil)))))

XDG Environment Validation

;; REPL-VERIFIED: 2026-05-03T13:00:00

(defun diagnostics-env-check ()
  "Validates XDG directories and environment configuration."
  (format t "DOCTOR: Checking XDG environment...~%")
  (let ((all-ok t)
        (config-dir (uiop:getenv "PASSEPARTOUT_CONFIG_DIR"))
        (data-dir (uiop:getenv "PASSEPARTOUT_DATA_DIR"))
        (state-dir (uiop:getenv "PASSEPARTOUT_STATE_DIR"))
        (memex-dir (uiop:getenv "MEMEX_DIR")))

    (flet ((check-dir (name path critical)
             (if (and path (> (length path) 0))
                 (if (uiop:directory-exists-p path)
                     (format t "  [OK] ~a: ~a~%" name path)
                     (progn
                       (format t "  [FAIL] ~a directory missing: ~a~%" name path)
                       (when critical (setf all-ok nil))))
                 (progn
                   (format t "  [FAIL] ~a variable not set.~%" name)
                   (when critical (setf all-ok nil))))))

      (check-dir "Config (PASSEPARTOUT_CONFIG_DIR)" config-dir t)
      (check-dir "Data (PASSEPARTOUT_DATA_DIR)" data-dir t)
      (check-dir "State (PASSEPARTOUT_STATE_DIR)" state-dir t)
      (check-dir "Memex (MEMEX_DIR)" memex-dir t))
    all-ok))

LLM Connectivity

The doctor checks all supported LLM providers and detects local Ollama instances.

;; REPL-VERIFIED: 2026-05-03T13:00:00

(defun diagnostics-llm-check ()
  "Tests connectivity to LLM providers. Returns T if at least one provider is configured."
  (format t "DOCTOR: Checking LLM connectivity...~%")
  (let ((providers '((:openrouter . "OPENROUTER_API_KEY")
                     (:anthropic . "ANTHROPIC_API_KEY")
                     (:openai . "OPENAI_API_KEY")
                     (:groq . "GROQ_API_KEY")
                     (:gemini . "GEMINI_API_KEY")
                     (:deepseek . "DEEPSEEK_API_KEY")
                     (:nvidia . "NVIDIA_API_KEY")
                     (:ollama . "OLLAMA_URL")))
        (configured nil))
    (dolist (p providers)
      (let ((env-val (uiop:getenv (cdr p))))
        (cond
          ((and env-val (> (length env-val) 0))
           (format t "  [OK] ~a configured~%" (car p))
           (setf configured t))
          ((eq (car p) :ollama)
           (let ((ollama-check (ignore-errors
                                 (uiop:run-program '("curl" "-s" "http://localhost:11434/api/tags")
                                                    :output :string :ignore-error-status t))))
             (when (and ollama-check (search "\"models\"" ollama-check))
               (format t "  [OK] Ollama local model server detected~%")
               (setf configured t)))))))
    (if configured
        (progn
          (format t "  [OK] LLM provider(s) available~%")
          t)
        (progn
          (format t "  [WARN] No LLM provider configured.~%")
          (format t "  Run 'passepartout configure' to configure a provider.~%")
          t))))

Orchestration

;; REPL-VERIFIED: 2026-05-03T13:00:00

(defun diagnostics-run-all (&key (auto-install t))
  "Executes the full diagnostic suite and returns T if system is healthy."
  (format t "==================================================~%")
  (format t " PASSEPARTOUT DOCTOR: Commencing Health Check~%")
  (format t "==================================================~%")
  (let ((dep-ok (diagnostics-dependencies-check)))
    (when (and (not dep-ok) auto-install *doctor-auto-install*)
      (format t "DOCTOR: Attempting automatic installation...~%")
      (setf dep-ok (diagnostics-dependencies-install))
      (when dep-ok
        (setf dep-ok (diagnostics-dependencies-check))))
    (let ((env-ok (diagnostics-env-check))
          (llm-ok (diagnostics-llm-check)))
      (format t "==================================================~%")
      (if (and dep-ok env-ok)
          (progn
            (format t " ✓ SYSTEM HEALTHY: Ready for ignition.~%")
            t)  ;; Explicitly return T
          (progn
            (format t "==================================================~%")
            (format t " ISSUES FOUND:~%")
            (when (not dep-ok)
              (format t "  - Missing system dependencies~%"))
            (when (not llm-ok)
              (format t "  - No LLM provider configured~%"))
            (format t "~%")
            (format t " RECOMMENDED ACTIONS:~%")
            (format t "  1. Run 'passepartout configure' to configure everything~%")
            (format t "  2. Or run 'passepartout doctor --fix' for auto-repair~%")
            (format t "==================================================~%")
            nil)))))  ;; Return nil when issues found

CLI Entry Point

;; REPL-VERIFIED: 2026-05-03T13:00:00

(defun diagnostics-main ()
  "Entry point for the 'doctor' CLI command."
  (if (diagnostics-run-all)
      (uiop:quit 0)
      (uiop:quit 1)))

Phase D: Verification (Testing)

(eval-when (:compile-toplevel :load-toplevel :execute)
  (ql:quickload :fiveam :silent t))

(defpackage :passepartout-diagnostics-tests
  (:use :cl :fiveam :passepartout)
  (:export #:diagnostics-suite))

(in-package :passepartout-diagnostics-tests)

(def-suite diagnostics-suite :description "Verification of the System Diagnostics logic")
(in-suite diagnostics-suite)

(test test-diagnostics-dependency-fail
  "Contract 1: missing binaries cause diagnostics-dependencies-check to return nil."
  (let* ((pkg (find-package "PASSEPARTOUT.SKILLS.SYSTEM-DIAGNOSTICS"))
         (bin-var (and pkg (find-symbol "*DIAGNOSTICS-BINARIES*" pkg))))
    (when bin-var
      (setf (symbol-value bin-var) '("non-existent-binary-123"))
      (is (null (diagnostics-dependencies-check))))))

(test test-diagnostics-env-fail
  "Contract 2: diagnostics-env-check returns a boolean."
  (let ((result (diagnostics-env-check)))
    (is (or (eq t result) (eq nil result))
        "diagnostics-env-check should return T or NIL")))

(test test-diagnostics-dependency-success
  "Contract 1: all binaries present returns T."
  (let* ((pkg (find-package "PASSEPARTOUT.SKILLS.SYSTEM-DIAGNOSTICS"))
         (bin-var (and pkg (find-symbol "*DIAGNOSTICS-BINARIES*" pkg))))
    (when bin-var
      (setf (symbol-value bin-var) '("ls"))
      (is (eq t (diagnostics-dependencies-check))))))

Phase E: Lifecycle

The doctor skill should be loaded early (priority 100) to validate system health before other skills initialize.

Skill Registration

(defskill :passepartout-system-diagnostics
  :priority 100
  :trigger (lambda (ctx) (eq (getf (getf ctx :payload) :sensor) :heartbeat))
  :deterministic (lambda (action ctx) (declare (ignore action ctx)) nil))