Skip to content

fix(kv-unified): supersede an unsettled receipt flight instead of failing the next submission - #101

Merged
antra-tess merged 1 commit into
mainfrom
fix/kv-unified-supersede-open-flight
Sep 16, 2026
Merged

antra-tess merged 1 commit into
mainfrom
fix/kv-unified-supersede-open-flight

Conversation

@Anarchid

@Anarchid Anarchid commented Sep 16, 2026 •

Copy link
Copy Markdown
Contributor

Symptom

Devops agent (conhost d1ce7d2 = AF 0.13.0 + CM 0.8.0, foldingStrategy: kv-unified), 2026-09-16 07:33:47Z:

[inference-failed] agent=devops consecutive=1: kv-unified submission devops:46:1789544019150:4 is still in flight

First failure in its streak, no provider error before it. The turn was lost.

Mechanism

KvUnifiedReceiptChain.begin() throws when a flight is pending. The host opens a flight once per provider attempt (membrane fires the wire receipt before the adapter call) and only that attempt's usage event settles it. When the attempt dies without usage — transport error, idle timeout, a host cancel for a budget restart or endTurn — the flight stays open until the host's stream teardown, and every host path that starts a successor stream runs before that teardown. The successor's first receipt then hits the guard, and the recovery itself fails.

Nothing can ever settle that flight once a new request is composed: accept() requires the matching in-flight id. Defending it only converts one transient into a second, self-inflicted failure.

Fix

begin() supersedes a pending flight: the old id is marked settled (late accept/fail for it are duplicate no-ops, across serialize/deserialize too) and returned as { superseded }; AutobiographicalStrategy.beginKvUnifiedSubmission logs it:

[kv-unified] submission <old> was never settled (no usage event before the next provider call); superseded by <new>

Presentation continuity is untouched: superseding never advances the head, and the new flight advances only on its own acceptance.

Companion: anima-research/agent-framework#154 fixes the host side (the agent fails its predecessor's open flight before submitting). Either fix alone clears the incident; together they are defence in depth. Independent of #98 (stale receipt / label explosion).

Tests

  • test/adaptive/kv-unified-receipts.test.ts: single-flight test updated (no longer asserts the throw); new test for supersession, late-callback no-ops, and persistence of the superseded id.
  • Full suite: 760/760.

🤖 Generated with Claude Code

https://claude.ai/code/session_01AhZDu1aVjg2wmu9kceQcxa

…ling the next submission

Membrane fires the cache-wire receipt once per provider attempt, before the
adapter call; only that attempt's usage event settles the flight. When the
attempt dies without usage (transport error, idle timeout, framework cancel)
the flight stays open until the host's stream teardown, and every host path
that starts a successor stream runs before that teardown. The successor's
first receipt then hit begin()'s single-flight guard and the recovery itself
failed: "kv-unified submission devops:46:1789544019150:4 is still in flight"
(devops agent on gpt-6-astra, 2026-09-16 07:33Z, no provider error logged).

Nothing can ever settle a flight once a new request is composed — accept()
requires the matching in-flight id — so begin() now supersedes it: the old id
is marked settled (late accept/fail become duplicate no-ops, across
persistence too) and returned, and the strategy logs the supersession.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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.

2 participants