fix(gateway): suppress lifecycle status emails on email platform - #12610
Open
ibrahimokdadov wants to merge 3 commits into
Open
fix(gateway): suppress lifecycle status emails on email platform#12610ibrahimokdadov wants to merge 3 commits into
ibrahimokdadov wants to merge 3 commits into
Conversation
On startup, the email adapter marks ALL inbox UIDs as seen, causing unread emails received while the machine was off to be silently ignored. Change the IMAP search from ALL to SEEN so only already-read messages are skipped, allowing unread emails from allowed users to be processed on the next poll cycle.
The email adapter only tracked processed UIDs in an in-memory set but never marked them as SEEN on the IMAP server. This caused emails to reappear as UNSEEN on subsequent poll cycles, resulting in duplicate replies being sent for a single email.
The status_callback sends every lifecycle event (rate limit retries, fallback attempts, error messages) through adapter.send(). On chat platforms these are quick inline messages, but on email each one becomes a separate email, flooding the user's inbox with internal retry noise. Skip lifecycle events for the email adapter and log them instead.
This was referenced May 16, 2026
teknium1
reviewed
Jul 12, 2026
teknium1
left a comment
Contributor
There was a problem hiding this comment.
Thanks for identifying the inbox-noise problem. The exact Email lifecycle rule is still not present on current main, although current main now selectively suppresses retry/fallback noise through gateway/run.py:440 and gateway/run.py:17812.
Problems
gateway/platforms/email.py:363marks a message\\Seenbefore the fetch at line 365. A failed fetch is then skipped after restart because this PR initializes its seen set from server-sideSEENmessages. Mark it seen only after successful retrieval and handoff.- Please add a regression test for the Email lifecycle behavior. Current main's shared filter suppresses only selected noisy strings;
run_agent.py:875still emits all statuses aslifecycle. - The PR includes two unrelated IMAP behavior changes alongside the lifecycle fix. They should be split for independent review.
Suggested changes
- Port the lifecycle policy to current main's centralized callback/filter path (
gateway/run.py:17812) and test it withPlatform.EMAIL. - Move the server-side
\\Seenupdate after successful processing.
Automated hermes-sweeper review.
|
|
||
| # Mark as read on the server so it won't reappear | ||
| # as UNSEEN on the next poll or after a restart. | ||
| imap.uid("store", uid, "+FLAGS", "\\Seen") |
Contributor
There was a problem hiding this comment.
This marks the UID \\Seen before the fetch at line 365. If that fetch fails, the message is skipped after restart because connect() now seeds _seen_uids from all server-side SEEN messages. Mark it seen only after successful fetch and handoff.
10 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
_status_callback_syncsends every lifecycle event (rate limit retries, fallback attempts, error messages) throughadapter.send()⏱️ Rate limit reached. Waiting 2.73s before retry (attempt 2/3)...⚠️ Max retries (3) exhausted — trying fallback...❌ Rate limited after 3 retries — HTTP 429: ...lifecycleevents for the email adapter and log them insteadChange
gateway/run.py—_status_callback_sync():Test plan