Repository navigation
fix(gateway): admit completion wakes whose source differs only by message_id - #138967
sharpnotebot wants to merge 1 commit into
Conversation
|
Independent synthetic validation on 01e86ff: the source-message_id regression passes. Combining this head with only the base.py change from #138986 (82a4d3d), 9 additional tests pass through real temporary SQLite/native admission and restore for Discord and Telegram. They cover no reply anchor, explicit override, idempotent retry, and rejection of chat/user/thread/profile/name and connector/home provenance drift. On this PR alone, the two positive roundtrips admit successfully but fail at the reply-anchor assertion, confirming the complementary fix is needed. No live adapter sends, production changes, or full-suite claim. |
… no-anchor branch The code-health ratchet caps gateway/platforms/base.py at its size on main (4914 lines); the internal-wake guard from #138967's companion fix added five. Both branches return None before any platform-specific anchor, so they share one condition. Behaviour is unchanged: the guard still runs after reply_anchor_override and before the Telegram DM-topic branch.
|
Thanks @sharpnotebot. This commit and your companion #138986 are carried into #139026 with your authorship ( One change on top of #138986: its guard is folded into the existing Slack no-anchor branch (same order, after #139026 is armed. I'll post the merge SHA here once it lands, and reopen this if it doesn't. |
… no-anchor branch The code-health ratchet caps gateway/platforms/base.py at its size on main (4914 lines); the internal-wake guard from #138967's companion fix added five. Both branches return None before any platform-specific anchor, so they share one condition. Behaviour is unchanged: the guard still runs after reply_anchor_override and before the Telegram DM-topic branch.
|
Landed on main via #139026 (merge head |
… no-anchor branch The code-health ratchet caps gateway/platforms/base.py at its size on main (4914 lines); the internal-wake guard from NousResearch#138967's companion fix added five. Both branches return None before any platform-specific anchor, so they share one condition. Behaviour is unchanged: the guard still runs after reply_anchor_override and before the Telegram DM-topic branch.
What does this PR do?
A background-process completion wake is refused with
admission_conflicton every attempt, so it never reaches the session and is requeued unchanged (#138940 on Discord, #134889 on Telegram).snapshot_automationrestores the session's last committed native envelope and compared its source to the synthetic event's source exactly. The synthetic completion deliberately clearssource.message_id(#52694, #123097) so a finished job does not reply-anchor to the message that started it, while Discord and Telegram put the human message id on the committed source.SessionSource.to_dict()writesmessage_idonly when it is truthy, so the two sides could never be equal.This PR compares the routing/provenance identity of both sources with only
message_idexcluded, keeps every other source field and the adapter identity check, and dropsmessage_idfrom the persisted automation envelope so the wake cannot anchor to the stale human message (it restores asmessage_id=None). The key is omitted rather than stored asnullbecause that is the canonicalSessionSource.to_dict()format (it never writes a null anchor), so the stored source looks exactly like one built from a source with nomessage_id, and no new null key enters the hashed payload.Fingerprint compatibility is supported only by a digest probe, not by an integration test: hashing a payload whose source omits
message_idgives the same digest as one whosemessage_idwas popped, and a different digest from one carryingmessage_id: null. Retrying, across versions, an admission recorded before this change is not tested here.#138947 is complementary rather than a duplicate: it bounds the retry loop for a refused admission (stops the log flood), while this PR removes the cause of the refusal so the wake is actually delivered. Both can land.
Background:
main(450ad5055e): [Bug]: Background process completion notifications fail delivery to Discord and spin in infinite retry injection loop #138940 (comment)Related Issue
Fixes #138940
Type of Change
Changes Made
gateway/session_automation.py—snapshot_automationcompares sources through_route_identity()(to_dict()withoutmessage_id) on both sides; the adapter identity check is unchanged; the stored automation envelope source no longer carriesmessage_id.tests/gateway/test_automation_notification_category.py— native Discord regressions in which the committed human source carriesmessage_id(the existing roundtrip test put the id only on theMessageEvent, so it never exercised this):message_id=None) admits, the stored envelope source has nomessage_idkey, the restored source'smessage_idisNone, and a retry returns the same admission (one automation admission);user_name,user_idorthread_idstill raisesadmission_conflict;admission_conflict.How to Test
scripts/run_tests.sh tests/gateway/test_automation_notification_category.py -k "message_id or source_drift or another_adapter" -vscripts/run_tests.sh tests/gateway/test_automation_notification_category.py tests/gateway/test_producer_owner_continuity.py tests/gateway/test_plugin_message_injection.py tests/gateway/test_heartbeat_inflight_owner_scan.py tests/gateway/test_bot_mailbox_async.py tests/gateway/test_webhook_durable_destination.pyruff check gateway/session_automation.py tests/gateway/test_automation_notification_category.pyEvidence
BEFORE (new tests on
main450ad5055e, product change stashed) — exit 1:(The BEFORE run predates tightening the stored-source check from
.get('message_id') is Noneto'message_id' not in source; onmainthe test fails earlier, at admission, so the tightened assertion was only run after the fix.)The runs below used the same hermetic environment as
scripts/run_tests.sh's finalexec(env -i … python scripts/run_tests_parallel.py … -p no:cacheprovider,PYTHONDONTWRITEBYTECODE=1), skipping only itscompileallpre-step, which writes.pycfiles for every tracked file.AFTER (same selection) — exit 0:
Related gateway files (six files above) — exit 0:
An earlier run of the same six files reported
tests/gateway/test_bot_mailbox_async.py::test_drain_defers_retry_without_publishing_interim_failureas flaky (failed once, passed on retry,assert 'settled' == 'claimed'). That test drives the local bot mailbox, which returns throughsnapshot_local_automationbefore the changed code; it passed in three isolated reruns and in the final run above (no flaky file reported).Lint — exit 0:
Checklist
Code
pytest tests/ -qand all tests pass (focused gateway files only, see Evidence)python scripts/checkpasses (not run; ruff on both changed files passed)Documentation & Housekeeping
cli-config.yaml.example— N/ACONTRIBUTING.md/AGENTS.md— N/A