Commit 0fd472fb0975

Vincent Demeester <vincent@sbr.pm>
2026-02-17 17:17:22
fix: org-todos buffer sync and test coverage
- Fixed stale buffer bug where write operations bypassed org-ql cached buffers, causing reads after writes to return outdated data - Fixed mutating quoted list literal in count-by-state that accumulated counts across invocations - Expanded test suite from 34 to 146 tests covering all tool actions, edge cases, and sequential operations Signed-off-by: Vincent Demeester <vincent@sbr.pm>
1 parent 7ad3921
dots/config/emacs/site-lisp/org-batch-functions.el
@@ -138,8 +138,8 @@ Uses org-ql's `ancestors' predicate for clean hierarchical queries."
 (defun org-batch-count-by-state (file)
   "Count TODOs in FILE by state.
 Returns alist with counts for each state."
-  (let ((counts '((total . 0) (TODO . 0) (NEXT . 0) (STRT . 0)
-                  (WAIT . 0) (DONE . 0) (CANX . 0))))
+  (let ((counts (list (cons 'total 0) (cons 'TODO 0) (cons 'NEXT 0) (cons 'STRT 0)
+                      (cons 'WAIT 0) (cons 'DONE 0) (cons 'CANX 0))))
     (org-ql-select file '(todo)
       :action (lambda ()
                 (let* ((state (org-get-todo-state))
@@ -374,6 +374,25 @@ NOTE: Works on current buffer state - save before calling if disk sync needed."
                     (is-link . ,(string-match-p "^\\[\\[http" heading))
                     (position . ,(point))))))))
 
+;;; Buffer Sync Utility
+
+(defun org-batch--sync-file-buffer (file)
+  "Revert any existing Emacs buffer visiting FILE from disk.
+Write operations use `with-temp-buffer' + `write-region' which bypass
+existing buffers.  Subsequent reads via `org-ql-select' use
+`find-file-noselect' which returns the stale cached buffer.
+Call this after writing to FILE to keep buffers in sync."
+  (let ((buf (find-buffer-visiting file)))
+    (when buf
+      (with-current-buffer buf
+        (revert-buffer t t t)))))
+
+(defun org-batch--write-and-sync (file)
+  "Write current buffer to FILE and sync any visiting buffer.
+Combines `write-region' and `org-batch--sync-file-buffer'."
+  (write-region (point-min) (point-max) file)
+  (org-batch--sync-file-buffer file))
+
 ;;; Write Operations (unchanged - org-ql is read-only)
 
 (defun org-batch--adjust-heading-levels (content parent-level)
@@ -450,7 +469,7 @@ Returns t on success, nil if heading not found."
             (when (looking-at "^\\*")
               (unless (looking-back "\n\n" nil)
                 (insert "\n")))
-            (write-region (point-min) (point-max) file)
+            (org-batch--write-and-sync file)
             (setq found t)))
         found))))
 
@@ -468,7 +487,7 @@ Returns t on success, nil if heading not found."
         (org-back-to-heading)
         (let ((org-log-done (if (string= new-state "DONE") 'time nil)))
           (org-todo new-state))
-        (write-region (point-min) (point-max) file)
+        (org-batch--write-and-sync file)
         (setq found t))
       found)))
 
@@ -498,7 +517,7 @@ TAGS: List of tag strings"
             (insert (format ":CREATED: [%s]\n"
                             (format-time-string "%Y-%m-%d %a %H:%M")))
             (insert ":END:\n")
-            (write-region (point-min) (point-max) file)
+            (org-batch--write-and-sync file)
             t)
         nil))))
 
@@ -515,7 +534,7 @@ DATE should be \"YYYY-MM-DD\" format."
       (when (re-search-forward heading-regexp nil t)
         (org-back-to-heading)
         (org-schedule nil date)
-        (write-region (point-min) (point-max) file)
+        (org-batch--write-and-sync file)
         (setq found t))
       found)))
 
@@ -532,7 +551,7 @@ DATE should be \"YYYY-MM-DD\" format."
       (when (re-search-forward heading-regexp nil t)
         (org-back-to-heading)
         (org-deadline nil date)
-        (write-region (point-min) (point-max) file)
+        (org-batch--write-and-sync file)
         (setq found t))
       found)))
 
@@ -551,7 +570,7 @@ DATE should be \"YYYY-MM-DD\" format."
         (when (looking-at " \\[#[1-5]\\]")
           (delete-region (point) (+ (point) 5)))
         (insert priority-cookie)
-        (write-region (point-min) (point-max) file)
+        (org-batch--write-and-sync file)
         (setq found t))
       found)))
 
@@ -570,7 +589,7 @@ NEW-TAGS is a list of tag strings to add."
         (let* ((current-tags (org-get-tags))
                (combined-tags (delete-dups (append current-tags new-tags))))
           (org-set-tags combined-tags)
-          (write-region (point-min) (point-max) file)
+          (org-batch--write-and-sync file)
           (setq found t)))
       found)))
 
@@ -588,7 +607,7 @@ NEW-TAGS is a list of tag strings to add."
         (let* ((current-tags (org-get-tags))
                (remaining-tags (seq-difference current-tags tags-to-remove)))
           (org-set-tags remaining-tags)
-          (write-region (point-min) (point-max) file)
+          (org-batch--write-and-sync file)
           (setq found t)))
       found)))
 
@@ -604,7 +623,7 @@ NEW-TAGS is a list of tag strings to add."
       (when (re-search-forward heading-regexp nil t)
         (org-back-to-heading)
         (org-set-tags new-tags)
-        (write-region (point-min) (point-max) file)
+        (org-batch--write-and-sync file)
         (setq found t))
       found)))
 
@@ -643,7 +662,7 @@ NEW-TAGS is a list of tag strings to add."
       (when (re-search-forward heading-regexp nil t)
         (org-back-to-heading)
         (org-set-property property-name value)
-        (write-region (point-min) (point-max) file)
+        (org-batch--write-and-sync file)
         (setq found t))
       found)))
 
@@ -689,7 +708,7 @@ Returns count of archived items."
                 (org-archive-subtree))
               (setq count (1+ count)))
           (error nil)))
-      (write-region (point-min) (point-max) file))
+      (org-batch--write-and-sync file))
     count))
 
 ;;; Bulk Operations
@@ -714,7 +733,7 @@ Returns count of updated tasks."
               (org-todo new-state))
             (setq count (1+ count))))
         (forward-line 1))
-      (write-region (point-min) (point-max) file))
+      (org-batch--write-and-sync file))
     count))
 
 (defun org-batch-bulk-add-tags (file filter-state new-tags)
@@ -734,7 +753,7 @@ Returns count of updated tasks."
               (org-set-tags combined-tags))
             (setq count (1+ count))))
         (forward-line 1))
-      (write-region (point-min) (point-max) file))
+      (org-batch--write-and-sync file))
     count))
 
 (defun org-batch-bulk-set-priority (file filter-state priority)
@@ -752,7 +771,7 @@ Returns count of updated tasks."
           (delete-region (point) (+ (point) 5)))
         (insert priority-cookie)
         (setq count (1+ count)))
-      (write-region (point-min) (point-max) file))
+      (org-batch--write-and-sync file))
     count))
 
 ;;; Time Tracking
@@ -769,7 +788,7 @@ Returns count of updated tasks."
       (when (re-search-forward heading-regexp nil t)
         (org-back-to-heading)
         (org-clock-in)
-        (write-region (point-min) (point-max) file)
+        (org-batch--write-and-sync file)
         (setq found t))
       found)))
 
@@ -791,7 +810,7 @@ Returns count of updated tasks."
                  (hours (floor (/ duration-seconds 3600)))
                  (minutes (floor (/ (mod duration-seconds 3600) 60))))
             (replace-match (format "%s%s--%s => %2d:%02d" indent start-time end-time hours minutes))
-            (write-region (point-min) (point-max) file)
+            (org-batch--write-and-sync file)
             (setq found t))))
       found)))
 
