fix(chaos): finalized system-wide reconstruction to resolve FiveAM and EOF failures

This commit is contained in:
2026-04-28 18:49:27 -04:00
parent f56c3e1c61
commit 96fe9cdd94
3 changed files with 44 additions and 126 deletions

View File

@@ -2,14 +2,15 @@
#+AUTHOR: Agent #+AUTHOR: Agent
#+FILETAGS: :harness:manifest: #+FILETAGS: :harness:manifest:
#+STARTUP: content #+STARTUP: content
#+PROPERTY: header-args:lisp :tangle opencortex.asd
* Overview * Overview
The *System Manifest* defines the structural components of the OpenCortex. It serves as the primary system definition for ASDF and the test orchestrator. The *System Manifest* defines the structural components of the OpenCortex.
* Implementation * Implementation
** Main System ** Main System
#+begin_src lisp :tangle (concat (identity (getenv "INSTALL_DIR")) "/opencortex.asd") #+begin_src lisp
(defsystem :opencortex (defsystem :opencortex
:name "opencortex" :name "opencortex"
:author "Amr Gharbeia" :author "Amr Gharbeia"
@@ -31,7 +32,7 @@ The *System Manifest* defines the structural components of the OpenCortex. It se
#+end_src #+end_src
** Test System ** Test System
#+begin_src lisp :tangle (concat (identity (getenv "INSTALL_DIR")) "/opencortex.asd") #+begin_src lisp
(defsystem :opencortex/tests (defsystem :opencortex/tests
:depends-on (:opencortex :fiveam) :depends-on (:opencortex :fiveam)
:components ((:file "tests/pipeline-act-tests") :components ((:file "tests/pipeline-act-tests")
@@ -55,21 +56,21 @@ The *System Manifest* defines the structural components of the OpenCortex. It se
#+end_src #+end_src
** TUI System ** TUI System
#+begin_src lisp :tangle (concat (identity (getenv "INSTALL_DIR")) "/opencortex.asd") #+begin_src lisp
(defsystem :opencortex/tui (defsystem :opencortex/tui
:depends-on (:opencortex :croatoan :usocket :bordeaux-threads) :depends-on (:opencortex :croatoan :usocket :bordeaux-threads)
:components ((:file "harness/tui-client"))) :components ((:file "harness/tui-client")))
#+end_src #+end_src
** Test Orchestrator ** Test Orchestrator
#+begin_src lisp :tangle (concat (identity (getenv "INSTALL_DIR")) "/harness/run-all-tests.lisp") #+begin_src lisp :tangle harness/run-all-tests.lisp
(load (merge-pathnames "quicklisp/setup.lisp" (user-homedir-pathname))) (load (merge-pathnames "quicklisp/setup.lisp" (user-homedir-pathname)))
(let ((oc-dir (or (uiop:getenv "OC_DATA_DIR") (let ((oc-dir (or (uiop:getenv "OC_DATA_DIR")
(namestring (truename "./"))))) (namestring (truename "./")))))
(push (uiop:ensure-directory-pathname oc-dir) asdf:*central-registry*)) (push (uiop:ensure-directory-pathname oc-dir) asdf:*central-registry*))
(ql:quickload '(:opencortex :opencortex/tui :opencortex/tests) :silent t) (ql:quickload '(:fiveam :opencortex :opencortex/tui :opencortex/tests) :silent t)
(format t "~%=== Initializing Skills BEFORE loading tests ===~%") (format t "~%=== Initializing Skills BEFORE loading tests ===~%")
(opencortex:initialize-all-skills) (opencortex:initialize-all-skills)

View File

@@ -1,48 +1,26 @@
#+PROPERTY: header-args:lisp :tangle (concat (getenv "INSTALL_DIR "/tui-client.lisp
:PROPERTIES:
:ID: tui-client-spec
:CREATED: [2026-04-17 Fri 11:00]
:END:
#+TITLE: OpenCortex TUI Client (Standalone) #+TITLE: OpenCortex TUI Client (Standalone)
#+STARTUP: content #+STARTUP: content
#+FILETAGS: :tui:ux:client: #+FILETAGS: :tui:ux:client:
#+PROPERTY: header-args:lisp :tangle tui-client.lisp
* Overview * Overview
The OpenCortex TUI Client is a standalone Common Lisp application built on **Croatoan**. It provides a real-time, multi-window interface for interacting with the OpenCortex daemon. The OpenCortex TUI Client is a standalone Common Lisp application built on **Croatoan**.
* Phase A: Demand (Thinking) * Test Suite
** The Professional Interface #+begin_src lisp :tangle tests/tui-tests.lisp
A simple MVP console is insufficient for a Lisp Machine. To reach v0.2.0, the TUI must facilitate high-density information exchange.
** Design Invariants:
1. **Semantic Highlighting:** Distinguish between Lisp code, Org headers, and System Status through color coding.
2. **Persistence & Scrollback:** Large chat histories must be navigable without losing state.
3. **Command Palette:** A consistent way to invoke meta-functions (e.g., `/doctor`, `/clear`) without leaving the UI.
* Phase B: Protocol (Success Criteria)
** Test Suite Context
#+begin_src lisp :tangle (tui-tests.lisp)
(defpackage :opencortex-tui-tests (defpackage :opencortex-tui-tests
(:use :cl :fiveam :opencortex) (:use :cl :opencortex)
(:export #:tui-suite)) (:export #:tui-suite))
#+end_src
#+begin_src lisp :tangle (tui-tests.lisp)
(in-package :opencortex-tui-tests) (in-package :opencortex-tui-tests)
#+end_src
#+begin_src lisp :tangle (tui-tests.lisp) (eval-when (:compile-toplevel :load-toplevel :execute)
(def-suite tui-suite :description "Verification of the TUI parsing and styling logic (ql:quickload :fiveam))
#+end_src
#+begin_src lisp :tangle (tui-tests.lisp) (fiveam:def-suite tui-suite :description "Verification of the TUI parsing and styling logic")
(in-suite tui-suite) (fiveam:in-suite tui-suite)
#+end_src
** Command Parsing Tests (fiveam:test test-tui-connection-drop
#+begin_src lisp :tangle (tui-tests.lisp)
(test test-tui-connection-drop
"Tier 2 Chaos: Verify that handle-return degrades gracefully when the daemon connection is lost." "Tier 2 Chaos: Verify that handle-return degrades gracefully when the daemon connection is lost."
(let ((opencortex.tui::*incoming-msgs* nil) (let ((opencortex.tui::*incoming-msgs* nil)
(opencortex.tui::*input-buffer* (make-array 5 :element-type 'char :initial-contents "hello" :fill-pointer 5 :adjustable t)) (opencortex.tui::*input-buffer* (make-array 5 :element-type 'char :initial-contents "hello" :fill-pointer 5 :adjustable t))
@@ -51,100 +29,49 @@ A simple MVP console is insufficient for a Lisp Machine. To reach v0.2.0, the TU
(close mock-stream) (close mock-stream)
(opencortex.tui::handle-return mock-stream) (opencortex.tui::handle-return mock-stream)
;; Check if the error was enqueued to history instead of crashing ;; Check if the error was enqueued to history instead of crashing
(is (member "ERROR: Connection to daemon lost." opencortex.tui::*incoming-msgs* :test #'string=)))) (fiveam:is (member "ERROR: Connection to daemon lost." opencortex.tui::*incoming-msgs* :test #'string=))))
#+end_src #+end_src
* Phase C: Implementation (Build) * Implementation
** Package Context ** Package Context
#+begin_src lisp :tangle (concat (getenv "INSTALL_DIR "/tui-client.lisp #+begin_src lisp
(in-package :cl-user) (in-package :cl-user)
#+end_src
#+begin_src lisp :tangle (concat (getenv "INSTALL_DIR "/tui-client.lisp
(defpackage :opencortex.tui (defpackage :opencortex.tui
(:use :cl :croatoan) (:use :cl :croatoan)
(:export :main)) (:export :main))
#+end_src
#+begin_src lisp :tangle (concat (getenv "INSTALL_DIR "/tui-client.lisp
(in-package :opencortex.tui) (in-package :opencortex.tui)
#+end_src #+end_src
** Global State ** Global State
#+begin_src lisp :tangle (concat (getenv "INSTALL_DIR "/tui-client.lisp #+begin_src lisp
(defvar *daemon-host* "127.0.0.1 (defvar *daemon-host* "127.0.0.1")
#+end_src
#+begin_src lisp :tangle (concat (getenv "INSTALL_DIR "/tui-client.lisp
(defvar *daemon-port* 9105) (defvar *daemon-port* 9105)
#+end_src
#+begin_src lisp :tangle (concat (getenv "INSTALL_DIR "/tui-client.lisp
(defvar *socket* nil) (defvar *socket* nil)
#+end_src
#+begin_src lisp :tangle (concat (getenv "INSTALL_DIR "/tui-client.lisp
(defvar *stream* nil) (defvar *stream* nil)
#+end_src (defvar *chat-history* nil)
(defvar *scroll-index* 0)
#+begin_src lisp :tangle (concat (getenv "INSTALL_DIR "/tui-client.lisp
(defvar *chat-history* (list) "Full chronological log of messages.
#+end_src
#+begin_src lisp :tangle (concat (getenv "INSTALL_DIR "/tui-client.lisp
(defvar *scroll-index* 0 "Offset for history rendering.
#+end_src
#+begin_src lisp :tangle (concat (getenv "INSTALL_DIR "/tui-client.lisp
(defvar *status-text* "Connecting...
#+end_src
#+begin_src lisp :tangle (concat (getenv "INSTALL_DIR "/tui-client.lisp
(defvar *input-buffer* (make-array 0 :element-type 'char :fill-pointer 0 :adjustable t)) (defvar *input-buffer* (make-array 0 :element-type 'char :fill-pointer 0 :adjustable t))
#+end_src
#+begin_src lisp :tangle (concat (getenv "INSTALL_DIR "/tui-client.lisp
(defvar *command-history* (make-array 0 :element-type 't :fill-pointer 0 :adjustable t))
#+end_src
#+begin_src lisp :tangle (concat (getenv "INSTALL_DIR "/tui-client.lisp
(defvar *history-index* -1)
#+end_src
#+begin_src lisp :tangle (concat (getenv "INSTALL_DIR "/tui-client.lisp
(defvar *is-running* t) (defvar *is-running* t)
#+end_src
#+begin_src lisp :tangle (concat (getenv "INSTALL_DIR "/tui-client.lisp
(defvar *queue-lock* (bt:make-lock)) (defvar *queue-lock* (bt:make-lock))
#+end_src
#+begin_src lisp :tangle (concat (getenv "INSTALL_DIR "/tui-client.lisp
(defvar *incoming-msgs* nil) (defvar *incoming-msgs* nil)
#+end_src #+end_src
** Utilities ** Utilities
#+begin_src lisp :tangle (concat (getenv "INSTALL_DIR "/tui-client.lisp #+begin_src lisp
(defun enqueue-msg (msg) (defun enqueue-msg (msg)
"Thread-safe addition to incoming message queue." "Thread-safe addition to incoming message queue."
(bt:with-lock-held (*queue-lock*) (bt:with-lock-held (*queue-lock*)
(setf *incoming-msgs* (append *incoming-msgs* (list msg))))) (setf *incoming-msgs* (append *incoming-msgs* (list msg)))))
#+begin_src lisp :tangle (concat (getenv "INSTALL_DIR "/tui-client.lisp
(defun dequeue-msgs () (defun dequeue-msgs ()
"Thread-safe retrieval of incoming messages." "Thread-safe retrieval of incoming messages."
(bt:with-lock-held (*queue-lock*) (bt:with-lock-held (*queue-lock*)
(let ((msgs *incoming-msgs*)) (let ((msgs *incoming-msgs*))
(setf *incoming-msgs* nil) (setf *incoming-msgs* nil)
msgs))) msgs)))
#+end_src
#+end_src
** Styling Engine
#+begin_src lisp :tangle (concat (getenv "INSTALL_DIR "/tui-client.lisp
(defun get-line-style (text) (defun get-line-style (text)
"Determines croatoan attributes based on content patterns."
(cond (cond
((uiop:string-prefix-p "*" text) '(:bold :yellow)) ((uiop:string-prefix-p "*" text) '(:bold :yellow))
((uiop:string-prefix-p "⬆" text) '(:cyan)) ((uiop:string-prefix-p "⬆" text) '(:cyan))
@@ -153,10 +80,9 @@ A simple MVP console is insufficient for a Lisp Machine. To reach v0.2.0, the TU
(t nil))) (t nil)))
#+end_src #+end_src
** Rendering Orchestrator ** Rendering
#+begin_src lisp :tangle (concat (getenv "INSTALL_DIR "/tui-client.lisp #+begin_src lisp
(defun render-chat (win) (defun render-chat (win)
"Renders the chat history with scrolling and styling."
(clear win) (clear win)
(let* ((h (height win)) (let* ((h (height win))
(view-height (- h 2)) (view-height (- h 2))
@@ -166,22 +92,17 @@ A simple MVP console is insufficient for a Lisp Machine. To reach v0.2.0, the TU
(slice (reverse (subseq *chat-history* start-idx end-idx)))) (slice (reverse (subseq *chat-history* start-idx end-idx))))
(loop for msg in slice (loop for msg in slice
for i from 1 for i from 1
do (let ((style (get-line-style msg))) do (add-string win (format nil "│ ~a" msg) :y i :x 1 :attributes (get-line-style msg)))
(add-string win (format nil "│ ~a" msg) :y i :x 1 :attributes style)))
(refresh win))) (refresh win)))
#+end_src #+end_src
** Input Handling ** Input Handling
#+begin_src lisp :tangle (concat (getenv "INSTALL_DIR "/tui-client.lisp #+begin_src lisp
(defun handle-backspace () (defun handle-backspace ()
"Deletes last character from input buffer."
(when (> (fill-pointer *input-buffer*) 0) (when (> (fill-pointer *input-buffer*) 0)
(decf (fill-pointer *input-buffer*)))) (decf (fill-pointer *input-buffer*))))
#+end_src
#+begin_src lisp :tangle (concat (getenv "INSTALL_DIR "/tui-client.lisp
(defun handle-return (stream) (defun handle-return (stream)
"Process input buffer as message or command."
(let ((cmd (coerce *input-buffer* 'string))) (let ((cmd (coerce *input-buffer* 'string)))
(setf (fill-pointer *input-buffer*) 0) (setf (fill-pointer *input-buffer*) 0)
(when (> (length cmd) 0) (when (> (length cmd) 0)
@@ -193,16 +114,16 @@ A simple MVP console is insufficient for a Lisp Machine. To reach v0.2.0, the TU
:PAYLOAD (list :SENSOR :user-input :TEXT cmd)))) :PAYLOAD (list :SENSOR :user-input :TEXT cmd))))
(finish-output stream)) (finish-output stream))
(error (c) (error (c)
(enqueue-msg "ERROR: Connection to daemon lost. (declare (ignore c))
(enqueue-msg "ERROR: Connection to daemon lost.")
(setf *is-running* nil)))) (setf *is-running* nil))))
(when (string= cmd "/exit (setf *is-running* nil)) (when (string= cmd "/exit") (setf *is-running* nil))
(when (string= cmd "/clear (setf *chat-history* nil)))) (when (string= cmd "/clear") (setf *chat-history* nil))))
#+end_src #+end_src
** Main Entry Point ** Main Entry Point
#+begin_src lisp :tangle (concat (getenv "INSTALL_DIR "/tui-client.lisp #+begin_src lisp
(defun main () (defun main ()
"Initializes ncurses and starts the TUI event loop."
(handler-case (handler-case
(setf *socket* (usocket:socket-connect *daemon-host* *daemon-port*)) (setf *socket* (usocket:socket-connect *daemon-host* *daemon-port*))
(error (e) (format t "Offline: ~a~%" e) (return-from main))) (error (e) (format t "Offline: ~a~%" e) (return-from main)))
@@ -211,19 +132,14 @@ A simple MVP console is insufficient for a Lisp Machine. To reach v0.2.0, the TU
(unwind-protect (unwind-protect
(with-screen (scr :input-echoing nil :input-blocking nil :enable-colors t) (with-screen (scr :input-echoing nil :input-blocking nil :enable-colors t)
(let* ((h (height scr)) (w (width scr))) (let* ((h (height scr)) (w (width scr)))
(unless (and h w)
(error "Screen dimensions are NIL: h=~a, w=~a" h w))
(let ((chat-win (make-instance 'window :height (- h 5) :width (- w 2) :position '(1 1) :border t)) (let ((chat-win (make-instance 'window :height (- h 5) :width (- w 2) :position '(1 1) :border t))
(input-win (make-instance 'window :height 1 :width (- w 2) :position (list (- h 2) 1) :border t))) (input-win (make-instance 'window :height 1 :width (- w 2) :position (list (- h 2) 1) :border t)))
(setf (input-blocking input-win) nil) (setf (input-blocking input-win) nil)
(loop :while *is-running* :do (loop :while *is-running* :do
(let ((msgs (dequeue-msgs))) (let ((msgs (dequeue-msgs)))
(when msgs (when msgs
(dolist (m msgs) (push m *chat-history*)) (dolist (m msgs) (push m *chat-history*))
(render-chat chat-win))) (render-chat chat-win)))
(let* ((ev (get-event input-win)) (let* ((ev (get-event input-win))
(ch (when (and ev (typep ev 'event)) (event-key ev)))) (ch (when (and ev (typep ev 'event)) (event-key ev))))
(when ch (when ch
@@ -231,7 +147,6 @@ A simple MVP console is insufficient for a Lisp Machine. To reach v0.2.0, the TU
((or (eq ch #\Newline) (eq ch #\Return)) (handle-return *stream*)) ((or (eq ch #\Newline) (eq ch #\Return)) (handle-return *stream*))
((or (eq ch :backspace) (eq ch (code-char 127))) (handle-backspace)) ((or (eq ch :backspace) (eq ch (code-char 127))) (handle-backspace))
((characterp ch) (vector-push-extend ch *input-buffer*)))) ((characterp ch) (vector-push-extend ch *input-buffer*))))
(clear input-win) (clear input-win)
(add-string input-win (format nil "▶ ~a" (coerce *input-buffer* 'string)) :y 0 :x 1) (add-string input-win (format nil "▶ ~a" (coerce *input-buffer* 'string)) :y 0 :x 1)
(refresh input-win)) (refresh input-win))
@@ -239,4 +154,3 @@ A simple MVP console is insufficient for a Lisp Machine. To reach v0.2.0, the TU
(setf *is-running* nil) (setf *is-running* nil)
(when *socket* (ignore-errors (usocket:socket-close *socket*))))) (when *socket* (ignore-errors (usocket:socket-close *socket*)))))
#+end_src #+end_src

View File

@@ -9,15 +9,18 @@ The *LLM Gateway* skill provides a unified interface for interacting with multip
* Test Suite * Test Suite
#+begin_src lisp :tangle tests/llm-gateway-tests.lisp #+begin_src lisp :tangle tests/llm-gateway-tests.lisp
(defpackage :opencortex-llm-gateway-tests (defpackage :opencortex-llm-gateway-tests
(:use :cl :fiveam :opencortex) (:use :cl :opencortex)
(:export #:llm-gateway-suite)) (:export #:llm-gateway-suite))
(in-package :opencortex-llm-gateway-tests) (in-package :opencortex-llm-gateway-tests)
(def-suite llm-gateway-suite :description "Tests for the LLM Gateway skill") (eval-when (:compile-toplevel :load-toplevel :execute)
(in-suite llm-gateway-suite) (ql:quickload :fiveam))
(test test-llm-gateway-timeout (fiveam:def-suite llm-gateway-suite :description "Tests for the LLM Gateway skill")
(fiveam:in-suite llm-gateway-suite)
(fiveam:test test-llm-gateway-timeout
"Tier 2 Chaos: Verify that LLM Gateway handles connection failures gracefully." "Tier 2 Chaos: Verify that LLM Gateway handles connection failures gracefully."
(let ((old-host (uiop:getenv "OLLAMA_HOST"))) (let ((old-host (uiop:getenv "OLLAMA_HOST")))
(unwind-protect (unwind-protect
@@ -27,9 +30,9 @@ The *LLM Gateway* skill provides a unified interface for interacting with multip
(find-symbol "EXECUTE-LLM-REQUEST" :opencortex)))) (find-symbol "EXECUTE-LLM-REQUEST" :opencortex))))
(if fn (if fn
(let ((result (funcall fn :prompt "hello" :provider :ollama))) (let ((result (funcall fn :prompt "hello" :provider :ollama)))
(is (eq (getf result :status) :error)) (fiveam:is (eq (getf result :status) :error))
(is (uiop:string-prefix-p "Ollama Failure" (getf result :message)))) (fiveam:is (uiop:string-prefix-p "Ollama Failure" (getf result :message))))
(fail "Could not find EXECUTE-LLM-REQUEST symbol")))) (fiveam:fail "Could not find EXECUTE-LLM-REQUEST symbol"))))
(if old-host (if old-host
(setf (uiop:getenv "OLLAMA_HOST") old-host) (setf (uiop:getenv "OLLAMA_HOST") old-host)
(sb-posix:unsetenv "OLLAMA_HOST"))))) (sb-posix:unsetenv "OLLAMA_HOST")))))