Skip to content

Commit 4a77428

Browse files
committed
refactor: Label syntax: case-sensitive with post-bracket space
1 parent 329ba6d commit 4a77428

5 files changed

Lines changed: 92 additions & 12 deletions

File tree

docs/bark-manual.org

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,9 @@ Reports are detected from email subject labels:
476476
Long-form aliases are also accepted: =[ANNOUNCEMENT]=, =[RELEASE]=,
477477
=[CHANGE]=.
478478

479+
Labels are case-sensitive and require whitespace (or end of subject)
480+
after the closing =]=. Malformed labels are ignored.
481+
479482
Announcements, releases and changes require maintainer permission.
480483

481484
All =<…>= parts are optional and positional. For release and change, a

resources/emails.edn

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -797,17 +797,17 @@
797797
:email/body-text "Notify: d:1\n"}
798798

799799
;; =========================================================================
800-
;; 59 — Case insensitive tag: [bug] lowercase
800+
;; 59 — Properly tagged [BUG] (anchors a thread for 61/62/64)
801801
;; =========================================================================
802802
{
803803
:email/id "59"
804804
:email/source "direct"
805805
:email/message-id "<59@test.org>"
806-
:email/subject "[bug] lowercase tag test"
806+
:email/subject "[BUG] strict tag test"
807807
:email/from-address "user@test.org"
808808
:email/date-sent #inst "2026-01-21T10:00:00"
809809
:email/ingested-at #inst "2026-01-21T10:00:00"
810-
:email/body-text "Lowercase tag.\n"}
810+
:email/body-text "Strict tag.\n"}
811811

812812
;; =========================================================================
813813
;; 60 — [ANNOUNCEMENT] long form tag
@@ -829,7 +829,7 @@
829829
:email/id "61"
830830
:email/source "direct"
831831
:email/message-id "<61@test.org>"
832-
:email/subject "Re: [bug] lowercase tag test"
832+
:email/subject "Re: [BUG] strict tag test"
833833
:email/from-address "maint@test.org"
834834
:email/date-sent #inst "2026-01-21T12:00:00"
835835
:email/ingested-at #inst "2026-01-21T12:00:00"
@@ -843,7 +843,7 @@
843843
:email/id "62"
844844
:email/source "direct"
845845
:email/message-id "<62@test.org>"
846-
:email/subject "Re: Re: [bug] lowercase tag test"
846+
:email/subject "Re: Re: [BUG] strict tag test"
847847
:email/from-address "maint@test.org"
848848
:email/date-sent #inst "2026-01-21T13:00:00"
849849
:email/ingested-at #inst "2026-01-21T13:00:00"
@@ -866,15 +866,15 @@
866866

867867
;; =========================================================================
868868
;; 64 — Duplicate report attempt (same subject tag, same thread)
869-
;; Tests that a second [bug] email in the same thread doesn't create
869+
;; Tests that a second [BUG] email in the same thread doesn't create
870870
;; a second report — it becomes a descendant instead.
871871
;; Uses its own message-id (unlike email 59) to avoid DB-level upsert.
872872
;; =========================================================================
873873
{
874874
:email/id "64"
875875
:email/source "direct"
876876
:email/message-id "<64@test.org>"
877-
:email/subject "[bug] lowercase tag test"
877+
:email/subject "[BUG] strict tag test"
878878
:email/from-address "user@test.org"
879879
:email/date-sent #inst "2026-01-21T15:00:00"
880880
:email/ingested-at #inst "2026-01-21T15:00:00"
@@ -1838,4 +1838,30 @@
18381838
:email/in-reply-to "<136@test.org>"
18391839
:email/references "<136@test.org>"
18401840
:email/body-text "Actually the other one was the better description.\nSuperseded-by: <135@test.org>\n"}
1841+
1842+
;; =========================================================================
1843+
;; 201 — Malformed label (lowercase): label must be case-sensitive
1844+
;; Expected: no report created (regex rejects [bug])
1845+
;; =========================================================================
1846+
{:email/id "201"
1847+
:email/source "direct"
1848+
:email/message-id "<201@test.org>"
1849+
:email/subject "[bug] should not match"
1850+
:email/from-address "user-a@test.org"
1851+
:email/date-sent #inst "2026-03-09T09:00:00"
1852+
:email/ingested-at #inst "2026-03-09T09:00:00"
1853+
:email/body-text "Lowercase tag, must not classify.\n"}
1854+
1855+
;; =========================================================================
1856+
;; 202 — Malformed label (no space after `]`): must be rejected
1857+
;; Expected: no report created (regex requires \s|$ after closing bracket)
1858+
;; =========================================================================
1859+
{:email/id "202"
1860+
:email/source "direct"
1861+
:email/message-id "<202@test.org>"
1862+
:email/subject "[BUG]glued subject"
1863+
:email/from-address "user-a@test.org"
1864+
:email/date-sent #inst "2026-03-09T09:01:00"
1865+
:email/ingested-at #inst "2026-03-09T09:01:00"
1866+
:email/body-text "Glued label, must not classify.\n"}
18411867
]

