Message Filter Extension: undocumented per-sender ILMessageFilterAction categorization state on iOS 26.5 — by design or bug?

Environment: iOS 26.5, Message Filter app extension (IdentityLookup framework), offline filtering.

Setup

My Message Filter Extension performs offline string matching on the message body and returns one of: ILMessageFilterAction.allow / .junk / .transaction / .promotion

In the Messages app the filtering UI shows these folders: Messages (the main/default folder), Transactions, Promotions, Junk. .allow is expected to surface a message in the main Messages folder.

The documented behavior (API docs + WWDC22 "Explore SMS message filters") only describes a static mapping from action → folder. On iOS 26.5 I'm seeing what looks like a stateful, per-sender behavior that I cannot find documented anywhere, and I can't tell whether it is intended or a bug.

Test methodology

  • All messages in every sequence are sent from the same single phone number.
  • Before each Case, I fully clear the receive history for that number, so every sequence starts from a clean slate with no prior state for that sender.

The notation shows how many conversations/entries appear in each folder after each step.

Case A — first message = allow

  1. allow → Messages: 1
  2. promotion → Messages: 2 (allow + promotion), Promotions: 1
  3. junk → everything collapses into Junk; Messages & Promotions become empty
  4. every subsequent message lands in Junk regardless of the action I return

Case B — first message = allow, then transaction

  1. allow → Messages: 1
  2. transaction → Messages: 2 (allow + transaction), Transactions: 1
  3. junk → everything collapses into Junk; Messages & Transactions empty
  4. every subsequent message lands in Junk regardless of returned action

Case C — first message = transaction

  1. transaction → Messages: 1, Transactions: 1 ← also appears in Messages
  2. allow → Messages: 2, Transactions: 1
  3. promotion → Messages: 3, Transactions: 1, Promotions: 1
  4. junk → Messages: 4, Transactions: 1, Promotions: 1, Junk: 1 (NOTE: here junk does NOT collapse the thread, and there is no "sticky junk")

Case D — first message = promotion

  1. promotion → Promotions: 1 only (does NOT appear in Messages)
  2. allow → Messages: 2 (the earlier promotion now also appears in Messages), Promotions: 1
  3. junk → everything collapses into Junk (sticky, same as Case A/B)
  4. every subsequent message lands in Junk regardless of returned action

My core question: are the following two behaviors by design, or are they bugs?

(1) "Sticky junk" after allow-first / promotion-first. In Cases A, B and D, once .junk is returned the whole sender thread collapses into Junk, and from then on every message is forced into Junk regardless of the action my extension returns. Is this expected/by-design, or a bug? If by design: is it permanent, and what resets it — does the extension have any control, or is it purely user-driven (e.g. the user moving the thread out of Junk)? What concerns me is that the system appears to ignore my returned action entirely once this state is entered.

(2) .transaction-first behaving differently from .allow-first. A sender whose first message is .transaction (Case C) behaves differently: the message also appears in the main Messages folder, and a later .junk does not collapse the thread (no sticky junk). Is this .transaction-first behavior expected/by-design, or a bug? If by design, what is the underlying rule that makes .transaction-first confer this state while .allow-first does not? Since the history is cleared before each test, this is determined purely by the first action returned.

Additional clarifying questions

  1. Is any of this per-sender state behavior documented beyond the static action → folder mapping in the API docs / WWDC22 "Explore SMS message filters"? If so, where?

  2. More generally, what determines whether a categorized (.transaction / .promotion) message is also mirrored into the main Messages folder?

Thanks — I'd like my extension's return values to produce predictable categorization for users, and right now this first-message-dependent behavior makes that hard to reason about.

Message Filter Extension: undocumented per-sender ILMessageFilterAction categorization state on iOS 26.5 — by design or bug?
 
 
Q