Skip to content

fix(chat): retry released queue inputs - #2137

Open
yyhhyyyyyy wants to merge 3 commits into
devfrom
fix/issue-2112-pending-input-retry
Open

fix(chat): retry released queue inputs#2137
yyhhyyyyyy wants to merge 3 commits into
devfrom
fix/issue-2112-pending-input-retry

Conversation

@yyhhyyyyyy

@yyhhyyyyyy yyhhyyyyyy commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Add a durable retry_required state for Queue inputs released after a pre-user-fact failure or transcript rollback.
  • Expose an explicit per-item Retry action in the pending input lane.
  • Preserve FIFO ordering, restart-held Queue behavior, ACP compatibility, and edit-as-retry behavior.
  • Add regression coverage across persistence, runtime admission, routes, restart recovery, ACP, and renderer behavior.

Closes #2112.

Root Cause

PR #2023 centralized pending input claim, release, and single-flight draining in PendingInputPump.

When a claimed Queue input failed before its user fact was committed, or after the fact was rolled back, it was released to the FIFO head as an ordinary pending row. The pump intentionally did not replay that row automatically, but the renderer could not distinguish it from a normal draft and exposed no explicit recovery action.

PR #2129 added restart recovery and Resume Queue behavior, which made the issue more visible, but did not introduce the original defect.

Changes

  • Add retry_required to PendingSessionInputState.
  • Transition failed DeepChat Queue settlements from claimed to retry_required.
  • Keep intentional Queue-to-Steer mutation and restart repair releases as ordinary pending.
  • Keep retry-required rows in FIFO ordering and capacity accounting while excluding them from automatic dispatch.
  • Add a typed, session-gated sessions.retryPendingQueueInput route.
  • Validate session ownership, retry state, and FIFO-head position inside the persistence transaction.
  • Return separate accepted and started results so an accepted retry delayed by a runtime gate is not reported as a failure.
  • Preserve retry acceptance when event publication or the first immediate drain fails after persistence.
  • Keep ACP pending-input semantics unchanged.
  • Preserve attachmentFallbackPolicy when editing a Queue item.
  • Add localized Retry labels and descriptions for all supported locales.

No database migration is required because the pending input state column is unconstrained text.

UI

Before

┌──────────────────────── Queue ────────────────────────┐
│ Failed message                              [Steer][×] │
└───────────────────────────────────────────────────────┘

The failed item remained at the Queue head without explaining why the Queue was blocked or how to continue it.

After

┌──────────────────────── Queue ────────────────────────┐
│ Failed message   This message wasn't sent             │
│                  [Retry required]          [Retry][×]  │
└───────────────────────────────────────────────────────┘

Retry-required rows cannot be reordered or converted to Steer. They can be retried, edited and retried, or removed.

Safety and Compatibility

  • Later Queue items cannot bypass a retry-required FIFO head, including through the question-follow-up fast path.
  • Duplicate Retry requests produce at most one state transition and turn.
  • Restart-held Queue rows retain the existing Resume Queue contract.
  • A retry-required head prevents a restart-held tail from being resumed out of order.
  • ACP continues releasing failed Queue claims to ordinary pending.
  • Retry diagnostics do not include message content, attachment paths, or payload data.
  • Publication and immediate-drain failures after a successful state transition remain recoverable.

Validation

  • pnpm run format
  • pnpm run i18n
  • pnpm run lint
  • pnpm run typecheck:node
  • pnpm run typecheck:web
  • Main pending-input, route, session, backend, and ACP tests — 210 passed
  • Renderer pending-input tests — 29 passed
  • DeepChat harness failure and rollback boundaries — 4 passed

Twelve native SQLite tests were skipped by the repository harness because the locally installed native module ABI does not match the Node ABI used by Vitest. Equivalent store, FIFO, retry, and restart contracts passed in the non-native suites.