@@ -862,7 +881,7 @@ Returns minutes as integer."
 
 (defun org-batch-get-priority-distribution (file)
   "Get distribution of tasks by priority in FILE."
-  (let ((distribution '((1 . 0) (2 . 0) (3 . 0) (4 . 0) (5 . 0))))
+  (let ((distribution (list (cons 1 0) (cons 2 0) (cons 3 0) (cons 4 0) (cons 5 0))))
     (dolist (p '(1 2 3 4 5))
       (let ((regexp (format "\\[#%d\\]" p)))
         (setcdr (assoc p distribution)
@@ -930,7 +949,7 @@ Returns minutes as integer."
           (insert (format "SCHEDULED: <%s %s>\n"
                           (format-time-string "%Y-%m-%d %a")
                           repeater-spec)))
-        (write-region (point-min) (point-max) file)
+        (org-batch--write-and-sync file)
         (setq found t))
       found)))
 
dots/config/emacs/site-lisp/pi-org-todos-test.el
@@ -19,6 +19,12 @@
 (require 'org-batch-functions)
 (require 'pi-org-todos)
 
+;; Ensure custom TODO keywords are available in batch mode.
+;; In interactive mode these come from init.el, but batch mode
+;; starts with only (sequence "TODO" "DONE").
+(setq org-todo-keywords
+      '((sequence "STRT(s)" "NEXT(n)" "TODO(t)" "WAIT(w)" "|" "DONE(d!)" "CANX(c@/!)")))
+
 ;;; Test Fixtures
 
 (defvar pi-org-todos-test-file nil
@@ -347,5 +353,630 @@ SCHEDULED: <2026-02-06 Fri>
            (parsed (pi-org-todos-test-parse-result result)))
       (should (eq :json-false (alist-get 'success parsed))))))
 
+;;; Deadline Tests
+
+(ert-deftest pi-org-todos-test-deadline ()
+  "Test setting a deadline on a TODO."
+  (unwind-protect
+      (progn
+        (pi-org-todos-test-setup)
+        (let* ((result (pi/org-todo-deadline "NixOS refactoring" "2026-04-01"))
+               (parsed (pi-org-todos-test-parse-result result)))
+          (should (eq t (alist-get 'success parsed)))
+          ;; Verify the deadline was set
+          (let* ((get-result (pi/org-todo-get "NixOS refactoring"))
+                 (get-parsed (pi-org-todos-test-parse-result get-result)))
+            (should (string-match-p "2026-04-01"
+                                    (alist-get 'deadline (alist-get 'data get-parsed)))))))
+    (pi-org-todos-test-teardown)))
+
+(ert-deftest pi-org-todos-test-deadline-not-found ()
+  "Test setting deadline on non-existent heading."
+  (unwind-protect
+      (progn
+        (pi-org-todos-test-setup)
+        (let* ((result (pi/org-todo-deadline "Nonexistent heading" "2026-04-01"))
+               (parsed (pi-org-todos-test-parse-result result)))
+          (should (eq :json-false (alist-get 'success parsed)))))
+    (pi-org-todos-test-teardown)))
+
+;;; Append Content Tests
+
+(ert-deftest pi-org-todos-test-append ()
+  "Test appending content to a TODO."
+  (unwind-protect
+      (progn
+        (pi-org-todos-test-setup)
+        (let* ((result (pi/org-todo-append "Buy groceries" "Remember to check prices"))
+               (parsed (pi-org-todos-test-parse-result result)))
+          (should (eq t (alist-get 'success parsed)))
+          ;; Verify content was appended
+          (let* ((get-result (pi/org-todo-get "Buy groceries"))
+                 (get-parsed (pi-org-todos-test-parse-result get-result))
+                 (data (alist-get 'data get-parsed)))
+            (should (string-match-p "check prices" (alist-get 'content data))))))
+    (pi-org-todos-test-teardown)))
+
+(ert-deftest pi-org-todos-test-append-not-found ()
+  "Test appending content to non-existent heading."
+  (unwind-protect
+      (progn
+        (pi-org-todos-test-setup)
+        (let* ((result (pi/org-todo-append "Nonexistent" "some content"))
+               (parsed (pi-org-todos-test-parse-result result)))
+          (should (eq :json-false (alist-get 'success parsed)))))
+    (pi-org-todos-test-teardown)))
+
+;;; Tag Operation Tests
+
+(ert-deftest pi-org-todos-test-add-tags ()
+  "Test adding tags to a TODO."
+  (unwind-protect
+      (progn
+        (pi-org-todos-test-setup)
+        (let* ((result (pi/org-todo-add-tags "Buy groceries" '("shopping" "personal")))
+               (parsed (pi-org-todos-test-parse-result result)))
+          (should (eq t (alist-get 'success parsed)))
+          ;; Verify tags were added
+          (let* ((get-result (pi/org-todo-get "Buy groceries"))
+                 (get-parsed (pi-org-todos-test-parse-result get-result))
+                 (tags (append (alist-get 'tags (alist-get 'data get-parsed)) nil)))
+            (should (member "shopping" tags))
+            (should (member "personal" tags)))))
+    (pi-org-todos-test-teardown)))
+
+(ert-deftest pi-org-todos-test-remove-tags ()
+  "Test removing tags from a TODO."
+  (unwind-protect
+      (progn
+        (pi-org-todos-test-setup)
+        ;; The "Fix CI/CD issue" has :urgent: tag
+        (let* ((result (pi/org-todo-remove-tags "Fix CI/CD issue" '("urgent")))
+               (parsed (pi-org-todos-test-parse-result result)))
+          (should (eq t (alist-get 'success parsed)))
+          ;; Verify tag was removed
+          (let* ((get-result (pi/org-todo-get "Fix CI/CD issue"))
+                 (get-parsed (pi-org-todos-test-parse-result get-result))
+                 (tags (append (alist-get 'tags (alist-get 'data get-parsed)) nil)))
+            (should-not (member "urgent" tags)))))
+    (pi-org-todos-test-teardown)))
+
+(ert-deftest pi-org-todos-test-all-tags ()
+  "Test listing all tags."
+  (unwind-protect
+      (progn
+        (pi-org-todos-test-setup)
+        (let* ((result (pi/org-todo-all-tags))
+               (parsed (pi-org-todos-test-parse-result result)))
+          (should (eq t (alist-get 'success parsed)))
+          (let ((tags (append (alist-get 'data parsed) nil)))
+            ;; Should find the tags from test data
+            (should (member "urgent" tags))
+            (should (member "nixos" tags))
+            (should (member "homelab" tags)))))
+    (pi-org-todos-test-teardown)))
+
+;;; Property Operation Tests
+
+(ert-deftest pi-org-todos-test-get-property ()
+  "Test getting a property value."
+  (unwind-protect
+      (progn
+        (pi-org-todos-test-setup)
+        (let* ((result (pi/org-todo-get-property "Review PR for pipeline" "CREATED"))
+               (parsed (pi-org-todos-test-parse-result result)))
+          (should (eq t (alist-get 'success parsed)))
+          (should (string-match-p "2026-02-01"
+                                  (alist-get 'value (alist-get 'data parsed))))))
+    (pi-org-todos-test-teardown)))
+
+(ert-deftest pi-org-todos-test-set-property ()
+  "Test setting a property value."
+  (unwind-protect
+      (progn
+        (pi-org-todos-test-setup)
+        (let* ((result (pi/org-todo-set-property "Buy groceries" "EFFORT" "30min"))
+               (parsed (pi-org-todos-test-parse-result result)))
+          (should (eq t (alist-get 'success parsed)))
+          ;; Verify property was set
+          (let* ((get-result (pi/org-todo-get-property "Buy groceries" "EFFORT"))
+                 (get-parsed (pi-org-todos-test-parse-result get-result)))
+            (should (equal "30min" (alist-get 'value (alist-get 'data get-parsed)))))))
+    (pi-org-todos-test-teardown)))
+
+;;; Upcoming Tests
+
+(ert-deftest pi-org-todos-test-upcoming ()
+  "Test getting upcoming tasks."
+  (unwind-protect
+      (progn
+        (pi-org-todos-test-setup)
+        (let* ((result (pi/org-todo-upcoming nil 365)) ;; Wide range to capture test data
+               (parsed (pi-org-todos-test-parse-result result)))
+          (should (eq t (alist-get 'success parsed)))
+          ;; Data is a JSON array (vector), convert to list for checking
+          (let ((data (append (alist-get 'data parsed) nil)))
+            (should (listp data)))))
+    (pi-org-todos-test-teardown)))
+
+;;; State Transition Tests
+
+(ert-deftest pi-org-todos-test-state-to-strt ()
+  "Test changing TODO state to STRT."
+  (unwind-protect
+      (progn
+        (pi-org-todos-test-setup)
+        (let* ((result (pi/org-todo-state "Buy groceries" "STRT"))
+               (parsed (pi-org-todos-test-parse-result result)))
+          (should (eq t (alist-get 'success parsed)))
+          (let* ((get-result (pi/org-todo-get "Buy groceries"))
+                 (get-parsed (pi-org-todos-test-parse-result get-result)))
+            (should (equal "STRT" (alist-get 'todo (alist-get 'data get-parsed)))))))
+    (pi-org-todos-test-teardown)))
+
+(ert-deftest pi-org-todos-test-state-to-wait ()
+  "Test changing TODO state to WAIT."
+  (unwind-protect
+      (progn
+        (pi-org-todos-test-setup)
+        (let* ((result (pi/org-todo-state "Buy groceries" "WAIT"))
+               (parsed (pi-org-todos-test-parse-result result)))
+          (should (eq t (alist-get 'success parsed)))
+          (let* ((get-result (pi/org-todo-get "Buy groceries"))
+                 (get-parsed (pi-org-todos-test-parse-result get-result)))
+            (should (equal "WAIT" (alist-get 'todo (alist-get 'data get-parsed)))))))
+    (pi-org-todos-test-teardown)))
+
+(ert-deftest pi-org-todos-test-state-to-canx ()
+  "Test changing TODO state to CANX."
+  (unwind-protect
+      (progn
+        (pi-org-todos-test-setup)
+        (let* ((result (pi/org-todo-state "Buy groceries" "CANX"))
+               (parsed (pi-org-todos-test-parse-result result)))
+          (should (eq t (alist-get 'success parsed)))
+          (let* ((get-result (pi/org-todo-get "Buy groceries"))
+                 (get-parsed (pi-org-todos-test-parse-result get-result)))
+            (should (equal "CANX" (alist-get 'todo (alist-get 'data get-parsed)))))))
+    (pi-org-todos-test-teardown)))
+
+;;; List Filter Tests
+
+(ert-deftest pi-org-todos-test-list-filter-next ()
+  "Test listing only NEXT items."
+  (unwind-protect
+      (progn
+        (pi-org-todos-test-setup)
+        (let* ((result (pi/org-todo-list nil "NEXT"))
+               (parsed (pi-org-todos-test-parse-result result)))
+          (should (eq t (alist-get 'success parsed)))
+          (let ((data (append (alist-get 'data parsed) nil)))
+            (should (> (length data) 0))
+            ;; All should be NEXT
+            (dolist (todo data)
+              (should (equal "NEXT" (alist-get 'todo todo)))))))
+    (pi-org-todos-test-teardown)))
+
+(ert-deftest pi-org-todos-test-list-filter-strt ()
+  "Test listing only STRT items."
+  (unwind-protect
+      (progn
+        (pi-org-todos-test-setup)
+        (let* ((result (pi/org-todo-list nil "STRT"))
+               (parsed (pi-org-todos-test-parse-result result)))
+          (should (eq t (alist-get 'success parsed)))
+          (let ((data (append (alist-get 'data parsed) nil)))
+            (should (= 1 (length data)))
+            (should (string-match-p "documentation" (alist-get 'heading (car data)))))))
+    (pi-org-todos-test-teardown)))
+
+(ert-deftest pi-org-todos-test-list-filter-comma-separated ()
+  "Test listing with comma-separated state filter."
+  (unwind-protect
+      (progn
+        (pi-org-todos-test-setup)
+        (let* ((result (pi/org-todo-list nil "NEXT,STRT"))
+               (parsed (pi-org-todos-test-parse-result result)))
+          (should (eq t (alist-get 'success parsed)))
+          (let ((data (append (alist-get 'data parsed) nil)))
+            (should (= 2 (length data)))
+            (dolist (todo data)
+              (should (member (alist-get 'todo todo) '("NEXT" "STRT")))))))
+    (pi-org-todos-test-teardown)))
+
+;;; Search with Content Tests
+
+(ert-deftest pi-org-todos-test-search-heading ()
+  "Test search matches in heading."
+  (unwind-protect
+      (progn
+        (pi-org-todos-test-setup)
+        (let* ((result (pi/org-todo-search "pipeline"))
+               (parsed (pi-org-todos-test-parse-result result)))
+          (should (eq t (alist-get 'success parsed)))
+          (let ((data (append (alist-get 'data parsed) nil)))
+            ;; "pipeline" appears in heading of one TODO and in BLOCKER property of another
+            (should (>= (length data) 1))
+            ;; At least one should match in heading (note: key is 'matched-in with hyphen)
+            (should (cl-some (lambda (todo)
+                               (equal "heading" (alist-get 'matched-in todo)))
+                             data)))))
+    (pi-org-todos-test-teardown)))
+
+(ert-deftest pi-org-todos-test-search-content ()
+  "Test search matches in content."
+  (unwind-protect
+      (progn
+        (pi-org-todos-test-setup)
+        (let* ((result (pi/org-todo-search "check prices" nil t))
+               (parsed (pi-org-todos-test-parse-result result)))
+          ;; Should return empty since content doesn't contain "check prices" yet
+          (should (eq t (alist-get 'success parsed)))
+          (should (= 0 (length (alist-get 'data parsed))))))
+    (pi-org-todos-test-teardown)))
+
+(ert-deftest pi-org-todos-test-search-no-results ()
+  "Test search with no results."
+  (unwind-protect
+      (progn
+        (pi-org-todos-test-setup)
+        (let* ((result (pi/org-todo-search "zzzznonexistent"))
+               (parsed (pi-org-todos-test-parse-result result)))
+          (should (eq t (alist-get 'success parsed)))
+          (should (= 0 (length (alist-get 'data parsed))))))
+    (pi-org-todos-test-teardown)))
+
+;;; Add with Various Options Tests
+
+(ert-deftest pi-org-todos-test-add-minimal ()
+  "Test adding TODO with minimal options (no schedule, priority, tags)."
+  (unwind-protect
+      (progn
+        (pi-org-todos-test-setup)
+        (let* ((result (pi/org-todo-add "Minimal task" "Personal"))
+               (parsed (pi-org-todos-test-parse-result result)))
+          (should (eq t (alist-get 'success parsed)))
+          (let* ((get-result (pi/org-todo-get "Minimal task"))
+                 (get-parsed (pi-org-todos-test-parse-result get-result))
+                 (data (alist-get 'data get-parsed)))
+            (should data)
+            (should (equal "TODO" (alist-get 'todo data))))))
+    (pi-org-todos-test-teardown)))
+
+(ert-deftest pi-org-todos-test-add-with-tags ()
+  "Test adding TODO with tags."
+  (unwind-protect
+      (progn
+        (pi-org-todos-test-setup)
+        (let* ((result (pi/org-todo-add "Tagged task" "Work" nil nil nil '("review" "code")))
+               (parsed (pi-org-todos-test-parse-result result)))
+          (should (eq t (alist-get 'success parsed)))
+          (let* ((get-result (pi/org-todo-get "Tagged task"))
+                 (get-parsed (pi-org-todos-test-parse-result get-result))
+                 (tags (append (alist-get 'tags (alist-get 'data get-parsed)) nil)))
+            (should (member "review" tags))
+            (should (member "code" tags)))))
+    (pi-org-todos-test-teardown)))
+
+(ert-deftest pi-org-todos-test-add-nonexistent-section ()
+  "Test adding TODO to nonexistent section."
+  (unwind-protect
+      (progn
+        (pi-org-todos-test-setup)
+        (let* ((result (pi/org-todo-add "Task" "Nonexistent"))
+               (parsed (pi-org-todos-test-parse-result result)))
+          (should (eq :json-false (alist-get 'success parsed)))))
+    (pi-org-todos-test-teardown)))
+
+;;; Inbox Operations Tests
+
+(ert-deftest pi-org-todos-test-inbox-all-empty ()
+  "Test getting all entries from an empty inbox."
+  (let ((test-inbox (make-temp-file "inbox-test-" nil ".org")))
+    (unwind-protect
+        (progn
+          (with-temp-file test-inbox
+            (insert "#+title: Test Inbox\n"))
+          (let* ((result (pi/org-todo-inbox-all test-inbox))
+                 (parsed (pi-org-todos-test-parse-result result)))
+            (should (eq t (alist-get 'success parsed)))
+            (should (= 0 (length (alist-get 'data parsed))))))
+      (delete-file test-inbox))))
+
+(ert-deftest pi-org-todos-test-inbox-all-with-items ()
+  "Test getting all entries from inbox with items."
+  (let ((test-inbox (make-temp-file "inbox-test-" nil ".org")))
+    (unwind-protect
+        (progn
+          (with-temp-file test-inbox
+            (insert "#+title: Test Inbox\n* TODO First item\n* Second item\n* TODO Third item\n"))
+          (let* ((result (pi/org-todo-inbox-all test-inbox))
+                 (parsed (pi-org-todos-test-parse-result result)))
+            (should (eq t (alist-get 'success parsed)))
+            (let ((data (append (alist-get 'data parsed) nil)))
+              ;; Should have 3 entries total
+              (should (= 3 (length data)))
+              ;; Two are TODOs, one is plain
+              (let ((todos (seq-filter (lambda (e) (alist-get 'todo e)) data)))
+                (should (= 2 (length todos)))))))
+      (delete-file test-inbox))))
+
+;;; Refile Targets Tests
+
+(ert-deftest pi-org-todos-test-refile-targets ()
+  "Test getting refile targets."
+  (unwind-protect
+      (progn
+        (pi-org-todos-test-setup)
+        (let* ((result (pi/org-todo-get-refile-targets))
+               (parsed (pi-org-todos-test-parse-result result)))
+          (should (eq t (alist-get 'success parsed)))
+          (let ((targets (append (alist-get 'data parsed) nil)))
+            (should (> (length targets) 0))
+            ;; Each target should have section, path, level, position
+            (dolist (target targets)
+              (should (alist-get 'section target))
+              (should (alist-get 'level target))
+              (should (alist-get 'position target))))))
+    (pi-org-todos-test-teardown)))
+
+;;; Refile Tests
+
+(ert-deftest pi-org-todos-test-refile ()
+  "Test refiling an entry from inbox to todos."
+  (let ((test-inbox (make-temp-file "inbox-test-" nil ".org")))
+    (unwind-protect
+        (progn
+          (pi-org-todos-test-setup)
+          (with-temp-file test-inbox
+            (insert "* TODO Refile me\n"))
+          (let* ((result (pi/org-todo-refile "Refile me" "Work" test-inbox))
+                 (parsed (pi-org-todos-test-parse-result result)))
+            (should (eq t (alist-get 'success parsed)))
+            ;; Verify it's in Work section now
+            (let* ((get-result (pi/org-todo-get "Refile me"))
+                   (get-parsed (pi-org-todos-test-parse-result get-result)))
+              (should (alist-get 'data get-parsed)))
+            ;; Verify it's gone from inbox
+            (with-temp-buffer
+              (insert-file-contents test-inbox)
+              (should-not (string-match-p "Refile me" (buffer-string))))))
+      (when (file-exists-p test-inbox)
+        (delete-file test-inbox))
+      (pi-org-todos-test-teardown))))
+
+;;; Get with Content Tests
+
+(ert-deftest pi-org-todos-test-get-returns-content ()
+  "Test getting a TODO returns its body content."
+  (unwind-protect
+      (progn
+        (pi-org-todos-test-setup)
+        (let* ((result (pi/org-todo-get "Review PR for pipeline"))
+               (parsed (pi-org-todos-test-parse-result result))
+               (data (alist-get 'data parsed)))
+          (should (alist-get 'content data))
+          (should (string-match-p "test TODO with some content" (alist-get 'content data)))))
+    (pi-org-todos-test-teardown)))
+
+(ert-deftest pi-org-todos-test-get-returns-properties ()
+  "Test getting a TODO returns its properties."
+  (unwind-protect
+      (progn
+        (pi-org-todos-test-setup)
+        (let* ((result (pi/org-todo-get "Review PR for pipeline"))
+               (parsed (pi-org-todos-test-parse-result result))
+               (data (alist-get 'data parsed))
+               (properties (alist-get 'properties data)))
+          (should properties)
+          ;; Should include CREATED property
+          (should (alist-get 'CREATED properties))))
+    (pi-org-todos-test-teardown)))
+
+;;; Edge Case Tests
+
+(ert-deftest pi-org-todos-test-empty-file ()
+  "Test operations on an empty org file."
+  (let ((test-file (make-temp-file "empty-test-" nil ".org")))
+    (unwind-protect
+        (progn
+          (with-temp-file test-file
+            (insert "#+title: Empty\n"))
+          (let ((pi/org-todo-default-file test-file))
+            (let* ((result (pi/org-todo-list))
+                   (parsed (pi-org-todos-test-parse-result result)))
+              (should (eq t (alist-get 'success parsed)))
+              (should (= 0 (length (alist-get 'data parsed)))))))
+      (delete-file test-file))))
+
+(ert-deftest pi-org-todos-test-statistics-empty-file ()
+  "Test statistics on empty file with no TODOs."
+  (let ((test-file (make-temp-file "empty-stats-" nil ".org")))
+    (unwind-protect
+        (progn
+          (with-temp-file test-file
+            (insert "#+title: Empty\n* Work\n"))
+          ;; Use explicit file arg to avoid stale default
+          (let* ((result (pi/org-todo-statistics test-file))
+                 (parsed (pi-org-todos-test-parse-result result)))
+            (should (eq t (alist-get 'success parsed)))
+            (should (= 0 (alist-get 'total (alist-get 'data parsed))))))
+      (delete-file test-file))))
+
+(ert-deftest pi-org-todos-test-schedule-not-found ()
+  "Test scheduling a non-existent heading."
+  (unwind-protect
+      (progn
+        (pi-org-todos-test-setup)
+        (let* ((result (pi/org-todo-schedule "Nonexistent" "2026-03-01"))
+               (parsed (pi-org-todos-test-parse-result result)))
+          (should (eq :json-false (alist-get 'success parsed)))))
+    (pi-org-todos-test-teardown)))
+
+(ert-deftest pi-org-todos-test-priority-not-found ()
+  "Test setting priority on non-existent heading."
+  (unwind-protect
+      (progn
+        (pi-org-todos-test-setup)
+        (let* ((result (pi/org-todo-priority "Nonexistent" 1))
+               (parsed (pi-org-todos-test-parse-result result)))
+          (should (eq :json-false (alist-get 'success parsed)))))
+    (pi-org-todos-test-teardown)))
+
+(ert-deftest pi-org-todos-test-state-not-found ()
+  "Test changing state on non-existent heading."
+  (unwind-protect
+      (progn
+        (pi-org-todos-test-setup)
+        (let* ((result (pi/org-todo-state "Nonexistent" "NEXT"))
+               (parsed (pi-org-todos-test-parse-result result)))
+          (should (eq :json-false (alist-get 'success parsed)))))
+    (pi-org-todos-test-teardown)))
+
+;;; JSON Output Robustness Tests
+
+(ert-deftest pi-org-todos-test-json-response-nil-data ()
+  "Test JSON response with nil data."
+  (let* ((result (pi/org-todo--json-response t nil))
+         (parsed (json-read-from-string result)))
+    (should (eq t (alist-get 'success parsed)))))
+
+(ert-deftest pi-org-todos-test-json-response-list-data ()
+  "Test JSON response with list data."
+  (let* ((result (pi/org-todo--json-response t '(("a" . 1) ("b" . 2))))
+         (parsed (json-read-from-string result)))
+    (should (eq t (alist-get 'success parsed)))))
+
+(ert-deftest pi-org-todos-test-json-response-empty-error ()
+  "Test JSON response with nil error defaults to unknown."
+  (let* ((result (pi/org-todo--json-response nil nil nil))
+         (parsed (json-read-from-string result)))
+    (should (eq :json-false (alist-get 'success parsed)))
+    (should (equal "Unknown error" (alist-get 'error parsed)))))
+
+;;; File Path Resolution Tests
+
+(ert-deftest pi-org-todos-test-file-env-override ()
+  "Test that ORG_TODO_FILE env var overrides default."
+  (let ((test-file (make-temp-file "env-test-" nil ".org")))
+    (unwind-protect
+        (progn
+          (with-temp-file test-file
+            (insert "#+title: Env Test\n* Work\n** TODO Env task\n"))
+          (setenv "ORG_TODO_FILE" test-file)
+          (let ((pi/org-todo-default-file "/should/not/be/used"))
+            (let* ((result (pi/org-todo-list))
+                   (parsed (pi-org-todos-test-parse-result result)))
+              (should (eq t (alist-get 'success parsed)))
+              (let ((data (append (alist-get 'data parsed) nil)))
+                (should (= 1 (length data)))
+                (should (equal "Env task" (alist-get 'heading (car data))))))))
+      (setenv "ORG_TODO_FILE" nil)
+      (delete-file test-file))))
+
+(ert-deftest pi-org-todos-test-file-explicit-overrides-env ()
+  "Test that explicit file arg overrides env var."
+  (let ((test-file (make-temp-file "explicit-test-" nil ".org")))
+    (unwind-protect
+        (progn
+          (with-temp-file test-file
+            (insert "#+title: Explicit\n* Work\n** TODO Explicit task\n"))
+          (setenv "ORG_TODO_FILE" "/should/not/be/used")
+          (let* ((result (pi/org-todo-list test-file))
+                 (parsed (pi-org-todos-test-parse-result result)))
+            (should (eq t (alist-get 'success parsed)))
+            (let ((data (append (alist-get 'data parsed) nil)))
+              (should (= 1 (length data))))))
+      (setenv "ORG_TODO_FILE" nil)
+      (delete-file test-file))))
+
+;;; By-Section Edge Cases
+
+(ert-deftest pi-org-todos-test-by-section-empty ()
+  "Test getting TODOs from a section with no TODOs."
+  (let ((test-file (make-temp-file "section-empty-" nil ".org")))
+    (unwind-protect
+        (progn
+          (with-temp-file test-file
+            (insert "#+title: Test\n* Empty Section\nNo todos here.\n"))
+          (let* ((result (pi/org-todo-by-section "Empty Section" test-file))
+                 (parsed (pi-org-todos-test-parse-result result)))
+            (should (eq t (alist-get 'success parsed)))
+            (should (= 0 (length (alist-get 'data parsed))))))
+      (delete-file test-file))))
+
+;;; Multiple Operations in Sequence
+
+(ert-deftest pi-org-todos-test-add-then-done ()
+  "Test adding a TODO then marking it done."
+  (unwind-protect
+      (progn
+        (pi-org-todos-test-setup)
+        ;; Add
+        (let* ((add-result (pi/org-todo-add "Sequential task" "Work"))
+               (add-parsed (pi-org-todos-test-parse-result add-result)))
+          (should (eq t (alist-get 'success add-parsed)))
+          ;; Verify it's TODO
+          (let* ((get-result (pi/org-todo-get "Sequential task"))
+                 (get-parsed (pi-org-todos-test-parse-result get-result)))
+            (should (equal "TODO" (alist-get 'todo (alist-get 'data get-parsed)))))
+          ;; Mark done
+          (let* ((done-result (pi/org-todo-done "Sequential task"))
+                 (done-parsed (pi-org-todos-test-parse-result done-result)))
+            (should (eq t (alist-get 'success done-parsed))))
+          ;; Verify it's DONE
+          (let* ((get-result (pi/org-todo-get "Sequential task"))
+                 (get-parsed (pi-org-todos-test-parse-result get-result)))
+            (should (equal "DONE" (alist-get 'todo (alist-get 'data get-parsed)))))))
+    (pi-org-todos-test-teardown)))
+
+(ert-deftest pi-org-todos-test-add-schedule-priority ()
+  "Test adding a TODO then setting schedule and priority."
+  (unwind-protect
+      (progn
+        (pi-org-todos-test-setup)
+        ;; Add minimal
+        (pi/org-todo-add "Multi-update task" "Work")
+        ;; Schedule
+        (let* ((result (pi/org-todo-schedule "Multi-update task" "2026-06-15"))
+               (parsed (pi-org-todos-test-parse-result result)))
+          (should (eq t (alist-get 'success parsed))))
+        ;; Priority
+        (let* ((result (pi/org-todo-priority "Multi-update task" 1))
+               (parsed (pi-org-todos-test-parse-result result)))
+          (should (eq t (alist-get 'success parsed))))
+        ;; Verify all set
+        (let* ((get-result (pi/org-todo-get "Multi-update task"))
+               (get-parsed (pi-org-todos-test-parse-result get-result))
+               (data (alist-get 'data get-parsed)))
+          (should (equal 1 (alist-get 'priority data)))
+          (should (string-match-p "2026-06-15" (alist-get 'scheduled data)))))
+    (pi-org-todos-test-teardown)))
+
+;;; Safe Call Wrapper Tests
+
+(ert-deftest pi-org-todos-test-safe-call-success ()
+  "Test safe call wrapper with successful function."
+  (let* ((result (pi/org-todo--safe-call (lambda () '((test . "ok")))))
+         (parsed (json-read-from-string result)))
+    (should (eq t (alist-get 'success parsed)))))
+
+(ert-deftest pi-org-todos-test-safe-call-error ()
+  "Test safe call wrapper catches errors."
+  (let* ((result (pi/org-todo--safe-call (lambda () (error "test error"))))
+         (parsed (json-read-from-string result)))
+    (should (eq :json-false (alist-get 'success parsed)))
+    (should (string-match-p "test error" (alist-get 'error parsed)))))
+
+;;; Numeric Key Fix Tests
+
+(ert-deftest pi-org-todos-test-fix-numeric-keys ()
+  "Test numeric key conversion for JSON compatibility."
+  (let ((alist '((1 . "one") (2 . "two") ("three" . 3))))
+    (let ((fixed (pi/org-todo--fix-numeric-keys alist)))
+      (should (assoc "1" fixed))
+      (should (assoc "2" fixed))
+      (should (assoc "three" fixed)))))
+
 (provide 'pi-org-todos-test)
 ;;; pi-org-todos-test.el ends here
dots/pi/agent/extensions/org-todos/bun.lock
@@ -7,9 +7,18 @@
       "dependencies": {
         "chrono-node": "^2.7.0",
       },
+      "devDependencies": {
+        "bun-types": "^1.0.0",
+      },
     },
   },
   "packages": {
+    "@types/node": ["@types/node@25.2.3", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ=="],
+
+    "bun-types": ["bun-types@1.3.9", "", { "dependencies": { "@types/node": "*" } }, "sha512-+UBWWOakIP4Tswh0Bt0QD0alpTY8cb5hvgiYeWCMet9YukHbzuruIEeXC2D7nMJPB12kbh8C7XJykSexEqGKJg=="],
+
     "chrono-node": ["chrono-node@2.9.0", "", {}, "sha512-glI4YY2Jy6JII5l3d5FN6rcrIbKSQqKPhWsIRYPK2IK8Mm4Q1ZZFdYIaDqglUNf7gNwG+kWIzTn0omzzE0VkvQ=="],
+
+    "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
   }
 }
dots/pi/agent/extensions/org-todos/index.test.ts
@@ -3,15 +3,16 @@
  *
  * Run with: bun test dots/pi/agent/extensions/org-todos/index.test.ts
  *
- * Note: These tests require Emacs daemon running with pi-org-todos.el loaded.
- * For unit tests that don't require Emacs, see the mock tests below.
+ * Note: Integration tests require Emacs daemon running with pi-org-todos.el loaded.
+ * Unit tests can run without Emacs.
  */
 
 import { describe, test, expect, beforeAll, afterAll } from "bun:test";
 import { execSync } from "node:child_process";
-import { writeFileSync, unlinkSync, existsSync } from "node:fs";
+import { writeFileSync, unlinkSync, readFileSync, existsSync } from "node:fs";
 import { join } from "node:path";
 import { tmpdir } from "node:os";
+import * as chrono from "chrono-node";
 
 // Test org file content
 const TEST_ORG_CONTENT = `#+title: Test TODOs
@@ -24,9 +25,21 @@ SCHEDULED: <2026-02-06 Fri>
 :CREATED: [2026-02-01 Mon]
 :END:
 
+This is a test TODO with some content.
+
 ** NEXT [#1] Fix CI/CD issue :urgent:
 DEADLINE: <2026-02-05 Thu>
 
+** STRT Write documentation
+:PROPERTIES:
+:CREATED: [2026-02-03 Wed]
+:END:
+
+** WAIT Waiting for approval :blocked:
+:PROPERTIES:
+:BLOCKER: Review PR for pipeline
+:END:
+
 ** DONE Completed task
 CLOSED: [2026-02-04 Thu 10:00]
 
@@ -36,6 +49,11 @@ CLOSED: [2026-02-04 Thu 10:00]
 SCHEDULED: <2026-02-10 Wed>
 
 ** TODO NixOS refactoring :nixos:homelab:
+
+* Personal
+
+** TODO Buy groceries
+SCHEDULED: <2026-02-06 Fri>
 `;
 
 // Test file path
@@ -71,82 +89,353 @@ function execEmacs(elisp: string): any {
 // Check if we can run integration tests
 const canRunIntegrationTests = isEmacsDaemonRunning();
 
+// ============================
+// UNIT TESTS (no Emacs needed)
+// ============================
+
 describe("org-todos extension", () => {
-  // Unit tests (no Emacs required)
   describe("unit tests", () => {
-    test("formatTodo formats basic TODO", () => {
-      const todo = {
-        todo: "TODO",
-        heading: "Test task",
-        priority: 2,
-        tags: ["work", "urgent"],
-        scheduled: "<2026-02-06 Fri>",
-      };
-
-      // Inline the format logic for testing
-      const parts: string[] = [];
-      parts.push(`[${todo.todo}]`);
-      if (todo.priority) parts.push(`[#${todo.priority}]`);
-      parts.push(todo.heading);
-      if (todo.tags?.length) parts.push(`:${todo.tags.join(":")}:`);
-      if (todo.scheduled) parts.push(`(SCHEDULED: ${todo.scheduled})`);
-
-      const formatted = parts.join(" ");
-
-      expect(formatted).toContain("[TODO]");
-      expect(formatted).toContain("[#2]");
-      expect(formatted).toContain("Test task");
-      expect(formatted).toContain(":work:urgent:");
-      expect(formatted).toContain("SCHEDULED:");
-    });
-
-    test("formatTodo handles minimal TODO", () => {
-      const todo = {
-        todo: "NEXT",
-        heading: "Simple task",
-      };
-
-      const parts: string[] = [];
-      parts.push(`[${todo.todo}]`);
-      parts.push(todo.heading);
-      const formatted = parts.join(" ");
-
-      expect(formatted).toBe("[NEXT] Simple task");
-    });
-
-    test("action validation", () => {
-      const validActions = [
-        "list",
-        "scheduled",
-        "upcoming",
-        "overdue",
-        "search",
-        "get",
-        "done",
-        "state",
-        "schedule",
-        "deadline",
-        "priority",
-        "add",
-        "append",
-        "sections",
-        "statistics",
-        "archive",
-      ];
-
-      for (const action of validActions) {
-        expect(validActions).toContain(action);
+    // --- stripOrgLinks ---
+    describe("stripOrgLinks", () => {
+      // Import logic inline since the function is not exported
+      function stripOrgLinks(text: string): string {
+        text = text.replace(/\[\[([^\]]*)\]\[([^\]]*)\]\]/g, "$2");
+        text = text.replace(/\[\[([^\]]*)\]\]/g, "$1");
+        return text;
       }
+
+      test("strips [[url][title]] to title", () => {
+        expect(stripOrgLinks("See [[https://example.com][Example]]")).toBe("See Example");
+      });
+
+      test("strips [[url]] to url", () => {
+        expect(stripOrgLinks("See [[https://example.com]]")).toBe("See https://example.com");
+      });
+
+      test("handles multiple links", () => {
+        expect(stripOrgLinks("[[a][A]] and [[b][B]]")).toBe("A and B");
+      });
+
+      test("handles text with no links", () => {
+        expect(stripOrgLinks("No links here")).toBe("No links here");
+      });
+
+      test("handles empty string", () => {
+        expect(stripOrgLinks("")).toBe("");
+      });
+
+      test("handles nested brackets", () => {
+        expect(stripOrgLinks("[[file:todos.org::*Heading][Heading]]")).toBe("Heading");
+      });
     });
 
-    test("elisp escaping", () => {
-      const heading = 'Task with "quotes" and \'apostrophes\'';
+    // --- formatTodo ---
+    describe("formatTodo", () => {
+      function formatTodo(todo: any): string {
+        const parts: string[] = [];
+        const state = todo.todo || "TODO";
+        parts.push(`[${state}]`);
+        if (todo.priority) parts.push(`[#${todo.priority}]`);
+        parts.push(todo.heading);
+        if (todo.tags && todo.tags.length > 0) parts.push(`:${todo.tags.join(":")}:`);
+        const dates: string[] = [];
+        if (todo.scheduled) dates.push(`SCHEDULED: ${todo.scheduled}`);
+        if (todo.deadline) dates.push(`DEADLINE: ${todo.deadline}`);
+        if (dates.length > 0) parts.push(`(${dates.join(", ")})`);
+        return parts.join(" ");
+      }
+
+      test("formats basic TODO", () => {
+        const result = formatTodo({ todo: "TODO", heading: "Test", priority: 2, tags: ["work"], scheduled: "<2026-02-06>" });
+        expect(result).toContain("[TODO]");
+        expect(result).toContain("[#2]");
+        expect(result).toContain("Test");
+        expect(result).toContain(":work:");
+        expect(result).toContain("SCHEDULED:");
+      });
+
+      test("formats minimal TODO", () => {
+        expect(formatTodo({ todo: "NEXT", heading: "Simple" })).toBe("[NEXT] Simple");
+      });
+
+      test("handles null state", () => {
+        expect(formatTodo({ heading: "No state" })).toBe("[TODO] No state");
+      });
+
+      test("formats with deadline only", () => {
+        const result = formatTodo({ todo: "TODO", heading: "Task", deadline: "<2026-03-01>" });
+        expect(result).toContain("DEADLINE:");
+        expect(result).not.toContain("SCHEDULED:");
+      });
+
+      test("formats with both scheduled and deadline", () => {
+        const result = formatTodo({ todo: "TODO", heading: "Task", scheduled: "<2026-02-06>", deadline: "<2026-03-01>" });
+        expect(result).toContain("SCHEDULED:");
+        expect(result).toContain("DEADLINE:");
+      });
+
+      test("formats with multiple tags", () => {
+        const result = formatTodo({ todo: "TODO", heading: "Task", tags: ["work", "urgent", "review"] });
+        expect(result).toContain(":work:urgent:review:");
+      });
+
+      test("formats with empty tags array", () => {
+        const result = formatTodo({ todo: "TODO", heading: "Task", tags: [] });
+        expect(result).not.toContain(":");
+      });
+    });
+
+    // --- formatTodoMarkdown ---
+    describe("formatTodoMarkdown", () => {
+      function stripOrgLinks(text: string): string {
+        text = text.replace(/\[\[([^\]]*)\]\[([^\]]*)\]\]/g, "$2");
+        text = text.replace(/\[\[([^\]]*)\]\]/g, "$1");
+        return text;
+      }
+
+      function formatTodoMarkdown(todo: any): string {
+        const parts: string[] = [];
+        const state = todo.todo || "TODO";
+        parts.push(`**[${state}]**`);
+        if (todo.priority) parts.push(`\`#${todo.priority}\``);
+        parts.push(stripOrgLinks(todo.heading));
+        if (todo.tags && todo.tags.length > 0) {
+          const tagStr = todo.tags.map((t: string) => `\`${t}\``).join(" ");
+          parts.push(tagStr);
+        }
+        const dates: string[] = [];
+        if (todo.scheduled) dates.push(`๐Ÿ“… ${todo.scheduled}`);
+        if (todo.deadline) dates.push(`โฐ ${todo.deadline}`);
+        let result = parts.join(" ");
+        if (dates.length > 0) result += ` *(${dates.join(", ")})*`;
+        return result;
+      }
+
+      test("wraps state in bold", () => {
+        const result = formatTodoMarkdown({ todo: "NEXT", heading: "Task" });
+        expect(result).toContain("**[NEXT]**");
+      });
+
+      test("formats priority as inline code", () => {
+        const result = formatTodoMarkdown({ todo: "TODO", heading: "Task", priority: 1 });
+        expect(result).toContain("`#1`");
+      });
+
+      test("formats tags as inline code", () => {
+        const result = formatTodoMarkdown({ todo: "TODO", heading: "Task", tags: ["urgent"] });
+        expect(result).toContain("`urgent`");
+      });
+
+      test("strips org links in heading", () => {
+        const result = formatTodoMarkdown({ todo: "TODO", heading: "See [[https://example.com][docs]]" });
+        expect(result).toContain("See docs");
+        expect(result).not.toContain("[[");
+      });
+
+      test("formats dates with emoji", () => {
+        const result = formatTodoMarkdown({ todo: "TODO", heading: "Task", scheduled: "<2026-02-06>", deadline: "<2026-03-01>" });
+        expect(result).toContain("๐Ÿ“…");
+        expect(result).toContain("โฐ");
+      });
+    });
+
+    // --- parseNaturalDate ---
+    describe("parseNaturalDate", () => {
+      function parseNaturalDate(text: string): string | null {
+        const result = chrono.parseDate(text);
+        if (!result) return null;
+        const year = result.getFullYear();
+        const month = String(result.getMonth() + 1).padStart(2, "0");
+        const day = String(result.getDate()).padStart(2, "0");
+        return `${year}-${month}-${day}`;
+      }
+
+      test("parses 'tomorrow'", () => {
+        const result = parseNaturalDate("tomorrow");
+        expect(result).toBeDefined();
+        const tomorrow = new Date();
+        tomorrow.setDate(tomorrow.getDate() + 1);
+        const expected = `${tomorrow.getFullYear()}-${String(tomorrow.getMonth() + 1).padStart(2, "0")}-${String(tomorrow.getDate()).padStart(2, "0")}`;
+        expect(result).toBe(expected);
+      });
+
+      test("parses 'next friday'", () => {
+        const result = parseNaturalDate("next friday");
+        expect(result).toBeDefined();
+        expect(result).toMatch(/^\d{4}-\d{2}-\d{2}$/);
+      });
+
+      test("parses ISO date", () => {
+        const result = parseNaturalDate("2026-03-15");
+        expect(result).toBe("2026-03-15");
+      });
+
+      test("parses 'in 3 days'", () => {
+        const result = parseNaturalDate("in 3 days");
+        expect(result).toBeDefined();
+      });
+
+      test("returns null for invalid date", () => {
+        const result = parseNaturalDate("not a date zzzzz");
+        expect(result).toBeNull();
+      });
+    });
+
+    // --- parseCommandArgs ---
+    describe("parseCommandArgs", () => {
+      function parseNaturalDate(text: string): string | null {
+        const result = chrono.parseDate(text);
+        if (!result) return null;
+        const year = result.getFullYear();
+        const month = String(result.getMonth() + 1).padStart(2, "0");
+        const day = String(result.getDate()).padStart(2, "0");
+        return `${year}-${month}-${day}`;
+      }
+
+      function parseCommandArgs(args: string) {
+        let remaining = args;
+        let section: string | undefined;
+        let scheduled: string | undefined;
+        let deadline: string | undefined;
+        let priority: number | undefined;
+        let state: string | undefined;
+
+        const sectionMatch = remaining.match(/@(\w+)/);
+        if (sectionMatch) {
+          section = sectionMatch[1];
+          remaining = remaining.replace(/@\w+/, "").trim();
+        }
+
+        const scheduledMatch = remaining.match(/scheduled:([^\s]+(?:\s+[^\s@:]+)*?)(?=\s+(?:deadline:|priority:|state:|@|$)|$)/i);
+        if (scheduledMatch) {
+          const dateStr = scheduledMatch[1].trim();
+          scheduled = parseNaturalDate(dateStr) || dateStr;
+          remaining = remaining.replace(scheduledMatch[0], "").trim();
+        }
+
+        const deadlineMatch = remaining.match(/deadline:([^\s]+(?:\s+[^\s@:]+)*?)(?=\s+(?:scheduled:|priority:|state:|@|$)|$)/i);
+        if (deadlineMatch) {
+          const dateStr = deadlineMatch[1].trim();
+          deadline = parseNaturalDate(dateStr) || dateStr;
+          remaining = remaining.replace(deadlineMatch[0], "").trim();
+        }
+
+        const priorityMatch = remaining.match(/priority:(\d)/i);
+        if (priorityMatch) {
+          priority = parseInt(priorityMatch[1], 10);
+          remaining = remaining.replace(priorityMatch[0], "").trim();
+        }
+
+        const stateMatch = remaining.match(/state:(TODO|NEXT|STRT|WAIT|DONE|CANX)/i);
+        if (stateMatch) {
+          state = stateMatch[1].toUpperCase();
+          remaining = remaining.replace(stateMatch[0], "").trim();
+        }
+
+        return { title: remaining.trim(), section, scheduled, deadline, priority, state };
+      }
+
+      test("parses title only", () => {
+        const result = parseCommandArgs("Buy groceries");
+        expect(result.title).toBe("Buy groceries");
+        expect(result.section).toBeUndefined();
+        expect(result.scheduled).toBeUndefined();
+      });
+
+      test("parses @Section", () => {
+        const result = parseCommandArgs("Buy groceries @Personal");
+        expect(result.title).toBe("Buy groceries");
+        expect(result.section).toBe("Personal");
+      });
+
+      test("parses scheduled: with ISO date", () => {
+        const result = parseCommandArgs("Task scheduled:2026-03-15");
+        expect(result.title).toBe("Task");
+        expect(result.scheduled).toBe("2026-03-15");
+      });
+
+      test("parses deadline: with ISO date", () => {
+        const result = parseCommandArgs("Task deadline:2026-04-01");
+        expect(result.title).toBe("Task");
+        expect(result.deadline).toBe("2026-04-01");
+      });
+
+      test("parses priority:", () => {
+        const result = parseCommandArgs("Task priority:2");
+        expect(result.title).toBe("Task");
+        expect(result.priority).toBe(2);
+      });
+
+      test("parses state:", () => {
+        const result = parseCommandArgs("Task state:NEXT");
+        expect(result.title).toBe("Task");
+        expect(result.state).toBe("NEXT");
+      });
+
+      test("parses all options combined", () => {
+        const result = parseCommandArgs("Complex task @Work scheduled:2026-03-15 deadline:2026-04-01 priority:1 state:NEXT");
+        expect(result.title).toBe("Complex task");
+        expect(result.section).toBe("Work");
+        expect(result.scheduled).toBe("2026-03-15");
+        expect(result.deadline).toBe("2026-04-01");
+        expect(result.priority).toBe(1);
+        expect(result.state).toBe("NEXT");
+      });
+
+      test("parses state case-insensitive", () => {
+        const result = parseCommandArgs("Task state:next");
+        expect(result.state).toBe("NEXT");
+      });
+
+      test("handles scheduled:tomorrow", () => {
+        const result = parseCommandArgs("Task scheduled:tomorrow");
+        expect(result.scheduled).toBeDefined();
+        expect(result.scheduled).toMatch(/^\d{4}-\d{2}-\d{2}$/);
+      });
+    });
+
+    // --- action validation ---
+    test("all tool actions are enumerated", () => {
+      const validActions = [
+        "list", "scheduled", "upcoming", "overdue", "search", "get",
+        "done", "state", "schedule", "deadline", "priority", "add", "append",
+        "sections", "statistics", "archive",
+        "inbox-list", "inbox-count", "inbox-add",
+        "refile-targets", "refile"
+      ];
+      expect(validActions.length).toBe(21);
+    });
+
+    // --- elisp escaping ---
+    test("elisp escaping handles double quotes", () => {
+      const heading = 'Task with "quotes"';
       const escaped = heading.replace(/"/g, '\\"');
-      expect(escaped).toBe('Task with \\"quotes\\" and \'apostrophes\'');
+      expect(escaped).toBe('Task with \\"quotes\\"');
+    });
+
+    test("elisp escaping handles single quotes", () => {
+      const heading = "Task with 'apostrophes'";
+      // Single quotes don't need escaping in double-quoted elisp strings
+      expect(heading).toContain("'");
+    });
+
+    test("elisp escaping handles backslashes", () => {
+      const heading = "Path\\to\\file";
+      const escaped = heading.replace(/\\/g, '\\\\');
+      expect(escaped).toBe("Path\\\\to\\\\file");
+    });
+
+    test("elisp escaping handles newlines", () => {
+      const content = "Line 1\nLine 2";
+      const escaped = content.replace(/\n/g, '\\n');
+      expect(escaped).toBe("Line 1\\nLine 2");
     });
   });
 
-  // Integration tests (require Emacs daemon)
+  // ==================================
+  // INTEGRATION TESTS (Emacs required)
+  // ==================================
+
   describe("integration tests", () => {
     beforeAll(() => {
       if (!canRunIntegrationTests) {
@@ -174,227 +463,522 @@ describe("org-todos extension", () => {
       }
     });
 
+    // --- List Operations ---
     test("list TODOs", () => {
       if (!canRunIntegrationTests) return;
-
       const result = execEmacs(`(pi/org-todo-list "${TEST_FILE}")`);
-
       expect(result.success).toBe(true);
       expect(Array.isArray(result.data)).toBe(true);
       expect(result.data.length).toBeGreaterThan(0);
-
-      // Should include TODO and NEXT but not DONE
       const states = result.data.map((t: any) => t.todo);
       expect(states).toContain("TODO");
       expect(states).toContain("NEXT");
     });
 
-    test("search TODOs", () => {
+    test("list TODOs with state filter", () => {
       if (!canRunIntegrationTests) return;
+      const result = execEmacs(`(pi/org-todo-list "${TEST_FILE}" "NEXT")`);
+      expect(result.success).toBe(true);
+      expect(result.data.length).toBeGreaterThan(0);
+      for (const todo of result.data) {
+        expect(todo.todo).toBe("NEXT");
+      }
+    });
 
+    test("list TODOs with comma-separated states", () => {
+      if (!canRunIntegrationTests) return;
+      const result = execEmacs(`(pi/org-todo-list "${TEST_FILE}" "NEXT,STRT")`);
+      expect(result.success).toBe(true);
+      for (const todo of result.data) {
+        expect(["NEXT", "STRT"]).toContain(todo.todo);
+      }
+    });
+
+    test("list all TODOs including DONE", () => {
+      if (!canRunIntegrationTests) return;
+      const result = execEmacs(`(pi/org-todo-list-all "${TEST_FILE}")`);
+      expect(result.success).toBe(true);
+      const states = result.data.map((t: any) => t.todo);
+      expect(states).toContain("DONE");
+    });
+
+    // --- Search ---
+    test("search TODOs by heading", () => {
+      if (!canRunIntegrationTests) return;
       const result = execEmacs(`(pi/org-todo-search "pipeline" "${TEST_FILE}")`);
-
       expect(result.success).toBe(true);
       expect(result.data.length).toBeGreaterThan(0);
       expect(result.data[0].heading).toContain("pipeline");
     });
 
+    test("search TODOs returns no results for nonexistent term", () => {
+      if (!canRunIntegrationTests) return;
+      const result = execEmacs(`(pi/org-todo-search "zzzznonexistent" "${TEST_FILE}")`);
+      expect(result.success).toBe(true);
+      // data may be null or empty array when no results
+      expect(result.data === null || result.data.length === 0).toBe(true);
+    });
+
+    // --- Get ---
     test("get specific TODO", () => {
       if (!canRunIntegrationTests) return;
-
-      const result = execEmacs(
-        `(pi/org-todo-get "Review PR for pipeline" "${TEST_FILE}")`
-      );
-
+      const result = execEmacs(`(pi/org-todo-get "Review PR for pipeline" "${TEST_FILE}")`);
       expect(result.success).toBe(true);
       expect(result.data.heading).toBe("Review PR for pipeline");
       expect(result.data.todo).toBe("TODO");
       expect(result.data.priority).toBe(2);
     });
 
-    test("get sections", () => {
+    test("get TODO includes content", () => {
       if (!canRunIntegrationTests) return;
-
-      const result = execEmacs(`(pi/org-todo-sections "${TEST_FILE}")`);
-
+      const result = execEmacs(`(pi/org-todo-get "Review PR for pipeline" "${TEST_FILE}")`);
       expect(result.success).toBe(true);
-      const sections = Array.isArray(result.data)
-        ? result.data
-        : Object.values(result.data);
-      expect(sections).toContain("Work");
-      expect(sections).toContain("Projects");
+      expect(result.data.content).toContain("test TODO with some content");
     });
 
+    test("get TODO includes properties", () => {
+      if (!canRunIntegrationTests) return;
+      const result = execEmacs(`(pi/org-todo-get "Review PR for pipeline" "${TEST_FILE}")`);
+      expect(result.success).toBe(true);
+      expect(result.data.properties).toBeDefined();
+      expect(result.data.properties.CREATED).toContain("2026-02-01");
+    });
+
+    // --- Sections ---
+    test("get sections", () => {
+      if (!canRunIntegrationTests) return;
+      const result = execEmacs(`(pi/org-todo-sections "${TEST_FILE}")`);
+      expect(result.success).toBe(true);
+      const sections = Array.isArray(result.data) ? result.data : Object.values(result.data);
+      expect(sections).toContain("Work");
+      expect(sections).toContain("Projects");
+      expect(sections).toContain("Personal");
+    });
+
+    test("get TODOs by section", () => {
+      if (!canRunIntegrationTests) return;
+      const result = execEmacs(`(pi/org-todo-by-section "Projects" "${TEST_FILE}")`);
+      expect(result.success).toBe(true);
+      expect(result.data.length).toBe(2);
+    });
+
+    // --- Statistics ---
     test("get statistics", () => {
       if (!canRunIntegrationTests) return;
-
       const result = execEmacs(`(pi/org-todo-statistics "${TEST_FILE}")`);
-
       expect(result.success).toBe(true);
       expect(result.data.total).toBeGreaterThan(0);
       expect(result.data.by_state).toBeDefined();
     });
 
+    // --- Write Operations (use fresh temp files) ---
     test("mark TODO as done", () => {
       if (!canRunIntegrationTests) return;
-
-      // Create a fresh test file with unique name
       const doneTestFile = join(tmpdir(), `pi-org-done-test-${Date.now()}.org`);
-      writeFileSync(
-        doneTestFile,
-        `* Work
-** TODO Task to complete
-`
-      );
-
+      writeFileSync(doneTestFile, `* Work\n** TODO Task to complete\n`);
       try {
-        const result = execEmacs(
-          `(pi/org-todo-done "Task to complete" "${doneTestFile}")`
-        );
-
+        const result = execEmacs(`(pi/org-todo-done "Task to complete" "${doneTestFile}")`);
         expect(result.success).toBe(true);
         expect(result.data.state).toBe("DONE");
-
-        // Verify file content directly (more reliable than re-reading via elisp)
-        const { readFileSync } = require("fs");
         const content = readFileSync(doneTestFile, "utf-8");
         expect(content).toContain("DONE Task to complete");
       } finally {
-        if (existsSync(doneTestFile)) {
-          unlinkSync(doneTestFile);
-        }
+        if (existsSync(doneTestFile)) unlinkSync(doneTestFile);
       }
     });
 
-    test("change TODO state", () => {
+    test("change TODO state to NEXT", () => {
       if (!canRunIntegrationTests) return;
-
-      // Create a fresh test file with unique name
       const stateTestFile = join(tmpdir(), `pi-org-state-test-${Date.now()}.org`);
-      writeFileSync(
-        stateTestFile,
-        `* Work
-** TODO Task to change
-`
-      );
-
+      writeFileSync(stateTestFile, `* Work\n** TODO Task to change\n`);
       try {
-        const result = execEmacs(
-          `(pi/org-todo-state "Task to change" "NEXT" "${stateTestFile}")`
-        );
-
+        const result = execEmacs(`(pi/org-todo-state "Task to change" "NEXT" "${stateTestFile}")`);
         expect(result.success).toBe(true);
         expect(result.data.state).toBe("NEXT");
-
-        // Verify file content directly
-        const { readFileSync } = require("fs");
         const content = readFileSync(stateTestFile, "utf-8");
         expect(content).toContain("NEXT Task to change");
       } finally {
-        if (existsSync(stateTestFile)) {
-          unlinkSync(stateTestFile);
-        }
+        if (existsSync(stateTestFile)) unlinkSync(stateTestFile);
+      }
+    });
+
+    test("change TODO state to STRT", () => {
+      if (!canRunIntegrationTests) return;
+      const f = join(tmpdir(), `pi-org-strt-${Date.now()}.org`);
+      writeFileSync(f, `* Work\n** TODO Task\n`);
+      try {
+        const result = execEmacs(`(pi/org-todo-state "Task" "STRT" "${f}")`);
+        expect(result.success).toBe(true);
+        expect(result.data.state).toBe("STRT");
+      } finally {
+        if (existsSync(f)) unlinkSync(f);
+      }
+    });
+
+    test("change TODO state to WAIT", () => {
+      if (!canRunIntegrationTests) return;
+      const f = join(tmpdir(), `pi-org-wait-${Date.now()}.org`);
+      writeFileSync(f, `* Work\n** TODO Task\n`);
+      try {
+        const result = execEmacs(`(pi/org-todo-state "Task" "WAIT" "${f}")`);
+        expect(result.success).toBe(true);
+        expect(result.data.state).toBe("WAIT");
+      } finally {
+        if (existsSync(f)) unlinkSync(f);
+      }
+    });
+
+    test("change TODO state to CANX", () => {
+      if (!canRunIntegrationTests) return;
+      const f = join(tmpdir(), `pi-org-canx-${Date.now()}.org`);
+      writeFileSync(f, `* Work\n** TODO Task\n`);
+      try {
+        const result = execEmacs(`(pi/org-todo-state "Task" "CANX" "${f}")`);
+        expect(result.success).toBe(true);
+        expect(result.data.state).toBe("CANX");
+      } finally {
+        if (existsSync(f)) unlinkSync(f);
       }
     });
 
     test("add new TODO", () => {
       if (!canRunIntegrationTests) return;
-
-      // Create a fresh test file with unique name
       const addTestFile = join(tmpdir(), `pi-org-add-test-${Date.now()}.org`);
-      writeFileSync(
-        addTestFile,
-        `* Work
-** TODO Existing task
-`
-      );
-
+      writeFileSync(addTestFile, `* Work\n** TODO Existing task\n`);
       try {
         const result = execEmacs(
           `(pi/org-todo-add "New task from test" "Work" "${addTestFile}" "2026-03-01" 2 '("test"))`
         );
-
         expect(result.success).toBe(true);
         expect(result.data.heading).toBe("New task from test");
-
-        // Verify file content directly
-        const { readFileSync } = require("fs");
         const content = readFileSync(addTestFile, "utf-8");
-        expect(content).toContain("TODO");
         expect(content).toContain("New task from test");
       } finally {
-        if (existsSync(addTestFile)) {
-          unlinkSync(addTestFile);
-        }
+        if (existsSync(addTestFile)) unlinkSync(addTestFile);
       }
     });
 
+    test("add TODO with tags", () => {
+      if (!canRunIntegrationTests) return;
+      const f = join(tmpdir(), `pi-org-add-tags-${Date.now()}.org`);
+      writeFileSync(f, `* Work\n`);
+      try {
+        const result = execEmacs(`(pi/org-todo-add "Tagged task" "Work" "${f}" nil nil '("review" "code"))`);
+        expect(result.success).toBe(true);
+        const content = readFileSync(f, "utf-8");
+        expect(content).toContain(":review:code:");
+      } finally {
+        if (existsSync(f)) unlinkSync(f);
+      }
+    });
+
+    test("add TODO to nonexistent section fails", () => {
+      if (!canRunIntegrationTests) return;
+      const f = join(tmpdir(), `pi-org-add-fail-${Date.now()}.org`);
+      writeFileSync(f, `* Work\n`);
+      try {
+        const result = execEmacs(`(pi/org-todo-add "Task" "Nonexistent" "${f}")`);
+        expect(result.success).toBe(false);
+      } finally {
+        if (existsSync(f)) unlinkSync(f);
+      }
+    });
+
+    // --- Schedule & Deadline ---
+    test("schedule a TODO", () => {
+      if (!canRunIntegrationTests) return;
+      const f = join(tmpdir(), `pi-org-sched-${Date.now()}.org`);
+      writeFileSync(f, `* Work\n** TODO Schedule me\n`);
+      try {
+        const result = execEmacs(`(pi/org-todo-schedule "Schedule me" "2026-06-15" "${f}")`);
+        expect(result.success).toBe(true);
+        const content = readFileSync(f, "utf-8");
+        expect(content).toContain("SCHEDULED:");
+        expect(content).toContain("2026-06-15");
+      } finally {
+        if (existsSync(f)) unlinkSync(f);
+      }
+    });
+
+    test("set deadline on a TODO", () => {
+      if (!canRunIntegrationTests) return;
+      const f = join(tmpdir(), `pi-org-deadline-${Date.now()}.org`);
+      writeFileSync(f, `* Work\n** TODO Deadline me\n`);
+      try {
+        const result = execEmacs(`(pi/org-todo-deadline "Deadline me" "2026-07-01" "${f}")`);
+        expect(result.success).toBe(true);
+        const content = readFileSync(f, "utf-8");
+        expect(content).toContain("DEADLINE:");
+        expect(content).toContain("2026-07-01");
+      } finally {
+        if (existsSync(f)) unlinkSync(f);
+      }
+    });
+
+    // --- Priority ---
+    test("set priority on a TODO", () => {
+      if (!canRunIntegrationTests) return;
+      const f = join(tmpdir(), `pi-org-prio-${Date.now()}.org`);
+      writeFileSync(f, `* Work\n** TODO Priority me\n`);
+      try {
+        const result = execEmacs(`(pi/org-todo-priority "Priority me" 1 "${f}")`);
+        expect(result.success).toBe(true);
+        const content = readFileSync(f, "utf-8");
+        expect(content).toContain("[#1]");
+      } finally {
+        if (existsSync(f)) unlinkSync(f);
+      }
+    });
+
+    // --- Append ---
+    test("append content to a TODO", () => {
+      if (!canRunIntegrationTests) return;
+      const f = join(tmpdir(), `pi-org-append-${Date.now()}.org`);
+      writeFileSync(f, `* Work\n** TODO Append to me\n`);
+      try {
+        const result = execEmacs(`(pi/org-todo-append "Append to me" "Added content here" "${f}")`);
+        expect(result.success).toBe(true);
+        const content = readFileSync(f, "utf-8");
+        expect(content).toContain("Added content here");
+      } finally {
+        if (existsSync(f)) unlinkSync(f);
+      }
+    });
+
+    // --- Tags ---
+    test("add tags to a TODO", () => {
+      if (!canRunIntegrationTests) return;
+      const f = join(tmpdir(), `pi-org-tags-${Date.now()}.org`);
+      writeFileSync(f, `* Work\n** TODO Tag me\n`);
+      try {
+        const result = execEmacs(`(pi/org-todo-add-tags "Tag me" '("new" "tags") "${f}")`);
+        expect(result.success).toBe(true);
+        const content = readFileSync(f, "utf-8");
+        expect(content).toContain(":new:");
+        expect(content).toContain(":tags:");
+      } finally {
+        if (existsSync(f)) unlinkSync(f);
+      }
+    });
+
+    test("remove tags from a TODO", () => {
+      if (!canRunIntegrationTests) return;
+      const f = join(tmpdir(), `pi-org-rmtag-${Date.now()}.org`);
+      writeFileSync(f, `* Work\n** TODO Remove tag :urgent:review:\n`);
+      try {
+        const result = execEmacs(`(pi/org-todo-remove-tags "Remove tag" '("urgent") "${f}")`);
+        expect(result.success).toBe(true);
+        const content = readFileSync(f, "utf-8");
+        expect(content).not.toContain(":urgent:");
+        expect(content).toContain(":review:");
+      } finally {
+        if (existsSync(f)) unlinkSync(f);
+      }
+    });
+
+    // --- Properties ---
+    test("set and get property", () => {
+      if (!canRunIntegrationTests) return;
+      const f = join(tmpdir(), `pi-org-prop-${Date.now()}.org`);
+      writeFileSync(f, `* Work\n** TODO Property me\n`);
+      try {
+        const setResult = execEmacs(`(pi/org-todo-set-property "Property me" "EFFORT" "2h" "${f}")`);
+        expect(setResult.success).toBe(true);
+        const getResult = execEmacs(`(pi/org-todo-get-property "Property me" "EFFORT" "${f}")`);
+        expect(getResult.success).toBe(true);
+        expect(getResult.data.value).toBe("2h");
+      } finally {
+        if (existsSync(f)) unlinkSync(f);
+      }
+    });
+
+    // --- Inbox ---
+    test("inbox-all on empty inbox", () => {
+      if (!canRunIntegrationTests) return;
+      const f = join(tmpdir(), `pi-org-inbox-empty-${Date.now()}.org`);
+      writeFileSync(f, `#+title: Inbox\n`);
+      try {
+        const result = execEmacs(`(pi/org-todo-inbox-all "${f}")`);
+        expect(result.success).toBe(true);
+        // data may be null or empty array for empty inbox
+        expect(result.data === null || result.data.length === 0).toBe(true);
+      } finally {
+        if (existsSync(f)) unlinkSync(f);
+      }
+    });
+
+    test("inbox-all with mixed items", () => {
+      if (!canRunIntegrationTests) return;
+      const f = join(tmpdir(), `pi-org-inbox-${Date.now()}.org`);
+      writeFileSync(f, `#+title: Inbox\n* TODO Task one\n* Link item\n* TODO Task two\n`);
+      try {
+        const result = execEmacs(`(pi/org-todo-inbox-all "${f}")`);
+        expect(result.success).toBe(true);
+        expect(result.data.length).toBe(3);
+        const todos = result.data.filter((i: any) => i.todo);
+        expect(todos.length).toBe(2);
+      } finally {
+        if (existsSync(f)) unlinkSync(f);
+      }
+    });
+
+    // --- Refile ---
+    test("get refile targets", () => {
+      if (!canRunIntegrationTests) return;
+      const result = execEmacs(`(pi/org-todo-get-refile-targets "${TEST_FILE}")`);
+      expect(result.success).toBe(true);
+      expect(result.data.length).toBeGreaterThan(0);
+      for (const target of result.data) {
+        expect(target.section).toBeDefined();
+        expect(target.position).toBeDefined();
+      }
+    });
+
+    test("refile entry from inbox to target", () => {
+      if (!canRunIntegrationTests) return;
+      const inbox = join(tmpdir(), `pi-org-refile-inbox-${Date.now()}.org`);
+      const target = join(tmpdir(), `pi-org-refile-target-${Date.now()}.org`);
+      writeFileSync(inbox, `* TODO Refile me\n`);
+      writeFileSync(target, `* Work\n** TODO Existing\n* Projects\n`);
+      try {
+        const result = execEmacs(`(pi/org-todo-refile "Refile me" "Work" "${inbox}" "${target}")`);
+        expect(result.success).toBe(true);
+        // Verify it's gone from inbox
+        const inboxContent = readFileSync(inbox, "utf-8");
+        expect(inboxContent).not.toContain("Refile me");
+        // Verify it's in target
+        const targetContent = readFileSync(target, "utf-8");
+        expect(targetContent).toContain("Refile me");
+      } finally {
+        if (existsSync(inbox)) unlinkSync(inbox);
+        if (existsSync(target)) unlinkSync(target);
+      }
+    });
+
+    // --- Error Handling ---
     test("error handling for non-existent heading", () => {
       if (!canRunIntegrationTests) return;
-
-      const result = execEmacs(
-        `(pi/org-todo-done "This heading does not exist" "${TEST_FILE}")`
-      );
-
+      const result = execEmacs(`(pi/org-todo-done "This heading does not exist" "${TEST_FILE}")`);
       expect(result.success).toBe(false);
       expect(result.error).toContain("not found");
     });
 
     test("error handling for non-existent file", () => {
       if (!canRunIntegrationTests) return;
-
-      const result = execEmacs(
-        `(pi/org-todo-list "/nonexistent/file.org")`
-      );
-
+      const result = execEmacs(`(pi/org-todo-list "/nonexistent/file.org")`);
       expect(result.success).toBe(false);
     });
+
+    test("error handling for schedule on non-existent heading", () => {
+      if (!canRunIntegrationTests) return;
+      const result = execEmacs(`(pi/org-todo-schedule "Nonexistent" "2026-03-01" "${TEST_FILE}")`);
+      expect(result.success).toBe(false);
+    });
+
+    test("error handling for deadline on non-existent heading", () => {
+      if (!canRunIntegrationTests) return;
+      const result = execEmacs(`(pi/org-todo-deadline "Nonexistent" "2026-03-01" "${TEST_FILE}")`);
+      expect(result.success).toBe(false);
+    });
+
+    test("error handling for priority on non-existent heading", () => {
+      if (!canRunIntegrationTests) return;
+      const result = execEmacs(`(pi/org-todo-priority "Nonexistent" 1 "${TEST_FILE}")`);
+      expect(result.success).toBe(false);
+    });
+
+    test("error handling for append on non-existent heading", () => {
+      if (!canRunIntegrationTests) return;
+      const result = execEmacs(`(pi/org-todo-append "Nonexistent" "content" "${TEST_FILE}")`);
+      expect(result.success).toBe(false);
+    });
+
+    // --- Sequential Operations ---
+    test("add then mark done (buffer sync)", () => {
+      if (!canRunIntegrationTests) return;
+      const f = join(tmpdir(), `pi-org-seq-${Date.now()}.org`);
+      writeFileSync(f, `* Work\n`);
+      try {
+        // Add
+        const addResult = execEmacs(`(pi/org-todo-add "Sequential task" "Work" "${f}")`);
+        expect(addResult.success).toBe(true);
+        // Done
+        const doneResult = execEmacs(`(pi/org-todo-done "Sequential task" "${f}")`);
+        expect(doneResult.success).toBe(true);
+        // Verify on disk
+        const content = readFileSync(f, "utf-8");
+        expect(content).toContain("DONE Sequential task");
+      } finally {
+        if (existsSync(f)) unlinkSync(f);
+      }
+    });
+
+    test("add, schedule, set priority, then verify", () => {
+      if (!canRunIntegrationTests) return;
+      const f = join(tmpdir(), `pi-org-multi-${Date.now()}.org`);
+      writeFileSync(f, `* Work\n`);
+      try {
+        execEmacs(`(pi/org-todo-add "Multi task" "Work" "${f}")`);
+        execEmacs(`(pi/org-todo-schedule "Multi task" "2026-08-15" "${f}")`);
+        execEmacs(`(pi/org-todo-priority "Multi task" 1 "${f}")`);
+        const getResult = execEmacs(`(pi/org-todo-get "Multi task" "${f}")`);
+        expect(getResult.success).toBe(true);
+        expect(getResult.data.priority).toBe(1);
+        expect(getResult.data.scheduled).toContain("2026-08-15");
+      } finally {
+        if (existsSync(f)) unlinkSync(f);
+      }
+    });
   });
 });
 
-// Tests for new command parsing
+// --- Command parsing regression tests ---
 describe("command parsing", () => {
-  // Import parseCommandArgs by extracting the logic
-  // Since it's not exported, we test the patterns directly
-  
-  test("parseNaturalDate patterns", () => {
-    const chrono = require("chrono-node");
-    
-    // Test chrono works
+  test("chrono-node parses tomorrow", () => {
     const tomorrow = chrono.parseDate("tomorrow");
-    expect(tomorrow).toBeDefined();
-    expect(tomorrow.getDate()).toBe(new Date().getDate() + 1);
-    
-    const nextFriday = chrono.parseDate("next friday");
-    expect(nextFriday).toBeDefined();
-    expect(nextFriday.getDay()).toBe(5); // Friday
-    
-    const specific = chrono.parseDate("2026-03-15");
-    expect(specific).toBeDefined();
-    expect(specific.getMonth()).toBe(2); // March (0-indexed)
+    expect(tomorrow).not.toBeNull();
+    expect(tomorrow!.getDate()).toBe(new Date().getDate() + 1);
   });
 
-  test("command arg patterns", () => {
-    // Test regex patterns used in parseCommandArgs
-    
-    // @Section pattern
-    const sectionMatch = "Buy milk @Personal".match(/@(\w+)/);
-    expect(sectionMatch).toBeDefined();
-    expect(sectionMatch![1]).toBe("Personal");
-    
-    // scheduled: pattern
-    const schedMatch = "Task scheduled:tomorrow".match(/scheduled:([^\s]+)/i);
-    expect(schedMatch).toBeDefined();
-    expect(schedMatch![1]).toBe("tomorrow");
-    
-    // priority: pattern
-    const prioMatch = "Task priority:2".match(/priority:(\d)/i);
-    expect(prioMatch).toBeDefined();
-    expect(prioMatch![1]).toBe("2");
-    
-    // state: pattern
-    const stateMatch = "Task state:NEXT".match(/state:(TODO|NEXT|STRT|WAIT|DONE|CANX)/i);
-    expect(stateMatch).toBeDefined();
-    expect(stateMatch![1]).toBe("NEXT");
+  test("chrono-node parses next friday", () => {
+    const nextFriday = chrono.parseDate("next friday");
+    expect(nextFriday).not.toBeNull();
+    expect(nextFriday!.getDay()).toBe(5);
+  });
+
+  test("chrono-node parses ISO date", () => {
+    const specific = chrono.parseDate("2026-03-15");
+    expect(specific).not.toBeNull();
+    expect(specific!.getMonth()).toBe(2); // March (0-indexed)
+  });
+
+  test("@Section pattern", () => {
+    const match = "Buy milk @Personal".match(/@(\w+)/);
+    expect(match).toBeDefined();
+    expect(match![1]).toBe("Personal");
+  });
+
+  test("scheduled: pattern", () => {
+    const match = "Task scheduled:tomorrow".match(/scheduled:([^\s]+)/i);
+    expect(match).toBeDefined();
+    expect(match![1]).toBe("tomorrow");
+  });
+
+  test("priority: pattern", () => {
+    const match = "Task priority:2".match(/priority:(\d)/i);
+    expect(match).toBeDefined();
+    expect(match![1]).toBe("2");
+  });
+
+  test("state: pattern", () => {
+    const match = "Task state:NEXT".match(/state:(TODO|NEXT|STRT|WAIT|DONE|CANX)/i);
+    expect(match).toBeDefined();
+    expect(match![1]).toBe("NEXT");
+  });
+
+  test("state: pattern case insensitive", () => {
+    const match = "Task state:done".match(/state:(TODO|NEXT|STRT|WAIT|DONE|CANX)/i);
+    expect(match).toBeDefined();
+    expect(match![1]).toBe("done");
   });
 });
dots/pi/agent/extensions/org-todos/Makefile
@@ -9,7 +9,7 @@
 #   make check-daemon  - Check if Emacs daemon is running
 #   make clean         - Clean build artifacts
 
-SHELL := /bin/bash
+SHELL := bash
 
 # Paths
 ELISP_DIR := ../../../../config/emacs/site-lisp
dots/pi/agent/extensions/org-todos/package.json
@@ -1,7 +1,14 @@
 {
   "name": "org-todos",
   "version": "1.0.0",
+  "type": "module",
+  "scripts": {
+    "test": "bun test index.test.ts"
+  },
   "dependencies": {
     "chrono-node": "^2.7.0"
+  },
+  "devDependencies": {
+    "bun-types": "^1.0.0"
   }
 }
dots/pi/agent/extensions/org-todos/tsconfig.json
@@ -0,0 +1,12 @@
+{
+  "compilerOptions": {
+    "types": ["bun-types"],
+    "module": "esnext",
+    "moduleResolution": "bundler",
+    "target": "esnext",
+    "strict": true,
+    "esModuleInterop": true,
+    "skipLibCheck": true
+  },
+  "include": ["*.ts"]
+}