Skip to content

fix(gateway): follow async completions across compression - #64530

Closed
richkapp wants to merge 1 commit into
NousResearch:mainfrom
richkapp:fix/gateway-compression-delegation-tip
Closed

fix(gateway): follow async completions across compression#64530
richkapp wants to merge 1 commit into
NousResearch:mainfrom
richkapp:fix/gateway-compression-delegation-tip

Conversation

@richkapp

Copy link
Copy Markdown
Contributor

Summary

  • Always validate the gateway session captured when an async delegation was dispatched.
  • Treat compression as logical continuation: resolve the ended parent to its live compression tip and deliver there.
  • Advance stale gateway routing with a compare-and-swap update that cannot overwrite /new or reopen SQLite session rows.
  • Keep every unknown, non-compression, ended-tip, or foreign-route case fail-closed.

Problem

The July 8 async-completion guard only validates the captured gateway_session_id when it differs from the current gateway route.

That leaves two compression cases broken:

  1. If sessions.json still points at the ended compressed parent, pinned == current bypasses the ended-row guard and the late completion can continue against the stale parent.
  2. If routing already advanced to the compression child, the guard sees the captured parent as ended and drops a valid completion instead of following the same logical conversation.

This is a gateway follow-up to #55578, #57498, #57576, and #61993. It complements the classic-CLI ownership work in #64240; this PR does not touch CLI or TUI delivery.

Fix

For events carrying the captured parent session:

  1. Read the captured session row even when it equals the current route.
  2. If it is live, preserve the existing pinning behavior.
  3. If it ended for compression, resolve the transitive compression tip, require that tip to be live, and prove the current route belongs to the same lineage.
  4. CAS-advance only the gateway key→session mapping. If another turn moved the route first (for example /new), fail closed.
  5. For every other end reason, lookup error, ended tip, or foreign route, drop injection; the durable delegation record remains available.

Blast radius

  • Producer scope: gateway_session_id is attached by the synthetic completion path from an event's parent_session_id; that field is produced by async delegations, not terminal/background-process completions.
  • Runtime scope: all messaging gateway adapters inherit the fix through GatewayRunner._handle_message; classic CLI and TUI paths are unchanged.
  • Persistence: no schema or transcript changes. The new SessionStore method only CAS-updates the existing routing index and deliberately does not call end_session or reopen_session.
  • Concurrency: /new wins the race. A stale snapshot cannot overwrite a newer route, and an already-advanced compression route is idempotent.
  • Cost: one async session-row lookup for pinned completions; compression-only cases add lineage/tip reads. AsyncSessionDB and AsyncSessionStore keep this work off the event loop.
  • Rollback: revert this single commit; no migration or state repair is required.

Verification

HOME=/home/deploy scripts/run_tests.sh -j 4 \
  tests/gateway/test_async_delegation_session_binding.py \
  tests/gateway/test_session_store_runtime_stale_guard.py \
  tests/gateway/test_background_process_notifications.py \
  tests/gateway/test_completion_delivery.py \
  tests/gateway/test_internal_event_bypass_pairing.py \
  tests/gateway/test_telegram_topic_mode.py \
  tests/gateway/test_async_session_store.py

135 passed

python -m ruff check gateway/run.py gateway/session.py \
  tests/gateway/test_async_delegation_session_binding.py \
  tests/gateway/test_session_store_runtime_stale_guard.py

All checks passed

Broader gateway/delegation run:

HOME=/home/deploy scripts/run_tests.sh -j 4 tests/gateway tests/tools/test_async_delegation.py
9219 passed, 1 failed

The sole failure is the checkout's existing Feishu SDK capability check (lark_oapi.ws.Client lacks extra_ua_tags in the local venv). The identical failure reproduces on a clean origin/main worktree: 209 passed, 1 failed.

Tested on Linux with Python 3.11.

@richkapp
richkapp force-pushed the fix/gateway-compression-delegation-tip branch from c3cdeaf to 48b6ae2 Compare July 14, 2026 17:44
@alt-glitch alt-glitch added type/bug Something isn't working comp/gateway Gateway runner, session dispatch, delivery tool/delegate Subagent delegation P2 Medium — degraded but workaround exists sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages labels Jul 14, 2026
@richkapp
richkapp force-pushed the fix/gateway-compression-delegation-tip branch from 48b6ae2 to 38f0227 Compare July 14, 2026 21:08
@teknium1

Copy link
Copy Markdown
Contributor

Thanks for tracing the compression continuation case. The premise is verified on current main: SessionStore.get_or_create_session() heals a stale route to the compression tip (gateway/session.py:1870-1923), but the subsequent completion pinning guard drops the compression-ended captured parent rather than following that tip (gateway/run.py:10982-11007).

The proposed resolver uses the existing lineage-aware SessionDB.get_compression_tip() contract (hermes_state.py:3236-3297) and separates the safe compression mapping update from switch_session(), whose documented behavior ends the current row and can reopen the target (gateway/session.py:2322-2379). The changed tests cover live, unknown, non-compression-ended, stale-parent, already-advanced, intermediate-lineage, ended-tip, lookup-failure, and /new-CAS-loss cases.

No substantive correctness issue was found in the inspected diff. This is an automated hermes-sweeper review.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-broad Sweeper blast radius: broad — a core path most sessions hit area/compression Context compression and continuation sessions labels Jul 16, 2026
@teknium1

Copy link
Copy Markdown
Contributor

Merged via #69312 (commit b120121). Your commit was cherry-picked with your authorship preserved in git log — you're the base fix for the whole delegation-across-compression cluster (first submitter by 2 days). The salvage adds the #65838-inspired ack-semantics layer on top. Thanks!

@teknium1 teknium1 closed this Jul 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/compression Context compression and continuation sessions comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists sweeper:blast-broad Sweeper blast radius: broad — a core path most sessions hit sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state tool/delegate Subagent delegation type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants