Skip to content

[Fix] Reactions and platform events no longer supersede parked Fast turns - #2183

Merged
roomote-roomote[bot] merged 3 commits into
developfrom
fix/fast-supersession-scope
Sep 5, 2026
Merged

[Fix] Reactions and platform events no longer supersede parked Fast turns#2183
roomote-roomote[bot] merged 3 commits into
developfrom
fix/fast-supersession-scope

Conversation

@mrubens

@mrubens mrubens commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

What the user sees today

A Fast turn hits a provider error and parks for a durable retry: the thread shows "Retrying in 30s" and the conversation lock is released while it waits. The same state exists for a few seconds after a deploy, while an interrupted turn waits for the queue to resume it.

If the user reacts with an emoji during that window (or, on the web, a setup kickoff or input response fires), the original question is gone. The reaction turn is admitted as a new inline turn, and inline admission discards every older pending turn row for the conversation. The parked turn's row is one of them. The reaction turn's entry then reconciles the "Retrying" notice into an interruption message, so the thread reads: their question, "Retrying in 30s" turned into "I was interrupted", then the reply to the reaction. The question is never answered and nothing tells them why.

This is the same rule that makes a typed message supersede an older pending turn, which is intentional: a typed message stands in for the earlier request, and the new turn is told about the request it still owes (findFastAgentUnresolvedRequest). A reaction or platform event does neither. #2156 routed them through the same admission path without narrowing the rule.

Change

  • Supersession is scoped to typed human messages. persistFastAgentInlineHumanTurn only discards older pending inline rows when the new event has no reaction input and is not a platform_event. A reaction or platform-event turn still gets its own durable row and runs normally; the older turn keeps its row and resumes once the conversation is idle again (the queue's drain hits the busy lock and retries every second until then).
  • Reconciles respect pending rows. The next-turn and turn-settled reconciles (reconcileFastAgentInferenceRetryNotices) now skip when another inline row for the conversation is still pending, excluding the calling turn's own row. Without this, the reaction turn would still stamp the parked turn's notice as interrupted, and the resumed run would then post its answer beside a false interruption instead of editing the notice in place. The expired-lease reconciler gets the same guard through the shared helper; its candidate query already filtered claimed and scheduled rows.

Typed-message behavior is unchanged.

What the user sees after

React to a message while a turn is retrying and you get the reaction's reply, the "Retrying" notice stays put, and the original question is answered when the retry fires, editing the notice in place as it always did.

Testing

  • fast-agent-human-follow-up.test.ts: a reaction and a platform event persist their own row without the supersede sweep; a typed message still supersedes; the resumption and dedupe cases are unchanged.
  • fast-agent-conversation-repository.test.ts (real DB): a next-turn reconcile with another pending inline row returns 0 and leaves the notice active; once that row settles, the caller's own row alone does not block it and the notice is stamped.
  • Full fast-agent-service suite (175), the drain and lock suites, pnpm lint:fast, pnpm check-types:fast, pnpm knip pass. Two service assertions that checked the reconcile was not called with a given reason now inspect the reason argument directly, since the call gained a third argument.

Not exercised live. The parked-turn resume path itself is the one #2040 smoke-tested; this change only stops a reaction from discarding it.

…urns

Every inline admission discarded the conversation's older pending inline
rows on the assumption that a newer human message stands in for the earlier
request. #2156 routed emoji reactions and web platform events through the
same path, so a reaction while a turn was parked for a retry, or waiting to
resume after an interruption, silently dropped that turn and turned its
retry notice into an interruption message.

Only typed human messages supersede now. The next-turn and settle reconciles
also leave a retry notice alone while another durable row for the
conversation is still pending, so the resumed run edits it into the answer
instead of posting beside a false interruption.
@roomote-community

roomote-community Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

No code issues found. See task

  • packages/cloud-agents/src/server/fast-agent/fast-agent-conversation-repository.ts:142-154 - The expired-lease reconciler is blocked by expired or released pending rows, leaving stale retry notices active when their queue handoff cannot run.

Reviewed a800c13

The pending-row guard now counts only a live claim or a scheduled retry on
the expired-lease path, so a released or expired row whose queue wakeup
never runs cannot leave a stale retry notice active forever.
@roomote-roomote
roomote-roomote Bot merged commit 6729a33 into develop Sep 5, 2026
17 checks passed
@roomote-roomote
roomote-roomote Bot deleted the fix/fast-supersession-scope branch September 5, 2026 19:09
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