diff options
| author | Craig Jennings <c@cjennings.net> | 2026-07-25 01:50:23 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-07-25 01:50:23 -0500 |
| commit | 8822b0dab56ca50269771b4c9d06b78657c2b8b4 (patch) | |
| tree | e318861637cdcf3281f123fe4ca01b9957d78c4e /todo.org | |
| parent | f7139d82173ccf9effdcdee50df22912f3f05fa0 (diff) | |
| download | rulesets-8822b0dab56ca50269771b4c9d06b78657c2b8b4.tar.gz rulesets-8822b0dab56ca50269771b4c9d06b78657c2b8b4.zip | |
fix(lint-org): stop flagging correctly paired verbatim blocks
org-lint's invalid-block checker flags both delimiters of a paired example or src block whose body holds a heading-shaped line. Its parser reads the `** ` as a structural break and loses the open block. Every org file that documents org syntax inside a block collects two false findings.
The checker is org-lint's own, not one lint-org implements, so I filtered its output instead of patching a local checker. `lo--matched-block-regions` pairs blocks by scanning lines under org's real rule (once open, only the block's own `#+end_TYPE` closes it). A finding inside a matched region gets dropped, delimiters included, since that's where org-lint reports. I scanned lines rather than asking org because org's parser is what's confused.
An unterminated block never lands in a matched region, so it still reports. Three tests cover the false positives: a heading in an example block, a literal `#+end_example` inside a src block, and uppercase delimiters. Two more guard the real finding, including a file with one paired and one unterminated block that proves the suppression is per-block.
Diffstat (limited to 'todo.org')
| -rw-r--r-- | todo.org | 16 |
1 files changed, 14 insertions, 2 deletions
@@ -39,10 +39,17 @@ Tags are assigned and refreshed by =task-audit=; =task-review= keeps them honest * Rulesets Open Work -** TODO [#C] lint-org invalid-block false-positives inside example/src blocks :bug:solo: +** DONE [#C] lint-org invalid-block false-positives inside example/src blocks :bug:solo: +CLOSED: [2026-07-24 Fri] :PROPERTIES: :LAST_REVIEWED: 2026-07-24 :END: +Fixed 2026-07-24, test-first. =lo--matched-block-regions= scans lines for correctly paired blocks under org's real rule — once a block is open, only its own =#+end_TYPE= closes it — and =lo--handle-item= drops an =invalid-block= finding whose line falls in one, delimiters included (org-lint reports at the delimiters themselves). Line-scanning rather than asking org is the point: org's parser is what mis-reads these blocks. 4 ERT tests: the heading-in-example case, a src block holding a literal =#+end_example=, a genuinely unterminated block that must still report, and a file with one of each proving the suppression is per-block not per-file. + +Verified against home's fixture end to end: =/home/cjennings/projects/home/.ai/notes.org= produced exactly the two reported findings (lines 386, 398) under the pre-change script and zero under the new one, file untouched. Full suite green. + +Left alone deliberately: the =,**= comma-escape in =claude-templates/.ai/notes.org= line 53. The task noted the fix "lets that escape be reverted," but the escape is the documented org convention for a literal =**= inside a verbatim block, so reverting it would trade correct org for no gain now that the finding is suppressed either way. + home reported it, and I verified it: an =#+begin_example= block whose body contains a line beginning =** = (or any heading-shaped line) makes =invalid-block= flag *both* delimiters as "Possible incomplete block", even though the block is correctly paired. The checker reads the heading line inside the verbatim body as a structural break and loses the open block. Reproduced 2026-07-24 on a three-line example block: 2 judgment findings, both false. This is a docs-file-common shape — any org file documenting org syntax inside an example block hits it (home's notes.org PENDING DECISIONS section does). @@ -51,7 +58,12 @@ Reproduced 2026-07-24 on a three-line example block: 2 judgment findings, both f Grading: Minor severity (judgment output, nothing mutates; pure noise that trains the reader to skim) x most users, frequently (every org file documenting org syntax in a verbatim block) = P3 = [#C]. -Fix direction (per home): while inside a =begin_example= / =begin_src= block, skip structural parsing of the body until the matching =#+end_= line — verbatim blocks contain no headings, timestamps, or delimiters by definition. The same class hits a src block containing =#+end_example= as literal text. Open question to settle at fix time: is =invalid-block= lint-org.el's own checker (fix in place) or org-lint's own (filter its output)? =link-to-local-file= turned out to be org-lint's; this one needs the same determination before the approach is chosen. Tagged :solo: because once that's known the fix and its test are mechanical. +Fix direction (per home): while inside a =begin_example= / =begin_src= block, skip structural parsing of the body until the matching =#+end_= line — verbatim blocks contain no headings, timestamps, or delimiters by definition. The same class hits a src block containing =#+end_example= as literal text. Tagged :solo: because the fix and its test are mechanical. + +*** 2026-07-24 Fri @ 20:10:00 -0500 Open question settled — invalid-block is org-lint's, so the fix is a filter +Home answered it and I re-verified both halves here: =grep invalid-block= over =lint-org.el= returns nothing, and =org-lint--checkers= enumerates =invalid-block= in batch Emacs alongside =link-to-local-file=, =invalid-babel-call-block=, and =missing-language-in-src-block=. So this is the same shape as the =link-to-local-file= episode: suppress an =invalid-block= finding whose line falls inside a verbatim block, on our side of org-lint's output. No local checker to edit. + +Regression fixture, ready to use: home's own =.ai/notes.org= PENDING DECISIONS example block (lines 386-398) holds an unescaped =** Feature Name or Topic= and trips both delimiters — findings at lines 386 and 398. Home is deliberately leaving its copy unescaped so it stays a fixture, and asked to be pinged when the filter lands so it can re-run. The filter should take those two findings to zero without touching the file. ** VERIFY [#B] Parked: telegram source treats "down" as launch, not SCAN FAILED (from .emacs.d) :PROPERTIES: |
