Skip to content

Retract recovered inference errors on failover - #711

Merged
TheGreatAxios merged 4 commits into
mainfrom
cl-7119-hide-recovered-quota-and-session-expired-errors-from-the
Aug 28, 2026
Merged

Retract recovered inference errors on failover#711
TheGreatAxios merged 4 commits into
mainfrom
cl-7119-hide-recovered-quota-and-session-expired-errors-from-the

Conversation

@TheGreatAxios

Copy link
Copy Markdown
Collaborator

Summary

  • Same-turn failover now retracts recovered inference error rows the same way committed retry already does
  • Echoed auto-retry prompts no longer expire the recovery handoff before the backup inference starts
  • Locally queued or steered user rows survive rollback while failed attempt rows disappear

Verification

  • bun test ./src/tui/stream-event-map.test.ts ./src/tui/runtime-bridge.test.ts
  • bun run check

Fixes CL-7119

Committed retry already rolled back the failed attempt. Failover is a
new inference.start, so the error row stayed. Treat that start as
recovery too, so quota and credential chrome disappear when the turn
continues.
@linear-code

linear-code Bot commented Aug 28, 2026

Copy link
Copy Markdown

CL-7119

@TheGreatAxios TheGreatAxios left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Lens: critic. Verdict: no blocking findings.

This branch retracts recovered inference-error rows on same-turn failover, preserves local queue/steer rows during rollback, and keeps terminal failures visible. Re-review verified the prior echo and local-row rollback holes are fixed. I found no remaining blocking or should-fix correctness issues.

@TheGreatAxios TheGreatAxios left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Critic · Comment

Failover inference.error then inference.start retracts recovered quota/credential rows; queued/steer rows survive truncate.

Findings

  • src/tui/runtime-bridge.ts:429 — global echo skip keeps errorRollbackArmed across a new turn. Sequence: error (handoff armed) → interrupt (idle, handoff still armed) → immediate submit → echoed message.received (mapping skipped) → inference.start (ATTEMPT_ROLLBACK). Untagged user rows, the stopped line, and the terminal error disappear. AC 4 and AC 6 collide.
  • src/tui/stream-event-map.ts:168connector.reply is not retry/start, so the one-event handoff dies before TUI long-window auto-retry can retract.
  • src/tui/runtime-bridge.test.ts:975 — terminal test only asserts errorRows.length > 0. A swallowed session-expired line plus failed still passes. Pin the classified copy.

@TheGreatAxios TheGreatAxios left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Neckbeard · Comment

Rollback copies operator rows out, truncates, then re-appends them.

Findings

  • src/tui/runtime-bridge.ts:726Array.from({ length }) plus a type-predicate filter walks [boundary, count) instead of a for-loop.
  • src/tui/runtime-bridge.ts:712isLocallyQueuedUserRow treats drain tags (steering, following-up) as queued and omits reinject.
  • src/tui/runtime-bridge.ts:707 — rollback JSDoc sits on isLocallyQueuedUserRow, which does not retract anything.

@TheGreatAxios TheGreatAxios left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Review · Request changes

Same-turn failover retracts recovered inference-error rows and re-appends queued/steer user rows.

GitHub refuses request-changes on the author's own PR, so this is a comment with the same blocking bar.

Findings

  • src/tui/runtime-bridge.ts:1080consumePendingEchoEvent returns from handle before mapProductionEvent, so a message.received echo never expires errorRollbackArmed. Interrupt does not clear that handoff or attemptRow. A later immediate prompt is untagged (runtime-bridge.ts:1163), then the next inference.start rolls back from the old mark and deletes the new prompt, the stopped row, and the terminal error. Scope the echo skip to in-flight recovery, or expire the handoff on interrupt.
  • src/tui/runtime-bridge.ts:712 — rollback only re-appends queue / steer / steering / following-up. Immediate submits have no meta, so they disappear on that rollback.
  • src/tui/runtime-bridge.test.ts:900 — echoed auto-retry test never asserts the echoed prompt survives, and does not cover interrupt then a new prompt.
  • src/tui/stream-event-map.ts:296connector.reply expires the one-event handoff, so a director-visible quota error followed by the TUI quota-loop replay cannot retract the row.

Reinject still stops the run before sending. Clear the one-event
handoff there too, and keep reinject rows if a rollback still fires.

@TheGreatAxios TheGreatAxios left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Review · Request changes

Treats a same-turn failover inference.start after inference.error as recovery and rolls back the failed attempt including its error row, keeps locally queued user rows across that rollback, short-circuits echoed message.received events before the mapper, and expires the handoff on interrupt/reinject.

