Skip to content

Loom memory: send a call again when the gateway did not send it - #681

Merged
pufit merged 1 commit into
mainfrom
pufit/loom-session-reset-retry
Nov 10, 2026
Merged

pufit merged 1 commit into
mainfrom
pufit/loom-session-reset-retry

Conversation

@pufit

@pufit pufit commented Nov 10, 2026

Copy link
Copy Markdown
Member

Nerve's Loom memory backend makes its lifecycle calls (the startup recall, transcript extraction, the health probe) through the MCP gateway. When the session that the gateway keeps with Loom has ended, the gateway answers the next call once with MCP server session ended; the state of earlier calls is lost (JSON-RPC -32002) and does not send it.

Before. A read spent its second try on that answer. A transcript for extraction is not sent again after a failure, because a lost answer may have queued it, so it failed and the window waited for the next sweep.

Now. A call that got this answer goes again at once, a transcript too: it did not reach Loom, so nothing can be extracted twice. It does not use up a read's second try. The gateway can give the answer a few times in a row; Nerve sends the call again at most three times, and a fourth such answer is a failure like any other -32002.

Only the gateway's own answer counts: code -32002, the message word for word, and no Retry-After. Any other failure may come after the call reached Loom and is handled as before.

Changes.

  • nerve/mcp_gateway.py: SESSION_ENDED_MESSAGE, the gateway's fixed message, beside UPSTREAM_TIMEOUT_MESSAGE.
  • nerve/memory/loom.py: _send makes one call on the client of Loom's server and sends it again on that answer (_SESSION_ENDED_RESENDS = 3); _call keeps its rules for every other failure. Each send has the call's own timeout: the gateway gives this answer before it reaches Loom, so there is no time limit across the sends.
  • docs/config.md: the paragraph "When Loom cannot answer".

Tests. tests/test_loom_memory.py: a transcript and a read that get the answer go again, and the read still has its second try for another failure, before or after; other words, another code and a paced answer do not send a transcript again; the answer four times in a row is an outage and nothing is extracted. The new tests fail when the call is not sent again, and each condition of the match has a case that fails without it. Run: tests/test_loom_memory.py tests/test_mcp_gateway_tools.py tests/test_loom_contract.py — 119 passed. No web changes.

Not in this change: an agent's own Loom tool calls go through the agent's MCP client, not through this code, and are not sent again.

Generated by Nerve

When the session that the MCP gateway keeps with Loom has ended, the
gateway answers the next call once with "MCP server session ended; the
state of earlier calls is lost" and does not send it. A read used its
second try on that answer, and a transcript for extraction, which is not
sent again after a failure, failed and waited for the next sweep.

Such a call now goes again at once, a transcript too, since it did not
reach Loom. It does not use up a read's second try. The gateway can give
the answer a few times in a row: Nerve sends the call again at most three
times. Only the gateway's own answer counts: its code, its message word
for word, and no Retry-After.
@nerveai

nerveai commented Nov 10, 2026 •

Copy link
Copy Markdown

Nerve PR review · ✅ Ready to merge

When the gateway's session with Loom has ended, it refuses the next call once with a fixed -32002 answer and does not forward it. On main, that answer costs a read its second try and fails an extraction batch until the next sweep. The new _send (nerve/memory/loom.py:757-788) resends only that answer, at most three times. It matches the code, the exact message and no Retry-After, the same pattern as UPSTREAM_TIMEOUT_MESSAGE, so a failure that may have reached Loom never resends a transcript. No lifecycle op depends on MCP session state, so a resend in the gateway's new session is equivalent. This is a small, bounded fix for a recurring lost-extraction path.

Completeness: tests for each match condition, the resend bound, and a read keeping its second try; docs/config.md updated; no config or migration needed.

Checked: full suite in an offline sandbox at fcdd403 (3 chunks): 8962 passed, 39 skipped; test_slack_integration.py excluded (hangs offline on main too); CI green; merges cleanly; read loom.py _call/_send/_gateway_failure and mcp_gateway.py call_tool; the gateway's "not forwarded" contract is outside this repo and taken from the PR · automated review by nerveai — mention @nerveai to ask a follow-up.

@pufit
pufit merged commit a32d164 into main Nov 10, 2026
4 checks passed
@pufit
pufit deleted the pufit/loom-session-reset-retry branch November 10, 2026 19:41
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.

3 participants