src/bark/detect.clj

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,18 @@
1313

1414
(def ^:private ml-prefix "(?:\\[[^\\]]*\\]\\s*)*")
1515

16-
(defn compile-labels [st]
16+
(defn compile-labels
17+
"Compile a type=>tags map into type=>regex. The label must take a
18+
strict bracket form anchored at the start of the subject: `[TAG]` or
19+
`[TAG <inner>]`, with the tag matched case-sensitively against the
20+
configured tokens and followed by whitespace or end-of-subject after
21+
the closing `]`. Optional mailing-list bracket prefixes (e.g.
22+
`[my-list] [BUG] ...`) are tolerated."
23+
[st]
1724
(update-vals st
1825
(fn [tags]
1926
(let [alts (str/join "|" (map #(java.util.regex.Pattern/quote %) tags))]
20-
(re-pattern (str "(?i)^" ml-prefix "\\[(" alts ")(?:\\s+([^\\]]*))?\\]"))))))
27+
(re-pattern (str "^" ml-prefix "\\[(" alts ")(?:\\s+([^\\]]*))?\\](?=\\s|$)"))))))
2128

2229
(def default-compiled-labels (compile-labels common/default-labels))
2330

test/bark/detect_test.clj

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,41 @@
6363
(is (not (detect/has-ics?
6464
{:email/body-text "Hello"
6565
:email/attachments [{:attachment/filename "doc.pdf"}]})))))
66+
67+
;; ---------------------------------------------------------------------------
68+
;; Strict label regex: case-sensitive tag, mandatory `\s` or EOL after `]`
69+
;; ---------------------------------------------------------------------------
70+
71+
(defn- detect-type [subject]
72+
(:type (detect/detect-report {:email/subject subject})))
73+
74+
(deftest label-regex-strict-test
75+
(testing "accepted forms"
76+
(is (= :bug (detect-type "[BUG] Mon bug")))
77+
(is (= :bug (detect-type "[BUG foo] Mon bug")))
78+
(is (= :bug (detect-type "[BUG]")))
79+
(is (= :bug (detect-type "[mylist] [BUG] Mon bug")))
80+
(is (= :patch (detect-type "[PATCH foo v2 1/2] Body")))
81+
(is (= :request (detect-type "[POLL] question?")))
82+
(is (= :request (detect-type "[TODO] task")))
83+
(is (= :announcement (detect-type "[ANN] news")))
84+
(is (= :announcement (detect-type "[ANNOUNCEMENT] news")))
85+
(is (= :release (detect-type "[REL 2.0] notes")))
86+
(is (= :change (detect-type "[CHG 9.8] heads-up"))))
87+
88+
(testing "rejected: wrong case"
89+
(is (nil? (detect-type "[Bug] Mon bug")))
90+
(is (nil? (detect-type "[bug] Mon bug")))
91+
(is (nil? (detect-type "[Patch] body")))
92+
(is (nil? (detect-type "[poll] question?"))))
93+
94+
(testing "rejected: no whitespace after closing bracket"
95+
(is (nil? (detect-type "[BUG]Mon bug")))
96+
(is (nil? (detect-type "[BUG foo]bar")))
97+
(is (nil? (detect-type "[POLL]?"))))
98+
99+
(testing "rejected: malformed bracket content"
100+
(is (nil? (detect-type "[BUG/RFC] Mon bug")))
101+
(is (nil? (detect-type "[ BUG ] Mon bug")))
102+
(is (nil? (detect-type "BUG: Mon bug")))
103+
(is (nil? (detect-type "Re: [BUG] Mon bug")))))

test/bark/digest_test.clj

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -564,8 +564,8 @@
564564
[:notify/key "direct:maint@test.org"])]
565565
(is (= 1 (:notify/interval-days pref)))))
566566

567-
;; --- Email 59 case insensitive [bug] ---
568-
(testing "Email 59 case insensitive [bug]"
567+
;; --- Email 59 properly tagged [BUG] ---
568+
(testing "Email 59 properly tagged [BUG]"
569569
(is (= :bug (:report/type (get-report db "<59@test.org>")))))
570570

571571
;; --- Email 60 [ANNOUNCEMENT] long form ---
@@ -593,9 +593,15 @@
593593
(is (not (report-exists? db "<63@test.org>"))))
594594

595595
;; --- Email 64 duplicate tag ---
596-
(testing "Email 64 [bug] reply creates own report"
596+
(testing "Email 64 [BUG] reply creates own report"
597597
(is (report-exists? db "<64@test.org>")))
598598

599+
;; --- Emails 201-202 malformed labels create no report ---
600+
(testing "Email 201 lowercase [bug] is rejected"
601+
(is (not (report-exists? db "<201@test.org>"))))
602+
(testing "Email 202 glued [BUG]subject is rejected"
603+
(is (not (report-exists? db "<202@test.org>"))))
604+
599605
;; --- Emails 65-66 HTML body fallback ---
600606
(testing "Emails 65-66 HTML body fallback"
601607
(let [r (get-report db "<65@test.org>")]

0 commit comments

Comments
 (0)