Summary by CodeRabbit

  • New Features

    • Added explicit retry handling for queued messages that cannot proceed automatically.
    • Retry-required items remain at the queue head until manually retried.
    • Added Retry actions with loading, failure, and recovery feedback.
    • Editing a retry-required message now retries it safely.
    • Added localized retry messaging across supported languages.
  • Bug Fixes

    • Improved restart and queue-resume behavior for interrupted messages.
    • Prevented duplicate retries and preserved queued message settings during edits.
    • Prevented later queued messages from bypassing items requiring retry.

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: dd61d8e5-f9c9-44d3-9363-1144743eeab8

📥 Commits

Reviewing files that changed from the base of the PR and between 6aaa23b and e665348.

📒 Files selected for processing (1)
  • src/renderer/src/i18n/ru-RU/chat.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/renderer/src/i18n/ru-RU/chat.json

📝 Walkthrough

Walkthrough

The PR adds durable retry_required handling for released DeepChat Queue inputs. It preserves FIFO blocking, adds explicit retry APIs from the renderer to the runtime, supports restart recovery, and exposes localized retry controls.

Changes

Explicit Queue Input Retry

Layer / File(s) Summary
Durable retry state and transitions
docs/issues/..., src/main/session/data/*, src/shared/types/agent-interface.d.ts, test/main/session/data/*
Queue inputs can enter retry_required, remain at the queue head, survive restart, and transition once back to pending. Editing and reordering rules handle this state.
Pump rollback and explicit retry admission
src/main/agent/deepchat/runtime/*, test/main/agent/deepchat/runtime/*, test/main/agent/deepchat/harness/*
Claim settlement separates mutation releases from retry releases. Explicit retries validate ownership, state, and head position before persistence and draining.
Session and route retry API
src/main/agent/manager/*, src/main/app/composition.ts, src/main/session/*, src/shared/contracts/routes/*, src/renderer/api/SessionClient.ts, test/main/*
The retry operation is exposed through runtime contracts, session handles, the session gate, the route catalog, and the renderer client.
Renderer retry controls and localization
src/renderer/src/components/chat/PendingInputLane.vue, src/renderer/src/features/chat-page/*, src/renderer/src/stores/ui/pendingInput.ts, src/renderer/src/i18n/*, test/renderer/*
The renderer displays retry-required status, disables conflicting queue actions, prevents concurrent retries, refreshes queue state, starts a plan turn when applicable, and adds translations.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Sequence Diagram(s)

sequenceDiagram
  participant PendingInputLane
  participant PendingInputStore
  participant SessionClient
  participant SessionTurn
  participant PendingInputAdmissionCoordinator
  participant PendingSessionInputStore

  PendingInputLane->>PendingInputStore: retryQueueInput(sessionId, itemId)
  PendingInputStore->>SessionClient: retryPendingQueueInput(sessionId, itemId)
  SessionClient->>SessionTurn: invoke retry route
  SessionTurn->>PendingInputAdmissionCoordinator: retryPendingQueueInput(sessionId, itemId)
  PendingInputAdmissionCoordinator->>PendingSessionInputStore: retryReleasedQueueInput(itemId)
  PendingInputAdmissionCoordinator->>PendingInputAdmissionCoordinator: drain or schedule queue
  PendingInputStore->>PendingInputLane: refresh item and clear retrying state
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.35% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main change: adding retry support for released chat queue inputs.
Linked Issues check ✅ Passed The implementation satisfies the linked issue [#2112] with retry-required state, explicit idempotent retry, FIFO protection, preserved attachment behavior, and regression tests.
Out of Scope Changes check ✅ Passed The documentation, runtime, persistence, routing, renderer, localization, and tests all support the retry-required queue-input objectives in [#2112].
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/issue-2112-pending-input-retry

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/renderer/src/i18n/ru-RU/chat.json`:
- Line 77: Update the retryRequired translation in the Russian chat localization
to use “Требуется повторная попытка,” clearly indicating that the unsent queue
message must be retried.

In `@test/renderer/stores/pendingInputStore.test.ts`:
- Line 97: Remove the duplicate const retry declaration in the affected test
scope, keeping the single existing createDeferred<{ accepted: boolean; started:
boolean }>() declaration and all references to retry unchanged.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d28fb894-de3d-44e5-b52b-d2ae20846b5e

📥 Commits

Reviewing files that changed from the base of the PR and between 4d02c92 and 60d189b.

📒 Files selected for processing (53)
  • docs/issues/pending-input-explicit-retry/spec.md
  • src/main/agent/deepchat/harness/deepChatAgentHarness.ts
  • src/main/agent/deepchat/runtime/pendingInputAdmissionCoordinator.ts
  • src/main/agent/deepchat/runtime/pendingInputContracts.ts
  • src/main/agent/deepchat/runtime/pendingInputPump.ts
  • src/main/agent/manager/deepChatAgentBackend.ts
  • src/main/agent/manager/sessionHandles.ts
  • src/main/app/composition.ts
  • src/main/session/contracts.ts
  • src/main/session/data/pendingInputStore.ts
  • src/main/session/data/pendingInputs.ts
  • src/main/session/routes.ts
  • src/main/session/turn.ts
  • src/renderer/api/SessionClient.ts
  • src/renderer/src/components/chat/PendingInputLane.vue
  • src/renderer/src/features/chat-page/ChatPage.vue
  • src/renderer/src/features/chat-page/composables/usePendingInputActions.ts
  • src/renderer/src/i18n/da-DK/chat.json
  • src/renderer/src/i18n/de-DE/chat.json
  • src/renderer/src/i18n/en-US/chat.json
  • src/renderer/src/i18n/es-ES/chat.json
  • src/renderer/src/i18n/fa-IR/chat.json
  • src/renderer/src/i18n/fr-FR/chat.json
  • src/renderer/src/i18n/he-IL/chat.json
  • src/renderer/src/i18n/id-ID/chat.json
  • src/renderer/src/i18n/it-IT/chat.json
  • src/renderer/src/i18n/ja-JP/chat.json
  • src/renderer/src/i18n/ko-KR/chat.json
  • src/renderer/src/i18n/ms-MY/chat.json
  • src/renderer/src/i18n/pl-PL/chat.json
  • src/renderer/src/i18n/pt-BR/chat.json
  • src/renderer/src/i18n/ru-RU/chat.json
  • src/renderer/src/i18n/tr-TR/chat.json
  • src/renderer/src/i18n/vi-VN/chat.json
  • src/renderer/src/i18n/zh-CN/chat.json
  • src/renderer/src/i18n/zh-HK/chat.json
  • src/renderer/src/i18n/zh-TW/chat.json
  • src/renderer/src/stores/ui/pendingInput.ts
  • src/shared/contracts/routes.ts
  • src/shared/contracts/routes/sessions.routes.ts
  • src/shared/types/agent-interface.d.ts
  • test/main/agent/deepchat/harness/deepChatAgentHarness.test.ts
  • test/main/agent/deepchat/runtime/pendingInputAdmissionCoordinator.test.ts
  • test/main/agent/deepchat/runtime/pendingInputPump.test.ts
  • test/main/agent/manager/deepChatAgentBackend.test.ts
  • test/main/routes/dispatcher.test.ts
  • test/main/session/data/pendingInputStore.test.ts
  • test/main/session/data/pendingInputs.test.ts
  • test/main/session/data/tables/deepchatPendingInputsTable.test.ts
  • test/main/session/turn.test.ts
  • test/renderer/components/PendingInputLane.test.ts
  • test/renderer/features/chat-page/composables/usePendingInputActions.test.ts
  • test/renderer/stores/pendingInputStore.test.ts

Comment thread src/renderer/src/i18n/ru-RU/chat.json Outdated
Comment thread test/renderer/stores/pendingInputStore.test.ts

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds durable, explicit retry handling for failed Queue inputs while preserving FIFO, restart, ACP, and attachment behavior.

Changes:

  • Introduces retry_required persistence and retry routing.
  • Adds localized retry UI and edit-as-retry support.
  • Expands regression coverage across runtime, persistence, routes, and renderer.

Reviewed changes

Copilot reviewed 52 out of 53 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
test/renderer/stores/pendingInputStore.test.ts Tests retry deduplication and deferred acceptance.
test/renderer/features/chat-page/composables/usePendingInputActions.test.ts Tests retry actions and payload preservation.
test/renderer/components/PendingInputLane.test.ts Tests retry-required lane controls.
test/main/session/turn.test.ts Tests gated retry delegation and ACP rejection.
test/main/session/data/tables/deepchatPendingInputsTable.test.ts Tests native persistence and FIFO blocking.
test/main/session/data/pendingInputStore.test.ts Tests retry transitions and ordering constraints.
test/main/session/data/pendingInputs.test.ts Tests ownership and restart recovery.
test/main/routes/dispatcher.test.ts Tests retry route dispatch.
test/main/agent/manager/deepChatAgentBackend.test.ts Tests backend retry delegation.
test/main/agent/deepchat/runtime/pendingInputPump.test.ts Tests release, FIFO, and restart behavior.
test/main/agent/deepchat/runtime/pendingInputAdmissionCoordinator.test.ts Tests retry admission and failure recovery.
test/main/agent/deepchat/harness/deepChatAgentHarness.test.ts Updates end-to-end failure-state expectations.
src/shared/types/agent-interface.d.ts Adds the retry_required state.
src/shared/contracts/routes/sessions.routes.ts Defines the typed retry route.
src/shared/contracts/routes.ts Registers the retry route.
src/renderer/src/stores/ui/pendingInput.ts Manages retry state and requests.
src/renderer/src/i18n/zh-TW/chat.json Adds Traditional Chinese retry copy.
src/renderer/src/i18n/zh-HK/chat.json Adds Hong Kong Chinese retry copy.
src/renderer/src/i18n/zh-CN/chat.json Adds Simplified Chinese retry copy.
src/renderer/src/i18n/vi-VN/chat.json Adds Vietnamese retry copy.
src/renderer/src/i18n/tr-TR/chat.json Adds Turkish retry copy.
src/renderer/src/i18n/ru-RU/chat.json Adds Russian retry copy.
src/renderer/src/i18n/pt-BR/chat.json Adds Brazilian Portuguese retry copy.
src/renderer/src/i18n/pl-PL/chat.json Adds Polish retry copy.
src/renderer/src/i18n/ms-MY/chat.json Adds Malay retry copy.
src/renderer/src/i18n/ko-KR/chat.json Adds Korean retry copy.
src/renderer/src/i18n/ja-JP/chat.json Adds Japanese retry copy.
src/renderer/src/i18n/it-IT/chat.json Adds Italian retry copy.
src/renderer/src/i18n/id-ID/chat.json Adds Indonesian retry copy.
src/renderer/src/i18n/he-IL/chat.json Adds Hebrew retry copy.
src/renderer/src/i18n/fr-FR/chat.json Adds French retry copy.
src/renderer/src/i18n/fa-IR/chat.json Adds Persian retry copy.
src/renderer/src/i18n/es-ES/chat.json Adds Spanish retry copy.
src/renderer/src/i18n/en-US/chat.json Adds English retry copy.
src/renderer/src/i18n/de-DE/chat.json Adds German retry copy.
src/renderer/src/i18n/da-DK/chat.json Adds Danish retry copy.
src/renderer/src/features/chat-page/composables/usePendingInputActions.ts Implements renderer retry handling.
src/renderer/src/features/chat-page/ChatPage.vue Connects retry state and events.
src/renderer/src/components/chat/PendingInputLane.vue Displays retry status and controls.
src/renderer/api/SessionClient.ts Exposes the retry client method.
src/main/session/turn.ts Gates retry through session operations.
src/main/session/routes.ts Handles the retry route.
src/main/session/data/pendingInputStore.ts Persists retry state and transitions.
src/main/session/data/pendingInputs.ts Coordinates retry ownership and recovery.
src/main/session/contracts.ts Extends session retry contracts.
src/main/app/composition.ts Wires retry into runtime composition.
src/main/agent/manager/sessionHandles.ts Extends the DeepChat control facet.
src/main/agent/manager/deepChatAgentBackend.ts Delegates backend retries.
src/main/agent/deepchat/runtime/pendingInputPump.ts Enforces retry-aware release and draining.
src/main/agent/deepchat/runtime/pendingInputContracts.ts Adds mutation-specific release disposition.
src/main/agent/deepchat/runtime/pendingInputAdmissionCoordinator.ts Implements retry admission and scheduling.
src/main/agent/deepchat/harness/deepChatAgentHarness.ts Exposes runtime retry operations.
docs/issues/pending-input-explicit-retry/spec.md Documents the design and validation.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/main/session/data/pendingInputStore.ts

@zerob13 zerob13 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two actionable state-machine issues remain. The first is reproduced by a failing regression on the current head; the second combines two already-supported paths that are only tested separately.

private getWaitingQueueRows(sessionId: string): DeepChatPendingInputRow[] {
return this.getQueueRows(sessionId).filter(
(row) => row.state === 'pending' || row.state === 'blocked'
(row) => row.state === 'pending' || row.state === 'blocked' || row.state === 'retry_required'

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Preserve the claimed Queue slot during every resequence. getWaitingQueueRows() excludes the active claimed row, while move/delete/conversion renumber the visible tails from 1. Reproduced on this head: reorder a newer item to the head, let the pump claim it, reorder an older tail, then release the claimed item to retry_required; both rows get queue_order = 1, the older tail wins created_at, and getNextPendingQueueInput() returns that tail instead of null. A later wake can therefore send the wrong item before the required retry. Include active claimed rows when resequencing (or preserve their occupied slots) and cover move/delete/conversion during a claim followed by retry release.

if (row.state !== 'retry_required') {
throw new Error(`Pending queue item ${itemId} does not require retry.`)
}
if (this.getWaitingQueueRows(row.session_id)[0]?.id !== itemId) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Do not require a retry-required row to already be the FIFO head. shouldClaimImmediately() intentionally lets an explicit Send bypass restart-held Queue drafts (and the question-follow-up fast path can bypass ordinary drafts), so that claimed row receives a later queue_order. If it fails before the user fact, this PR turns it into a non-head retry_required row; the renderer shows Retry, but this check makes every click silently return accepted: false until unrelated earlier rows are cleared. Transitioning that row to pending is safe because the existing dispatcher still enforces FIFO. Let the transactional operation return accepted/no-op without this head restriction, delete the duplicated coordinator-side sort/preflight, and add a held-head + failing fast-path Send + Retry regression.

@zerob13 zerob13 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One additional persisted-data compatibility issue:

## Fix Design

- Add durable `retry_required` to `PendingSessionInputState`. SQLite already stores unconstrained
text, so no schema migration is required.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Treat retry_required as a persisted contract change even though the SQLite column needs no DDL. A downgrade binary still sees schema v46 and reads this row, but its waiting-row filter excludes the state while its renderer presents ordinary edit/reorder/Steer controls; those mutations reject the unknown state, leaving deletion as the only recovery. The repository SDD contract requires upgrade/rollback considerations for stored user data. Add an explicit downgrade strategy or schema/version fence, and cover/document the behavior of active retry rows when rolling back.

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.

[bug] Expose explicit retry for released queued inputs

3 participants