Short identifier, e.g. ‘junk_from_noreply’ or ‘job_applications’.
When the rule is evaluated. On EMAIL, pre_run and post_tag are the SAME live stage (evaluate_handoff runs every live rule in one pass, list order, after tagging — the label no longer partitions it; the router skips tagging when a terminal live rule already matches on pre-tag facts). On chat, pre_run runs at conversation entry (chat has no mid-run tagger). post_close runs in the post-close analysis pipeline after a chat ticket is closed/escalated — fresh tags available.
Conditions (AND’d): sources super_tag / sender / medium / location / language / llm (and arg for payload-carrying triggers). llm asks a free-text yes/no question about the guest’s message and matches when the answer is yes — unavailable on post_close, which has no message in scope. Empty list = the rule always matches its trigger.
How often this rule’s shaping steps (set_status / send_reply / assign / add_tag) may act on ONE conversation. ‘once_per_thread’ (default) runs them the first time the rule matches and never again — the guest is not re-emailed and the ticket is not re-assigned on every follow-up, even though the tag that matched stays on the thread forever. ‘every_message’ restores the unguarded behaviour. Routing/disposition steps (human_handoff / skip_agent / end_run_silent / mark_junk / close_ticket / limit_tools) always apply — they are the gate, not an effect.
Ordered steps to run on match — the rule’s mini-flow. Ticket-side steps (set_status / send_reply / assign / add_tag / forward_email / close_ticket / set_classification) execute in order; a routing step (mark_junk / human_handoff / skip_agent / end_run_silent) ends the run after the remaining ticket-side steps execute; limit_tools blocks tools for the run. Empty = the rule records its match and does nothing.
Human-readable why — surfaced in events, handoff reasons, and blocked-call messages.