aboutsummaryrefslogtreecommitdiff
path: root/.ai/scripts/todo-cleanup.el
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-05-15 16:16:18 -0500
committerCraig Jennings <c@cjennings.net>2026-05-15 16:16:18 -0500
commitee721ee96f984ccd38233309f0dfe6362057e644 (patch)
treef84a3b21ae846c82a2677a59f54947ee5b557174 /.ai/scripts/todo-cleanup.el
parent421b17a15219c7061ee92c07451993965fad88ea (diff)
downloadrulesets-ee721ee96f984ccd38233309f0dfe6362057e644.tar.gz
rulesets-ee721ee96f984ccd38233309f0dfe6362057e644.zip
chore(ai): sync scripts and workflows from claude-templates
- todo-cleanup.el: :no-sync: tag now inherits down the outline tree - task-review.org: completion procedure scoped to top-level entries - cj-scan.py + cj-remove-block.py: helpers for cj-comment block handling - inbox-send.py: cross-project messaging via inbox directories
Diffstat (limited to '.ai/scripts/todo-cleanup.el')
-rw-r--r--.ai/scripts/todo-cleanup.el46
1 files changed, 34 insertions, 12 deletions
diff --git a/.ai/scripts/todo-cleanup.el b/.ai/scripts/todo-cleanup.el
index 11851b2..569e7c7 100644
--- a/.ai/scripts/todo-cleanup.el
+++ b/.ai/scripts/todo-cleanup.el
@@ -41,7 +41,10 @@
;; match a child. Children with no priority cookie at all are left alone, as
;; are parents with no priority cookie. A child can opt out of being bumped
;; by carrying the `:no-sync:' tag — useful for `Follow-up:'/`Spike:' children
-;; that are deliberately deprioritized. Because the walk visits parents
+;; that are deliberately deprioritized. The opt-out inherits down the tree:
+;; if any ancestor heading carries `:no-sync:', every descendant under it is
+;; skipped, so tagging a top-level PROJECT once is enough to keep its whole
+;; subtree from cascading. Because the walk visits parents
;; before their descendants in document order, a multi-level chain
;; ([#A] → [#B] → [#D]) collapses to the top priority in a single pass.
;; --check-child-priority is the report-only alias for --sync-child-priority
@@ -60,7 +63,9 @@
"Regexp matching an org priority cookie. Match group 1 is the letter.")
(defconst tc-no-sync-tag "no-sync"
- "Org tag a child heading carries to opt out of `--sync-child-priority'.")
+ "Org tag that opts a heading and all its descendants out of
+`--sync-child-priority'. Inherits down: a tag on an ancestor counts for
+every heading below it.")
(defvar tc-fixes 0)
(defvar tc-archived 0)
@@ -271,20 +276,36 @@ the alphabet, since A is highest in org's default priority scheme."
(and child parent (> child parent)))
(defun tc--heading-has-no-sync-tag-p ()
- "Non-nil when the heading line at point carries `:no-sync:' as a trailing
-tag-style marker. Uses a literal regex match rather than `org-get-tags'
-because org's default tag character class (`org-tag-re') excludes hyphens —
+ "Non-nil when the heading line at point carries the literal substring
+`:no-sync:'. Uses a literal regex match rather than `org-get-tags' because
+org's default tag character class (`org-tag-re') excludes hyphens —
`no-sync' isn't recognized as a real org tag in batch mode unless the user
has extended that regex. The literal `:no-sync:' is what wrap-up sessions
-actually type, so match it directly: any `:no-sync:' preceded by whitespace
-on the heading line counts."
+actually type, so match it directly anywhere on the heading line; the
+heading line is scoped narrowly enough that a false-positive match in title
+text is unlikely, and the cost would only be skipping a bump."
(save-excursion
(org-back-to-heading t)
(let ((line (buffer-substring-no-properties
(line-beginning-position) (line-end-position))))
- (string-match-p (format "[ \t]:%s:" (regexp-quote tc-no-sync-tag))
+ (string-match-p (format ":%s:" (regexp-quote tc-no-sync-tag))
line))))
+(defun tc--ancestor-or-self-has-no-sync-tag-p ()
+ "Non-nil when the heading at point, or any strict ancestor, carries the
+literal `:no-sync:' tag on its own heading line. Walks up the outline
+chain via `org-up-heading-safe', which returns nil at the top level
+instead of erroring."
+ (save-excursion
+ (org-back-to-heading t)
+ (catch 'found
+ (when (tc--heading-has-no-sync-tag-p)
+ (throw 'found t))
+ (while (org-up-heading-safe)
+ (when (tc--heading-has-no-sync-tag-p)
+ (throw 'found t)))
+ nil)))
+
(defun tc--set-heading-priority (letter)
"Rewrite the priority cookie on the heading at point to LETTER (a character)."
(save-excursion
@@ -313,9 +334,10 @@ level deeper than the parent."
(defun tc-sync-child-priority-at-heading ()
"If the heading at point carries a priority cookie, bump any direct child
-heading whose own priority is lower, skipping children tagged
-`tc-no-sync-tag'. A priority-less parent is a no-op; priority-less children
-are left untouched (down-only does not invent priorities)."
+heading whose own priority is lower, skipping children whose own heading
+or any ancestor carries `tc-no-sync-tag'. A priority-less parent is a
+no-op; priority-less children are left untouched (down-only does not
+invent priorities)."
(let ((parent (tc--heading-priority-letter)))
(when parent
(let ((parent-heading (org-get-heading t t t t)))
@@ -325,7 +347,7 @@ are left untouched (down-only does not invent priorities)."
(let ((child (tc--heading-priority-letter)))
(when (and child
(tc--priority-lower-p child parent)
- (not (tc--heading-has-no-sync-tag-p)))
+ (not (tc--ancestor-or-self-has-no-sync-tag-p)))
(let ((child-heading (org-get-heading t t t t))
(child-line (line-number-at-pos)))
(cl-incf tc-bumped)