Skip to main content

Passive folder and label sorting

Passive sorting evaluates saved folder and label rules in background work. A run is one saved batch of selected messages. Each message has a work record.

See background-work terms for durable admission, outbox, leases, and fencing. It evaluates folder and label targets in parallel. Provider mutations use the ordered mailbox operation path. Prepare Graph access or Google access for the mailbox.

Configure and start sorting​

Open Sorting in a folder or label editor. Configure positive and negative conditions. Enable the target. Then save the sorting rules. Legacy users_folders regex fields, descriptions, Qdrant similarity, and the old folder-tree fallback do not participate in this rule evaluator.

The sidebar Sorting page separately controls folder sorting, label sorting, and automatic arrival processing. Automatic mode requires an allowed model. Administrators set the tenant model, reasoning, and context defaults. Personal overrides require separate permission. See sorter defaults.

A manual run with only cheap conditions needs no model. A semantic condition always needs one.

Lower folder priority numbers run first. Target ID breaks a tie. This does not change sidebar order. Only the first matching folder wins. All matching labels apply to the whole conversation while preserving existing labels.

For manual work, select messages in a folder, search, all-folder, or label view, then start sorting. A conversation row expands only to messages within that view's folder/date boundary. Explicit search or label message selection does not expand to other conversation members. Empty selection never starts mailbox-wide work.

Rule semantics​

FieldConditions
sender, ccPositive and negative contact lists with saved contact email addresses. Any negative contact match vetoes the target
subject, bodyLiteral text, regular expressions, and natural-language meaning conditions
dateOpen absolute date bounds or relative calendar days

Trim removes whitespace from the pattern's edges. It does not truncate mail. Pattern controls separately set case, accents, whole-word matching, and regex mode. Literal matching treats the pattern as text. Whole-word boundaries include Unicode letters, numbers, and underscore.

Regex uses Node Unicode syntax with a 50 ms execution timeout. Saving reports syntax errors. A timeout fails the work rather than returning “no match.”

Lists default to OR. Empty lists impose no condition. Positive and negative lists can have different lengths. Configured fields default to AND.

OperatorMeaning
Positive list ORAt least one item must match
Positive list ANDEvery item must match
Negative list OROne matching item blocks the target
Negative list ANDAll items must match to block the target
Positive pattern/meaning OREither configured condition type can satisfy the positive side
Negative pattern/meaning ANDBoth configured types must pass their exclusion checks
Negative pattern/meaning ORAt least one configured type must pass its exclusion check

For example, a subject that matches “invoice” still fails if its body matches the excluded term “gas.” This is a global veto: another positive field cannot override the exclusion.

The positive result and successful exclusion check always combine with AND. A computed exclusion is a global veto for that target. For example, subject || body cannot bypass a negative body result. The negative type operator differs from the negative list operator.

Advanced mode accepts sender, cc, subject, date, body, &&, ||, and parentheses. Example: sender && (subject || body). The parser accepts at most 512 characters and does not use eval. Every configured field must appear.

Unconfigured fields are invalid. Disabling Advanced restores default operators.

OR broadens positive selection. If subjects are vague, retain a required body condition. The editor warns about this choice.

Dates use Europe/Budapest calendar days and the run's fixed start-day value. On October 11, “older than 5 days” means before October 6 at 00:00. The newer direction starts at that boundary. Absolute after and before exclude their boundary days. Daylight-saving changes do not use simple 24-hour subtraction.

For example, a server-fee positive match still fails when a streaming exclusion matches. A combined electricity/gas invoice also fails if gas is excluded. These exclusions are intentional, not model overrides.

Evaluation and errors​

Cheap checks run in this order: sender, cc, subject, date, body. Only unresolved subject/body meaning checks call the model. Within a field, the shorter cheap list runs first. AND wins equal-length ordering. Literal patterns run before regex.

Short-circuit evaluation avoids unnecessary inference.

Body read failures, regex errors, invalid model output, and required inference failures never count as successful exclusions. Work fails or blocks. An uncertain higher-priority folder does not silently yield to a lower match. A folder/label evaluation error prevents partial mutation for that message. Other messages' completed operations remain.

Transient model/provider errors allow at most five attempts, with waits of 2, 10, 30, and 60 seconds. Rate limits honor at least the provider's Retry-After. Configuration and permission errors do not receive automatic transport retries.

Durable work and confirmation​

The transaction that stores a new non-draft arrival records only a deduplicated arrival intent. It does not run inference. Delta replay and the application's own moves do not create another arrival. EmailScheduler performs admission and recovery in the existing background service.

Admission means accepting the run and saving signed proof of its allowed action. It checks current mailbox ownership, mail.sorting.manage, mail.crud, and model access when required. Automatic admission needs saved opt-in and a fresh authorization snapshot. The saved signed proof identifies selected messages, rules, model, start time, and mailbox. Switching mailboxes cannot transfer a run.

Per-message sorting_work records hold state, attempts, result, error, retry time, and sync operation IDs. The work lock and operation/outbox creation share a transaction. Recovery reconciles an accepted mutation before considering another submission.

Work stateMeaning
preparedDurable work awaiting execution. Arrival can be queued before this
runningWorker evaluates the message
retry-waitWaiting after a transient provider failure
awaiting-syncEvaluation finished, provider confirmation remains outstanding
completedNo change needed, or all requested remote operations confirmed
no-matchNo target matched
failed / blockedVisible failure, not success or no-match
cancelledWork stopped before execution

Run states include running, paused, cancelled, and finished. Finished means all work reached a terminal state. It does not mean every item succeeded. The interface separately shows errors and pending confirmation.

Pause preserves remaining work. Resume continues the same run. Cancel closes work that did not start and retains tracking for submitted operations. Retry failed starts a new cycle of inference attempts and preserves confirmed remote effects. Uncertain or superseded effects need reconciliation first. One mailbox permits at most one running/paused run, including retries.

A heartbeat reports that the worker remains active. Attempt fencing rejects changes from an expired worker or earlier attempt. Together they prevent an expired worker from submitting another mutation. Recovery uses a 120-second stale threshold. Fan-out accounts for available container memory: 128 MiB reserve and 256 MiB per worker, with at least one worker. Folder/label evaluation remains parallel.

Conversation category fan-out preserves per-message successes. An equal state with a Graph timestamp in the same second can confirm an intent. A differing state at that timestamp cannot supersede a newer intent. Apply can issue a targeted GET for checkpointed or unchanged categories. It does not issue another PATCH for that check.

API and interface refresh​

BFF /api/sorting?accountId=… proxies backend /sync/sorting.

RequestResult
GET overviewSettings, allowed models, rules, target names, latest 50 runs, at most 50 queued/blocked arrivals
GET with runId, optional cursorRun summary and 100 work items per page
POST actionrule, settings, start, pause, resume, cancel, retry

Legacy autonomousSort and single-message sortEmail starts also use durable runs. Empty selection is rejected. Read and manage permissions differ. Read-only mailboxes reject mutations.

The interface reads state on process.progress events and reconnection. The active mail-list sorting control reads durable run state every 3 seconds. Overview refresh uses 5 seconds. Detail uses 3 seconds. Reconnection also reads the API. These paths keep durable state authoritative after a lost completion event.

Automatic admission errors appear on Sorting. After correcting access or settings, save enabled automatic settings again to requeue blocked arrivals.