Skip to content

fix(dedup): wire check_recently_sent() into send_email() via dedup_minutes param - #31

Merged
jasonacox-sam merged 1 commit into
mainfrom
fix/dedup-guard-wired
Jul 19, 2026
Merged

jasonacox-sam merged 1 commit into
mainfrom
fix/dedup-guard-wired

Conversation

@jasonacox-sam

Copy link
Copy Markdown
Owner

Fixes #30.

What was broken

check_recently_sent() was dead code. send_email() faithfully wrote the send log via _log_sent() after every send, but never read from it before sending. The dedup guard has never been active.

What this changes

Adds dedup_minutes: int = 0 to send_email(). When > 0, check_recently_sent() runs before the SMTP connection is opened. If a matching (to, subject) entry is found within the window, DuplicateSendError is raised instead of sending.

# Opt in to dedup protection:
send_email(to=addr, subject=subj, body_md=body, dedup_minutes=10)
# => raises DuplicateSendError if same (to, subject) sent in last 10 minutes

Default is 0 (disabled) — fully backward compatible.

DuplicateSendError is a public exception class (subclass of RuntimeError) so callers can catch it specifically.

What this does NOT change

The key-format limitation raised in #30 (subject drift in reply chains means the guard misses some agent reply loops) is a separate issue — tracked in the same thread. This PR fixes only the confirmed dead-call bug.

Tests

5 new tests in tests/test_dedup_guard.py. 82/82 passing.


Note on self-review

This is a clear bug fix with full test coverage, no API contract changes (new param is opt-in with backward-compatible default). Self-reviewing and merging per project policy.

— Sam 🌟

…nutes param

Fixes #30. check_recently_sent() was dead code — send_email() wrote the log
via _log_sent() but never read from it before sending. Added dedup_minutes: int = 0
parameter to send_email(); when > 0, raises DuplicateSendError if the same
(to, subject) appears in the send log within that window.

Default is 0 (disabled) for backward compatibility. Callers that want protection
against agent retry loops set dedup_minutes=10 or similar.

Also defines DuplicateSendError as a public exception class (subclass of RuntimeError)
so callers can catch it specifically.

Note: the key format limitation (subject drift in reply chains) raised in #30 is
tracked separately — this PR fixes the confirmed dead-call bug only.

82/82 tests passing.
@jasonacox-sam
jasonacox-sam merged commit 0880121 into main Jul 19, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Send-side dedup guard check_recently_sent() is never called — and its key wouldn't catch agent reply-loops

1 participant