Classifying: file order outranks kind order, so a rejection in one language is read by another's interview rule #18
Labels
No labels
bug
documentation
enhancement
performance
security
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Postulo/postulo-imap#18
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found on 2026-09-29 in a read-only review of postulo-imap at
cc09c6381, runningclassifyon sentences that say two things.classifyloops over the rule files in alphabetical order and, inside each file, over the kinds. A weaker kind in an earlier file therefore beats a stronger kind in the message's own language. The module and the README both promise the opposite ("most consequential first, and the first that matches wins"; "The most consequential reading wins"), and several patterns are valid in more than one language, so a rejection or an offer that mentions an earlier interview or assessment is filed as that interview or assessment.Where:
src/postulo_imap/classifying.py:119-125,classify:for data in _loaded():outside,for kind, patterns in data["compiled"]:inside;_loadedsorts the files by name (src/postulo_imap/classifying.py:71): de, en, es, fr, it, nl, pl, pt.src/postulo_imap/classifying.py:8-10andREADME.md:58-59: the promise.src/postulo_imap/classifying.py:21-24andREADME.md:82-84do say the files are tried in turn, but only as a reason the wrong language may be named, not that the kind may be wrong.src/postulo_imap/rules/es.json:36andsrc/postulo_imap/rules/pt.json:36: the same pattern,entrevista (telef[óo]nica|presencial|por v[íi]deo); es is consulted first.src/postulo_imap/rules/de.json:46andsrc/postulo_imap/rules/de.json:52:online-?(test|assessment)andassessment[-\s]?center, both plain English as well; de is consulted before en.src/postulo_imap/rules/nl.json:47repeats the second.src/postulo_imap/classifying.py:112-117andsrc/postulo_imap/classifying.py:88-90: theonlyparameter that would narrow the files, andlanguages()"for the connection form". No caller passesonlyand the form has no such field (src/postulo_imap/sync.py:42-117).tests/test_imap.py:324-328,test_the_most_consequential_reading_wins: two kinds, both English.What happens: run on the snapshot:
interview(es), matchedentrevista presencial. The Portuguese rejection rule is never reached.interview(es).interview(es): an offer read as an invitation.assessment(de).assessment(de).The same sentences with the weaker term in the message's own file are read correctly ("After your interview we regret to inform you" →
rejected), which is what shows the order of the files is the cause.Why it matters: no mixed-language mailbox is needed: every Portuguese message is read by the Spanish rules first and every English one by the German rules first. The suggestion says would move it to Interviewing or Assessment for a rejection or an offer, names the wrong language under Recognised, and for an interview collects dates with the wrong language's patterns (
dates_in(text, data["language"]),src/postulo_imap/classifying.py:126), so Portuguese month names are not found. The person accepts a wrong status, or declines and loses the entry.Fix: loop over the kinds first and the files second: for each kind in the fixed order, try every language, and report the language of the pattern that matched. Offer and rejected in any language are then tried before interview in any language. The fixed order already exists as
MOST_CONSEQUENTIAL_FIRSTintests/test_imap.py:168and is asserted for every file; move it intoclassifying.pyand index each file's patterns by kind. Optionally add a languages field to the connection form and pass it asonly; otherwise removeonlyand correct thelanguages()docstring.Done when
tests/test_imap.pyhas a cross-language test: each language's rejection sentence and each language's offer sentence, combined with another language's interview and assessment terms, staysrejectedoroffer. With the sentences above it fails today.classifying.pyandREADME.mddescribe the order the code uses.Fixed by pull request #25.