aboutsummaryrefslogtreecommitdiff
path: root/.ai/scripts/tests
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-06-02 21:31:37 -0500
committerCraig Jennings <c@cjennings.net>2026-06-02 21:31:37 -0500
commitac693a6b7fa7abe88f7778f8e793d5ddfd32f24e (patch)
treeac0e8babf829ffd3ab64bb3b2bb5e9498d1f2f13 /.ai/scripts/tests
parent291103803495cd1937244dc7c993faaaf00023ab (diff)
downloadrulesets-ac693a6b7fa7abe88f7778f8e793d5ddfd32f24e.tar.gz
rulesets-ac693a6b7fa7abe88f7778f8e793d5ddfd32f24e.zip
feat(lint-org): reconcile follow-ups on write instead of appending
Every run appended a fresh dated "lint-org follow-ups" section with line-number-keyed entries, so the follow-ups file grew an unbounded pile of near-duplicate sections, kept entries whose finding had since resolved, and broke whenever the target file's line numbers shifted. Running an audit against a large todo.org surfaced exactly that drift: dead-link flags pointing at docs that now exist, and three stacked dated runs for one file. Now lint-org rewrites the current file's section from the current run. Findings that no longer reproduce simply are not re-emitted, re-runs dedupe to one section, and entries key on checker plus message with the line as a trailing annotation, so a finding survives line shifts as the same entry. Other files' sections are left intact, and the strip step tolerates the old dated-header shape so existing follow-ups files migrate on first run. This changes the follow-ups file from an append-only log to the current outstanding findings per file. task-audit's Phase C link-hygiene step now also reaps a matching dead-link entry when it fixes or verifies the link, scoped strictly to dead-link entries, so the audit and the follow-ups file stop drifting between lint runs. Five follow-ups tests cover record-by-content, dedupe across runs, drop-on-resolve, and preserve-other-files. Mirrors synced.
Diffstat (limited to '.ai/scripts/tests')
-rw-r--r--.ai/scripts/tests/test-lint-org.el105
1 files changed, 93 insertions, 12 deletions
diff --git a/.ai/scripts/tests/test-lint-org.el b/.ai/scripts/tests/test-lint-org.el
index 416f4f6..60deb8c 100644
--- a/.ai/scripts/tests/test-lint-org.el
+++ b/.ai/scripts/tests/test-lint-org.el
@@ -428,7 +428,9 @@ suspicious-language judgment."
;;; ---------------------------------------------------------------------------
;;; Follow-ups file behavior
-(ert-deftest lo-followups-file-appends-judgments ()
+(ert-deftest lo-followups-records-judgments-by-content ()
+ "Each judgment is recorded under a per-file section, keyed by checker +
+message with the line as a trailing annotation rather than the entry's identity."
(let ((followups (make-temp-file "lo-followups-" nil ".org"))
(file (make-temp-file "lo-test-fup-" nil ".org")))
(unwind-protect
@@ -442,17 +444,96 @@ suspicious-language judgment."
(let ((content (with-temp-buffer
(insert-file-contents followups)
(buffer-string))))
- ;; Dated section header.
- (should (string-match-p
- (format "^\\* %s lint-org follow-ups"
- (format-time-string "%Y-%m-%d"))
- content))
- ;; Each judgment is a TODO line referencing checker + line number.
- (should (string-match-p "TODO line [0-9]+ — link-to-local-file" content))
- (should (string-match-p "TODO line [0-9]+ — invalid-fuzzy-link" content))
- (should (string-match-p
- "TODO line [0-9]+ — suspicious-language-in-src-block"
- content))))
+ ;; Per-file section header naming the file.
+ (should (string-match-p "^\\* lint-org follow-ups — " content))
+ ;; Entries lead with the checker (the content key); line is annotation.
+ (should (lo-test--has content "TODO link-to-local-file — "))
+ (should (lo-test--has content "TODO invalid-fuzzy-link — "))
+ (should (lo-test--has content "TODO suspicious-language-in-src-block — "))
+ (should (string-match-p "(line [0-9]+)" content))))
+ (lo-test--drop-buffer file)
+ (when (file-exists-p file) (delete-file file))
+ (when (file-exists-p followups) (delete-file followups)))))
+
+(ert-deftest lo-followups-dedupes-across-runs ()
+ "Running twice on the same file reconciles to one section, not two appended
+sections."
+ (let ((followups (make-temp-file "lo-followups-" nil ".org"))
+ (file (make-temp-file "lo-test-fup-ded-" nil ".org")))
+ (unwind-protect
+ (progn
+ (with-temp-file file (insert lo-test--mixed))
+ (with-temp-file followups (insert ""))
+ (dotimes (_ 2)
+ (lo-test--reset nil followups)
+ (lo-process-file file)
+ (lo-emit-report)
+ (lo-test--drop-buffer file))
+ (let ((content (with-temp-buffer
+ (insert-file-contents followups)
+ (buffer-string)))
+ (n 0) (start 0))
+ (while (string-match "^\\* lint-org follow-ups — " content start)
+ (setq n (1+ n) start (match-end 0)))
+ (should (= n 1))))
+ (lo-test--drop-buffer file)
+ (when (file-exists-p file) (delete-file file))
+ (when (file-exists-p followups) (delete-file followups)))))
+
+(ert-deftest lo-followups-drops-resolved-finding ()
+ "A finding that no longer reproduces (its source was fixed) is dropped from the
+followups file on the next run."
+ (let ((followups (make-temp-file "lo-followups-" nil ".org"))
+ (file (make-temp-file "lo-test-fup-drop-" nil ".org")))
+ (unwind-protect
+ (progn
+ ;; First run: a broken file link produces a judgment.
+ (with-temp-file file
+ (insert "#+TITLE: t\n* H\n[[file:does-not-exist-zzz.org][broken]]\n"))
+ (with-temp-file followups (insert ""))
+ (lo-test--reset nil followups)
+ (lo-process-file file)
+ (lo-emit-report)
+ (lo-test--drop-buffer file)
+ (should (lo-test--has
+ (with-temp-buffer (insert-file-contents followups) (buffer-string))
+ "link-to-local-file"))
+ ;; Fix the source, re-run: the finding no longer reproduces.
+ (with-temp-file file (insert "#+TITLE: t\n* H\nclean now\n"))
+ (lo-test--reset nil followups)
+ (lo-process-file file)
+ (lo-emit-report)
+ (lo-test--drop-buffer file)
+ (let ((content (with-temp-buffer
+ (insert-file-contents followups)
+ (buffer-string))))
+ (should-not (lo-test--has content "link-to-local-file"))))
+ (lo-test--drop-buffer file)
+ (when (file-exists-p file) (delete-file file))
+ (when (file-exists-p followups) (delete-file followups)))))
+
+(ert-deftest lo-followups-preserves-other-files-sections ()
+ "Reconciling one file's section leaves other files' sections intact."
+ (let ((followups (make-temp-file "lo-followups-" nil ".org"))
+ (file (make-temp-file "lo-test-fup-other-" nil ".org")))
+ (unwind-protect
+ (progn
+ (with-temp-file followups
+ (insert "* lint-org follow-ups — other-file.org (2026-01-01)\n"
+ "** TODO some-checker — a prior finding (line 5)\n"))
+ (with-temp-file file (insert lo-test--mixed))
+ (lo-test--reset nil followups)
+ (lo-process-file file)
+ (lo-emit-report)
+ (lo-test--drop-buffer file)
+ (let ((content (with-temp-buffer
+ (insert-file-contents followups)
+ (buffer-string))))
+ ;; The unrelated file's section survives.
+ (should (lo-test--has content "other-file.org"))
+ (should (lo-test--has content "a prior finding"))
+ ;; This file's section was added alongside it.
+ (should (lo-test--has content "link-to-local-file"))))
(lo-test--drop-buffer file)
(when (file-exists-p file) (delete-file file))
(when (file-exists-p followups) (delete-file followups)))))