Findings

  • src/tui/runtime-bridge.ts:1081if (consumePendingEchoEvent(bag, event)) return; skips the bookkeeping every echoed user message used to run via applyInbound: bag.turnThinking = null at :848, plus closeOpenRow/disarmAttempt in stream-event-map.ts:328. After a steer or follow-up delivered at a tool boundary, the next cycle's thinking deltas fold into the pre-steer reasoning row above the operator's message instead of opening a new row below it. Reproduced against both trees with: submit → echo → inference.start → thinking "planning" → tool_call.endinference.donesubmit(..., "steer")tool.done → echo → inference.start → thinking "after steer" → text. main: thinking:"planning", tool, user/steer, thinking:"after steer", assistant. branch: thinking:"planning\n\nafter steer", tool, user/steer, assistant. Running closeOpenRow(shell, bag); bag.turnThinking = null; in the short-circuit path restores the split. None of the new tests assert thinking-row placement after a steer echo.
  • src/tui/runtime-bridge.test.ts:906-945 — the test never asserts the fate of the "retry this" user row. It is a plain role:"user" row (no meta, runtime-bridge.ts:1166) so rollbackAttempt truncates it (isLocallyQueuedUserRow at :707 only spares meta rows). Probably desired for the quota auto-replay, but it is unstated — pin it either way.

Notes

  • isLocallyQueuedUserRow covering "queue" and "steer" metas is correct; those rows sit after the boundary only by ordering.
  • Checks: bun run typecheck, bun run lint, bun test src/tui/stream-event-map.test.ts src/tui/runtime-bridge.test.ts (77 pass).

Consuming an echoed message before the mapper kept the recovery handoff alive but skipped the turn bookkeeping, so reasoning after a steer folded into the row above the operator's message. Close the open row and reset the turn thinking on that path, and pin that the replayed quota prompt row is dropped by the rollback.
@TheGreatAxios
TheGreatAxios force-pushed the cl-7119-hide-recovered-quota-and-session-expired-errors-from-the branch from 2e6f6ba to aa031e9 Compare August 28, 2026 19:16

@TheGreatAxios TheGreatAxios left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Critic · Approve

Same-turn failover after inference.error retracts the recovered error row; echoed auto-retry no longer expires the handoff; interrupt/reinject expire it; locally queued/steer rows survive rollback. Prior review holes are closed in the current tip.

Findings

Blocking: none.

Merge readiness: ready. CI green (build-and-test, typecheck, eslint, prettier, cla). Targeted tests pass (stream-event-map, runtime-bridge).

Verified against tip (aa031e9c):

  • stream-event-map.ts:168 / :333recoversErrorHandoff treats inference.start like inference.retry; recovered start emits ATTEMPT_ROLLBACK then remakes the boundary.
  • runtime-bridge.ts:1081 — echoed message.received skips the mapper so the one-event handoff survives, and still runs closeOpenRow + clears turnThinking so post-steer reasoning opens a new row.
  • runtime-bridge.ts:1207 / :1148 — interrupt and reinject clear errorRollbackArmed and attemptRow, so a later new-turn start cannot erase a terminal error or the operator's next prompt.
  • runtime-bridge.ts:707 / :723 — rollback re-appends queue/steer/steering/following-up/reinject rows after truncate.

File-for-later (non-blocking):

  • runtime-bridge.ts:1081 — echo short-circuit skips mapper disarmAttempt. Product mid-run Enter is meta-tagged (steer/queue) and survives rollback; a direct submit(..., "immediate") while an attempt is still armed could still lose an untagged user row on a later retry. Not reachable from current product chords.
  • Long-window TUI quota auto-retry is intentionally durable (turn-monitor.test.ts comment); the one-event handoff is for same-turn recovery only. Out of scope here.

Recommended permanent tests (already present; keep)

  • Failover start after quota/credential error clears the error row.
  • Echoed auto-retry keeps recovery and drops the duplicate prompt.
  • Interrupt/reinject then new prompt keeps classified error + prompt.
  • Queued steer survives failover rollback.
  • Steer echo opens a new thinking row below the steering line.

@TheGreatAxios
TheGreatAxios merged commit 1cfffe3 into main Aug 28, 2026
5 checks passed
TheGreatAxios added a commit that referenced this pull request Aug 28, 2026
sendFailureText rematched the #710 credential_failure line against
raw-provider auth patterns and replaced it with the generic other
copy. #711 tests still expected the pre-#710 session-expired string.
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.

1 participant