aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.ai/scripts/tests/test-todo-cleanup.el24
-rw-r--r--.ai/scripts/todo-cleanup.el12
2 files changed, 31 insertions, 5 deletions
diff --git a/.ai/scripts/tests/test-todo-cleanup.el b/.ai/scripts/tests/test-todo-cleanup.el
index 2a9184d..5d43f97 100644
--- a/.ai/scripts/tests/test-todo-cleanup.el
+++ b/.ai/scripts/tests/test-todo-cleanup.el
@@ -417,6 +417,16 @@ in ISSUES, in document order."
*** TODO [#B] Child equal
")
+;; A dated-log heading inside a parent task whose title quotes other priorities
+;; in =[#X]= verbatim. Those quoted cookies must NOT be read as the heading's
+;; own priority — the cookie has to sit in canonical position to count.
+(defconst tc-test--sync-cookie-in-title "\
+* Open Work
+** TODO [#B] Parent
+*** 2026-05-14 Reprioritized children =[#D]= → =[#B]= to match parent
+*** TODO [#D] Regular drifted child
+")
+
;;; ---------------------------------------------------------------------------
;;; Sync-child-priority tests
@@ -466,13 +476,25 @@ in ISSUES, in document order."
(should (= 0 (plist-get out :bumped)))
(should (equal tc-test--sync-no-change (plist-get out :result)))))
+(ert-deftest tc-sync-ignores-cookie-shaped-text-in-title ()
+ (let* ((out (tc-test--sync tc-test--sync-cookie-in-title))
+ (res (plist-get out :result)))
+ ;; Only the real drifted child bumps; the dated-log heading with
+ ;; =[#D]= / =[#B]= verbatim text in its title is untouched.
+ (should (= 1 (plist-get out :bumped)))
+ (should (equal "B" (tc-test--priority-of res "Regular drifted child")))
+ ;; Substring still appears in the dated-log heading; the heading itself
+ ;; was not rewritten.
+ (should (string-match-p "Reprioritized children =\\[#D\\]= → =\\[#B\\]= to match parent" res))))
+
(ert-deftest tc-sync-is-idempotent ()
(dolist (fixture (list tc-test--sync-basic
tc-test--sync-multi
tc-test--sync-no-sync-tag
tc-test--sync-priority-less-parent
tc-test--sync-cascade
- tc-test--sync-no-change))
+ tc-test--sync-no-change
+ tc-test--sync-cookie-in-title))
(let ((once (plist-get (tc-test--sync fixture 1) :result))
(twice (plist-get (tc-test--sync fixture 2) :result)))
(should (equal once twice)))))
diff --git a/.ai/scripts/todo-cleanup.el b/.ai/scripts/todo-cleanup.el
index 97be96e..11851b2 100644
--- a/.ai/scripts/todo-cleanup.el
+++ b/.ai/scripts/todo-cleanup.el
@@ -254,12 +254,16 @@ are reported but not performed."
(defun tc--heading-priority-letter ()
"Return the priority letter (a character) on the heading at point, or nil
-if the heading has no priority cookie."
+if the heading has no priority cookie in canonical position.
+
+Uses `org-heading-components' rather than regexing the whole line, because
+the cookie must sit right after the stars or the optional TODO keyword —
+otherwise `[#X]'-shaped text inside the title (a dated log entry like
+\"... reprioritized =[#D]= → =[#B]= to match parent\") gets misread as a
+real cookie."
(save-excursion
(org-back-to-heading t)
- (let ((eol (line-end-position)))
- (when (re-search-forward tc--priority-cookie-regexp eol t)
- (string-to-char (match-string 1))))))
+ (nth 3 (org-heading-components))))
(defun tc--priority-lower-p (child parent)
"Non-nil when CHILD priority letter ranks lower than PARENT — i.e. later in