Fix spinner clobbering user message text; add worker logging

ober

85af1a620b45c26ee9ae5f71b3fa6f8150e1fc0d

diff --git a/lib/jcode/ui/tui.sls b/lib/jcode/ui/tui.sls
index 19dd78b..0cd8e66 100644
--- a/lib/jcode/ui/tui.sls
+++ b/lib/jcode/ui/tui.sls
@@ -88,8 +88,10 @@
   (def (msg-area-height state)
        (- (app-state-height state)
           (app-state-input-height state)
+          1
           1))
-  (def (input-y state) (msg-area-height state))
+  (def (agent-status-y state) (msg-area-height state))
+  (def (input-y state) (+ (msg-area-height state) 1))
   (def (status-y state) (- (app-state-height state) 1))
   (def (sidebar-x state)
        (- (app-state-width state) (app-state-sidebar-width state)))
@@ -587,6 +589,7 @@
            (and (config-get-provider-key p-name) #t))
          (spawn
            (lambda ()
+             (tui-log "worker: entered, main-thread=~a" (*main-thread*))
              (try (parameterize ([current-provider-override p-override]
                                  [current-model-override m-override]
                                  [current-stream-cb
@@ -595,18 +598,28 @@
                                       (list 'stream-token token)))]
                                  [current-tool-cb
                                   (lambda (event name args)
+                                    (tui-log
+                                      "worker: tool-cb ~a ~a"
+                                      event
+                                      name)
                                     (send-agent-event!
                                       (list 'tool-event event name args)))]
                                  [current-usage-cb
                                   (lambda (usage)
                                     (send-agent-event!
                                       (list 'usage-update usage)))])
+                    (tui-log "worker: calling agent-run")
                     (agent-run s-id text)
-                    (send-agent-event! (list 'agent-done)))
+                    (tui-log
+                      "worker: agent-run returned, sending agent-done")
+                    (send-agent-event! (list 'agent-done))
+                    (tui-log
+                      "worker: agent-done sent, worker exiting normally"))
                   (catch
                     (e)
-                    (send-agent-event!
-                      (list 'agent-error (err->string e)))))))))
+                    (let ([msg (err->string e)])
+                      (tui-log "worker: CAUGHT exception: ~a" msg)
+                      (send-agent-event! (list 'agent-error msg)))))))))
   (def (tui-stream-token! state token)
        "Handle a streaming token from the LLM — called from agent thread."
        (let ([buf (app-state-stream-buf state)])
@@ -785,7 +798,7 @@
               [frame (vector-ref *spinner-frames* frame-idx)]
               [fg (face-fg-attr 'spinner)]
               [bg (face-bg-attr 'default)]
-              [y (max 0 (- (input-y state) 1))]
+              [y (agent-status-y state)]
               [w (msg-area-width state)]
               [text (string-append " " frame " thinking...")])
          (let clear ([col 0])
diff --git a/src/jcode/ui/tui.ss b/src/jcode/ui/tui.ss
index a24172c..68a470e 100644
--- a/src/jcode/ui/tui.ss
+++ b/src/jcode/ui/tui.ss
@@ -153,9 +153,14 @@
 (def (msg-area-height state)
   (- (app-state-height state)
      (app-state-input-height state)
-     1))  ;; -1 for status bar
-
-(def (input-y state) (msg-area-height state))
+     1    ;; -1 for status bar
+     1))  ;; -1 for dedicated agent-status row (spinner)
+
+;; The dedicated row for the thinking spinner. Lives BETWEEN the message
+;; area and the input, so drawing the spinner never clobbers the last
+;; rendered message line (which was the cause of missing user text).
+(def (agent-status-y state) (msg-area-height state))
+(def (input-y state) (+ (msg-area-height state) 1))
 (def (status-y state) (- (app-state-height state) 1))
 
 (def (sidebar-x state)
@@ -657,6 +662,7 @@
     ;; Agent worker: sends events to main thread mailbox; never touches state.
     (spawn
       (lambda ()
+        (tui-log "worker: entered, main-thread=~a" (*main-thread*))
         (try
           (parameterize
             ((current-provider-override p-override)
@@ -666,14 +672,20 @@
                  (send-agent-event! (list 'stream-token token))))
              (current-tool-cb
                (lambda (event name args)
+                 (tui-log "worker: tool-cb ~a ~a" event name)
                  (send-agent-event! (list 'tool-event event name args))))
              (current-usage-cb
                (lambda (usage)
                  (send-agent-event! (list 'usage-update usage)))))
+            (tui-log "worker: calling agent-run")
             (agent-run s-id text)
-            (send-agent-event! (list 'agent-done)))
+            (tui-log "worker: agent-run returned, sending agent-done")
+            (send-agent-event! (list 'agent-done))
+            (tui-log "worker: agent-done sent, worker exiting normally"))
           (catch (e)
-            (send-agent-event! (list 'agent-error (err->string e)))))))))
+            (let ((msg (err->string e)))
+              (tui-log "worker: CAUGHT exception: ~a" msg)
+              (send-agent-event! (list 'agent-error msg)))))))))
 
 (def (tui-stream-token! state token)
   "Handle a streaming token from the LLM — called from agent thread."
@@ -870,10 +882,10 @@
          (frame (vector-ref *spinner-frames* frame-idx))
          (fg (face-fg-attr 'spinner))
          (bg (face-bg-attr 'default))
-         (y (max 0 (- (input-y state) 1)))
+         (y (agent-status-y state))
          (w (msg-area-width state))
          (text (string-append " " frame " thinking...")))
-    ;; Clear entire row first so message content doesn't bleed through
+    ;; Dedicated status row, so clearing it never touches the message area.
     (let clear ((col 0))
       (when (< col w)
         (tb-change-cell! col y (char->integer #\space) bg bg)