diff options
| author | Craig Jennings <c@cjennings.net> | 2026-06-20 23:28:47 -0400 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-06-20 23:28:47 -0400 |
| commit | f6dde4e0fe21022966196e19d535f2bb7abcfcdb (patch) | |
| tree | 5bb56e120e0be84e66eaf82bb8b12c1837b0747e /claude-templates | |
| parent | 76e55591e2a66e8ef42ee6e4535882545ee3d33b (diff) | |
| download | rulesets-f6dde4e0fe21022966196e19d535f2bb7abcfcdb.tar.gz rulesets-f6dde4e0fe21022966196e19d535f2bb7abcfcdb.zip | |
feat(lint-org): flag level-2 dated headers as a completion defect
A `** <YYYY-MM-DD> …` heading carries no keyword, so todo-cleanup's --archive-done can never archive it and task-review drops it from selection. The new level-2-dated-header check (custom, like org-table-standard) emits a judgment item per offending heading so the wrap-up sweep routes it to the next morning's review. Judgment-only, never auto-fixed: the repair needs a DONE-vs-CANCELLED call and the original heading text. Three ERT cases cover it (flagged at level 2, clean for DONE+CLOSED, clean for a level-3 dated entry).
Diffstat (limited to 'claude-templates')
| -rw-r--r-- | claude-templates/.ai/scripts/lint-org.el | 27 | ||||
| -rw-r--r-- | claude-templates/.ai/scripts/tests/test-lint-org.el | 26 |
2 files changed, 53 insertions, 0 deletions
diff --git a/claude-templates/.ai/scripts/lint-org.el b/claude-templates/.ai/scripts/lint-org.el index 8f55cc6..3633dba 100644 --- a/claude-templates/.ai/scripts/lint-org.el +++ b/claude-templates/.ai/scripts/lint-org.el @@ -368,6 +368,31 @@ Emits one judgment item per violating table." (string-join violations "; "))))))))) ;;; --------------------------------------------------------------------------- +;;; level-2 dated-header check (claude-rules/todo-format.md) +;; +;; A completed task or resolved VERIFY at level 2 must carry a terminal +;; keyword (DONE/CANCELLED + CLOSED:), never a dated heading. A `** <date>' +;; header has no keyword, so todo-cleanup's --archive-done can never archive +;; it (it accumulates in Open Work forever) and task-review drops it from +;; selection. Judgment-only, never auto-fixed: the repair needs a +;; DONE-vs-CANCELLED call and the original heading text, which is a judgment +;; the sweep can't make. Targets todo/task files; a dated-log-format org +;; file using `** <date>' headings intentionally will false-positive here, in +;; which case the human dismisses the judgment item. + +(defun lo--check-level2-dated-headers () + "Flag level-2 headings whose text begins with a YYYY-MM-DD date. +Emits one judgment item per offending heading (checker +`level-2-dated-header')." + (save-excursion + (goto-char (point-min)) + (while (re-search-forward + "^\\*\\* \\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}\\)" nil t) + (lo--emit-judgment + 'level-2-dated-header (line-number-at-pos) + "level-2 dated header is a completion defect (todo-format.md): a ** task or VERIFY closes with DONE/CANCELLED + CLOSED:, not a dated heading — convert it so --archive-done can archive it")))) + +;;; --------------------------------------------------------------------------- ;;; File processing (defun lo--backup (file) @@ -401,6 +426,8 @@ left unmodified and mechanical entries are recorded with :preview t." ;; After org-lint items: the custom table-standard scan. Runs on the ;; post-fix buffer; judgment-only, so order doesn't perturb fixes. (lo--check-tables) + ;; Same shape: flag level-2 dated headers (completion defects). + (lo--check-level2-dated-headers) (when (and (not lo-check-only) (buffer-modified-p)) (save-buffer))) (with-current-buffer buf (set-buffer-modified-p nil)) diff --git a/claude-templates/.ai/scripts/tests/test-lint-org.el b/claude-templates/.ai/scripts/tests/test-lint-org.el index 3a83602..242c35c 100644 --- a/claude-templates/.ai/scripts/tests/test-lint-org.el +++ b/claude-templates/.ai/scripts/tests/test-lint-org.el @@ -659,5 +659,31 @@ missing-rules violation." (judgments (lo-test--judgments (plist-get run :issues)))) (should-not (memq 'org-table-standard (lo-test--checkers judgments))))) +;;; --------------------------------------------------------------------------- +;;; level-2 dated-header check (claude-rules/todo-format.md) + +(ert-deftest lo-level2-dated-header-is-judgment () + "A level-2 heading beginning with a YYYY-MM-DD date is flagged." + (let* ((out (lo-test--run + "* Open Work\n\n** 2026-06-20 Sat @ 10:00:00 -0500 Something resolved\nBody.\n")) + (res (plist-get out :result)) + (judgments (lo-test--judgments (plist-get out :issues)))) + (should (= 0 (plist-get out :fixes))) ; judgment-only, never auto-fixed + (should (member 'level-2-dated-header (lo-test--checkers judgments))))) + +(ert-deftest lo-level2-done-task-not-flagged () + "A level-2 task closed with a terminal keyword + CLOSED: is fine." + (let* ((out (lo-test--run + "* Open Work\n\n** DONE [#B] Something resolved\nCLOSED: [2026-06-20 Sat]\nBody.\n")) + (judgments (lo-test--judgments (plist-get out :issues)))) + (should-not (member 'level-2-dated-header (lo-test--checkers judgments))))) + +(ert-deftest lo-level3-dated-entry-not-flagged () + "A dated event-log entry at level 3 is the correct sub-task shape, not a defect." + (let* ((out (lo-test--run + "* Open Work\n\n** TODO [#B] Parent task\n*** 2026-06-20 Sat @ 10:00:00 -0500 sub-entry landed\nBody.\n")) + (judgments (lo-test--judgments (plist-get out :issues)))) + (should-not (member 'level-2-dated-header (lo-test--checkers judgments))))) + (provide 'test-lint-org) ;;; test-lint-org.el ends here |
