aboutsummaryrefslogtreecommitdiff
path: root/.ai/scripts/lint-org.el
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-06-11 14:25:55 -0500
committerCraig Jennings <c@cjennings.net>2026-06-11 14:25:55 -0500
commit8d790f371e54a8cc3e79a5ce72cd4dd5b3fa4513 (patch)
treeff6c1a496c4e7727bd823979a582dc21ef25b811 /.ai/scripts/lint-org.el
parentbdc9a5d6e1320032770f54c747c210e4f465c399 (diff)
downloadrulesets-8d790f371e54a8cc3e79a5ce72cd4dd5b3fa4513.tar.gz
rulesets-8d790f371e54a8cc3e79a5ce72cd4dd5b3fa4513.zip
feat(org): table standard as a rule, reflow helper, and lint check
Wide org tables overflow the page in exported PDF/docx, and hand-wrapping a cell into continuation rows is tedious and error-prone. The standard existed only as a work-project convention with nothing enforcing it. claude-rules/org-tables.md carries the generalized standard: 120-column budget measured at render width (a link counts as its visible label and is never split), over-budget cells wrap onto continuation rows, and a rule sits under the header and every logical row. wrap-org-table.el reflows a table to that shape mechanically. Columns shrink from natural width toward a floor of their widest atomic token, cells wrap link-safe, and rule-delimited continuation groups merge back into their logical row before re-wrapping, which makes the reflow idempotent. A table whose floors still exceed the budget reflows best-effort and stays flagged for restructuring. lint-org.el gains an org-table-standard judgment check: width overruns and missing rules surface during the sweep with a pointer to the helper. Conformant wrapped tables don't false-flag, since the check reuses the helper's continuation-group reading. The check is judgment-only by design: reflowing is a visible layout change the sweep shouldn't make silently.
Diffstat (limited to '.ai/scripts/lint-org.el')
-rw-r--r--.ai/scripts/lint-org.el60
1 files changed, 60 insertions, 0 deletions
diff --git a/.ai/scripts/lint-org.el b/.ai/scripts/lint-org.el
index 85886af..5d47644 100644
--- a/.ai/scripts/lint-org.el
+++ b/.ai/scripts/lint-org.el
@@ -42,6 +42,7 @@
(require 'org-lint)
(require 'cl-lib)
(require 'subr-x)
+(require 'wrap-org-table) ; render-width + table parsing for the table check
(defvar lo-fixes 0
"Count of mechanical fixes applied (or would-apply in --check) on the last file.")
@@ -285,6 +286,62 @@ Craig-specific annotation marker rather than Babel src-block syntax."
(lo--emit-judgment name line msg)))))
;;; ---------------------------------------------------------------------------
+;;; org-table-standard check (claude-rules/org-tables.md)
+;;
+;; Not an org-lint checker — a custom scan run alongside the org-lint pass.
+;; Violations surface as judgment items (checker `org-table-standard'), never
+;; auto-fixed: reflowing a table is a visible layout change that
+;; wrap-org-table.el performs on request, not something a lint sweep does
+;; silently.
+
+(defun lo--table-violations (lines)
+ "Standard violations for the table given as LINES, as message strings.
+Width is render-measured (links count as their labels, per wot-render-width).
+Rules: an hline must follow the header and every logical data row, closing
+rule included; continuation lines inside a rule-delimited group are one
+logical row, matching wrap-org-table.el's grouping."
+ (let ((violations nil)
+ (max-width (apply #'max (mapcar #'wot-render-width lines))))
+ (when (> max-width wot-default-budget)
+ (push (format "renders %d wide (budget %d)" max-width wot-default-budget)
+ violations))
+ (let* ((parsed (mapcar #'wot--parse-row lines))
+ (header-p (and (listp (car parsed)) (eq (cadr parsed) 'hline)))
+ (data (if header-p (cddr parsed) parsed)))
+ (when (and (cl-some #'listp data)
+ (not (eq (car (last data)) 'hline)))
+ (push "no closing rule" violations))
+ (let ((group nil))
+ (cl-loop for e in data
+ if (eq e 'hline) do (setq group nil)
+ else do (push e group)
+ when (and (> (length group) 1)
+ (not (wot--continuation-group-p (reverse group))))
+ return (push "missing rule between rows" violations))))
+ (nreverse violations)))
+
+(defun lo--check-tables ()
+ "Scan the current buffer for org tables violating the table standard.
+Emits one judgment item per violating table."
+ (save-excursion
+ (goto-char (point-min))
+ (while (re-search-forward "^[ \t]*|" nil t)
+ (let ((start-line (line-number-at-pos))
+ (lines nil))
+ (beginning-of-line)
+ (while (and (not (eobp)) (looking-at "[ \t]*|"))
+ (push (buffer-substring-no-properties (line-beginning-position)
+ (line-end-position))
+ lines)
+ (forward-line 1))
+ (let ((violations (lo--table-violations (nreverse lines))))
+ (when violations
+ (lo--emit-judgment
+ 'org-table-standard start-line
+ (format "table violates the org-table standard: %s — wrap-org-table.el reflows it"
+ (string-join violations "; ")))))))))
+
+;;; ---------------------------------------------------------------------------
;;; File processing
(defun lo--backup (file)
@@ -314,6 +371,9 @@ left unmodified and mechanical entries are recorded with :preview t."
(lambda (a b) (> (lo--line a) (lo--line b))))))
(dolist (item sorted)
(lo--handle-item item)))
+ ;; 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)
(when (and (not lo-check-only) (buffer-modified-p))
(save-buffer)))
(with-current-buffer buf (set-buffer-modified-p nil))