Skip to content

fix: clear stale failures and duplicate answers after model fallback - #139753

Merged
steipete merged 1 commit into
mainfrom
codex/fallback-history-projection
Sep 6, 2026
Merged

steipete merged 1 commit into
mainfrom
codex/fallback-history-projection

Conversation

@steipete

@steipete steipete commented Sep 6, 2026 •

Copy link
Copy Markdown
Contributor

Related: #139410

What Problem This Solves

Fixes an issue where Control UI users saw “The agent run failed before producing a reply.” and a duplicate ×2 answer after a model fallback had already succeeded. Reloading or opening older history could preserve or reintroduce the stale failure.

Why This Change Was Made

The transcript correctly stored one failed attempt and one successful answer with the same run identity. Keeping the empty failure in display history made the live answer ambiguous to the shared client reconciler. The Gateway now retires empty failed attempts only after a visible successful runtime answer from that exact run, using one projection flow for legacy stream errors and provider failures.

Raw transcript diagnostics, partial replies, structured output, unrelated runs, and unresolved failures remain intact. Incremental history requests reset while recovery can remove a previously displayed row. Historical pages and exact-message lookup share bounded recovery context so navigating history follows the same visibility rule. Ordinary successful turns retain the existing fast path.

User Impact

A successful fallback displays its answer once, without a stale failed-attempt placeholder. Follow-up messages and reloads remain usable. No configuration, transcript migration, or protocol change is required.

Evidence

  • Reproduced the original defect in a real isolated Gateway and Chromium using an injected first-request model restriction, followed by real OpenAI fallback and follow-up responses. Raw SQLite records confirmed one stored answer.
  • The core regression suite failed on the original source for the intended recovery and reconciliation failures.
  • 188 focused tests passed across 15 files, covering matching run identity, length-limited completion, unresolved and partial failures, shared client reconciliation, SSE refresh, delta cursors, historical page boundaries, concurrent appends, exact-message lookup, access restrictions, and raw transcript preservation.
  • The final full build and complete changed-file gate passed. The real Gateway/browser rerun showed one answer, no stale failure or duplicate badge, a successful follow-up, and clean history after reload. Independent review is clean through P2. Sanitized before/after captures show the result.

Recovery lookahead retains the existing message and byte limits; insufficient evidence preserves a failure. An opaque cursor cached before this fix and already past a failure is corrected by the next full history read.

The first CI run exposed an unrelated suppression-inventory line-number mismatch, already fixed on main by #139747. The branch was rebased onto that fix; git range-diff confirmed the reviewed patch is unchanged.

The PR review reported no actionable code findings but could not inspect the linked media. Both uploaded images are reachable: a one-byte ranged GET returns HTTP 206 for each. HEAD follows a GET-signed storage redirect and returns 403, so it is not a valid reachability check here. The sanitized originals were visually inspected before upload; the live run also asserted no stale failure or duplicate badge before and after reload.

Before: stale failure and duplicate answer

After: one answer and successful follow-up

@clawsweeper

clawsweeper Bot commented Sep 6, 2026 •

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

ClawSweeper review complete

ClawSweeper finished reviewing this revision. The review result is being finalized.

View the workflow run.

@steipete
steipete marked this pull request as ready for review September 6, 2026 04:46
@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation gateway Gateway runtime agents Agent runtime and tooling labels Sep 6, 2026
@steipete

steipete commented Sep 6, 2026

Copy link
Copy Markdown
Contributor Author

Before and after from the same isolated real-Gateway/browser scenario: a model/account rejection on the first request, a real fallback answer, then a real follow-up answer. The after run also passed reload checks. Raw transcript inspection confirmed one successful fallback answer and preserved the failed attempt for diagnostics.

Before:
Before: stale failure and duplicate answer

After:
After: one answer and successful follow-up

188 focused tests passed across 15 files, including history pagination, delta/SSE recovery, exact-message lookup, and access restrictions. Full build passed; independent review is clean through P2. Screenshots contain synthetic conversation data, with avatars removed.

@openclaw-barnacle openclaw-barnacle Bot added size: XL maintainer Maintainer-authored PR labels Sep 6, 2026
@steipete
steipete force-pushed the codex/fallback-history-projection branch from 6b8f933 to 4a2889e Compare September 6, 2026 04:52
@clawsweeper clawsweeper Bot added P2 Normal backlog priority with limited blast radius. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Sep 6, 2026
@clawsweeper

clawsweeper Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Codex review: blocked before merge. Reviewed September 6, 2026, 12:58 AM ET / 04:58 UTC.

ClawSweeper review

What this changes

The PR makes Gateway history hide empty failed attempts after a successful answer from the same run, keeping Control UI answers consistent across live updates, reloads, pagination, and message lookup.

Merge readiness

⛔ Blocked before merge - 1 item remains

Keep open: current main still retains empty provider failures after successful fallback, and the related merged refactor explicitly left this display defect unresolved. No blocking introduced defect was found.

Priority: P2
Reviewed head: 4a2889ea2b253be13fa3194342457ada37542e29

Review scores

Measure Result What it means
Overall readiness 🐚 platinum hermit (4/6) A coherent owner-level repair with focused boundary coverage and reported real-flow validation; no blocking correctness issue was found.
Proof confidence 🌊 off-meta tidepool Not applicable: The repository-state MEMBER association exempts this PR from the contributor proof gate. Supplied evidence maps Gateway projection recovery to a real Chromium fallback, follow-up, and reload run; linked captures were unavailable for independent visual inspection.
Patch quality 🐚 platinum hermit (4/6) No actionable review findings were identified.

Verification

Check Result Evidence
Real behavior Not applicable Not applicable: The repository-state MEMBER association exempts this PR from the contributor proof gate. Supplied evidence maps Gateway projection recovery to a real Chromium fallback, follow-up, and reload run; linked captures were unavailable for independent visual inspection.
Evidence reviewed 9 items Current main still needs the repair: Current main repairs only pure stream-error sentinels; empty provider failures subsequently receive failure placeholder text. The proposed same-run recovery behavior is absent.
Introduced recovery boundary: The projection requires a visible stop/length completion with matching run identity for attributed failures, excludes transcript bookkeeping as completion evidence, and preserves visible partial or structured output. This is an OpenClaw transcript-display contract; the patch does not depend on a Codex runtime contract.
Pagination and incremental consistency: Recovery lookahead uses message anchors, checks display-source continuity, and bounds retained context by messages and bytes. Pending errors prevent append-only delta cursors from advancing past rows that recovery may later remove.
Findings None None.
Security None None.

How this fits together

The Gateway projects stored transcript records into display history consumed by the Control UI and shared session reconciler. This projection controls which failed attempts remain visible without deleting diagnostic records.

flowchart TD
  A[Stored transcript attempts] --> B[Gateway display projection]
  B --> C{Successful answer from same run?}
  C -->|Yes| D[Hide empty failed attempts]
  C -->|No| E[Preserve failure display]
  D --> F[History pages and refreshes]
  E --> F
  F --> G[Control UI session reconciliation]
Loading

Before merge

  • Resolve merge risk (P1) - The linked before/after images were unreachable during review; the supplied real-run observations could be read, but the captures could not be independently inspected.
Agent review details

Security

None.

PR surface

Source +203, Tests +627, Docs +2, Other 0. Total +832 across 17 files.

View PR surface stats
Area Files Added Removed Net
Source 8 303 100 +203
Tests 7 633 6 +627
Docs 1 2 0 +2
Config 0 0 0 0
Generated 0 0 0 0
Other 1 1 1 0
Total 17 939 107 +832

Review metrics

Metric Value Why it matters
Production and regression coverage growth production net +203 lines; tests net +627 lines Production growth supports shared bounded recovery across history entrypoints; the larger test addition protects pagination, reconciliation, and diagnostic preservation.

Merge-risk options

Maintainer options:

  1. Decide the mitigation before merge
    Keep recovery in the shared display projection, preserve raw diagnostics and partial output, and reconcile removed placeholders through bounded authoritative history refreshes.
  2. Pause or close
    Do not merge this PR until maintainers decide whether the risk is worth taking.

Technical review

Best possible solution:

Keep recovery in the shared display projection, preserve raw diagnostics and partial output, and reconcile removed placeholders through bounded authoritative history refreshes.

Do we have a high-confidence way to reproduce the issue?

Yes, from source: a user message followed by an empty error attempt and a successful answer under the same run remains incorrectly visible on current main. The contributor also reports a real Gateway/browser reproduction; this review did not execute it.

Is this the best way to solve the issue?

Yes. Extending the existing projection owner addresses the stale row and reconciliation ambiguity while keeping diagnostic storage, provider routing, and durable answer identities unchanged.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning medium; reviewed against 883d4d65bad1.

Labels

Label changes:

  • add P2: Repairs misleading failure placeholders and duplicate answer display after an otherwise successful fallback, with a limited presentation-layer blast radius.
  • add rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🌊 off-meta tidepool and patch quality is 🐚 platinum hermit.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Not applicable: The repository-state MEMBER association exempts this PR from the contributor proof gate. Supplied evidence maps Gateway projection recovery to a real Chromium fallback, follow-up, and reload run; linked captures were unavailable for independent visual inspection.

Label justifications:

  • P2: Repairs misleading failure placeholders and duplicate answer display after an otherwise successful fallback, with a limited presentation-layer blast radius.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🌊 off-meta tidepool and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Not applicable: The repository-state MEMBER association exempts this PR from the contributor proof gate. Supplied evidence maps Gateway projection recovery to a real Chromium fallback, follow-up, and reload run; linked captures were unavailable for independent visual inspection.

Evidence

What I checked:

  • Current main still needs the repair: Current main repairs only pure stream-error sentinels; empty provider failures subsequently receive failure placeholder text. The proposed same-run recovery behavior is absent. (src/gateway/chat-display-projection.core.ts:234, 883d4d65bad1)
  • Introduced recovery boundary: The projection requires a visible stop/length completion with matching run identity for attributed failures, excludes transcript bookkeeping as completion evidence, and preserves visible partial or structured output. This is an OpenClaw transcript-display contract; the patch does not depend on a Codex runtime contract. (src/gateway/chat-display-projection.core.ts:256, 4a2889ea2b25)
  • Pagination and incremental consistency: Recovery lookahead uses message anchors, checks display-source continuity, and bounds retained context by messages and bytes. Pending errors prevent append-only delta cursors from advancing past rows that recovery may later remove. (src/gateway/session-history-tail.ts:68, 4a2889ea2b25)
  • Duplicate-answer mechanism: The existing shared client adopts a provisional answer only when its durable match is unambiguous. Leaving both failed and successful records under one run can produce multiple matches; removing the recovered empty failure resolves that ambiguity without changing durable message identity. (packages/gateway-client/src/session-projection.ts:386, 4a2889ea2b25)
  • Regression coverage inspected: Tests cover recovery outside page boundaries, concurrent appends during lookahead, exhausted byte budgets, exact-message visibility, cursor reset/resumption, raw-record preservation, and client reconciliation. The supplied body reports 188 passing focused tests and an original-source failure; this read-only review did not execute them. (src/gateway/server-methods/chat-history-pages.recovery.test.ts:84, 4a2889ea2b25)
  • Supplied real-flow evidence: The captured PR body and fix: clear stale failures and duplicate answers after model fallback #139753 (comment) describe an isolated real Gateway and Chromium run: injected initial model rejection, real fallback and follow-up responses, one displayed answer, no stale failure or duplicate badge, and clean reload. The linked before/after attachments could not be retrieved, so their visual contents were not independently inspected. (4a2889ea2b25)

Likely related people:

  • steipete: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)

Rating scale

Score Internal tier Crab rank Meaning
6/6 S 🦀 challenger crab Exceptional readiness
5/6 A 🦞 diamond lobster Very strong readiness
4/6 B 🐚 platinum hermit Good normal PR; ordinary maintainer review
3/6 C 🦐 gold shrimp Useful, but confidence is limited
2/6 D 🦪 silver shellfish Proof or implementation needs work
1/6 F 🧂 unranked krab Not merge-ready
N/A NA 🌊 off-meta tidepool Rating does not apply

Overall follows the weaker of proof and patch quality.
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

Workflow

  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@steipete
steipete merged commit f172f13 into main Sep 6, 2026
192 checks passed
@steipete
steipete deleted the codex/fallback-history-projection branch September 6, 2026 05:04
@steipete

steipete commented Sep 6, 2026

Copy link
Copy Markdown
Contributor Author

Merged via squash.

fuller-stack-dev added a commit that referenced this pull request Sep 6, 2026
Carry the existing #139792 repair and its merged #139715 test prerequisite.
The neutral projection state comes from the owning helper, so this fixture
works before and after the main-branch field rename in #139753.
No session-first implementation changes.

Co-authored-by: Peter Steinberger <steipete@gmail.com>
steipete added a commit that referenced this pull request Sep 6, 2026
#139753 renamed the projection result's streamErrorFallbackPending and
streamErrorFallbackRepaired to assistantErrorPending and
assistantErrorRecoveryObserved, but one call site in this test kept the old
names, leaving main red on check-test-types-core-2. Its targeted PR CI never
selected this shard, so the break first appeared on a full main run.
steipete added a commit that referenced this pull request Sep 6, 2026
Align the false-valued pagination fixture with the projection field names introduced in #139753 after #139715. Preserve all pagination assertions.

The Gateway-root typecheck reproduces TS2353 before the change and passes afterward. All 30 history tests pass before and after; the full changed-file gate and independent P2 review pass.
steipete added a commit that referenced this pull request Sep 6, 2026
…39766)

* fix(tooling): diagnose declaration escapes into ancestor installs

* fix(gateway): use current projection fields in session history test

#139753 renamed the projection result's streamErrorFallbackPending and
streamErrorFallbackRepaired to assistantErrorPending and
assistantErrorRecoveryObserved, but one call site in this test kept the old
names, leaving main red on check-test-types-core-2. Its targeted PR CI never
selected this shard, so the break first appeared on a full main run.
steipete added a commit that referenced this pull request Sep 6, 2026
…#139733)

* fix(test): align runtime E2E failures with continuation recovery

Use a missing-model response for selected-provider fallback proof. Preserve one same-model continuation before a provider-advertised outage exceeds the normal recovery window, while retaining exact HTTP 500 Telegram settlement and raw-error redaction.

Recognize provider timeouts from their diagnostic classification instead of comparing clocks with different start boundaries. Require terminal error evidence and retain exactly one successful queued followup.

Proof: five focused E2E tests pass, root test typecheck and scoped lint pass, and independent review is clean. The aggregate changed gate encounters the pre-existing services shard root-count limit (721 versus 720); no test roots are added.

* fix(test): update history projection fixture fields

Align the false-valued pagination fixture with the projection field names introduced in #139753 after #139715. Preserve all pagination assertions.

The Gateway-root typecheck reproduces TS2353 before the change and passes afterward. All 30 history tests pass before and after; the full changed-file gate and independent P2 review pass.
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Sep 7, 2026
…enclaw#139766)

* fix(tooling): diagnose declaration escapes into ancestor installs

* fix(gateway): use current projection fields in session history test

openclaw#139753 renamed the projection result's streamErrorFallbackPending and
streamErrorFallbackRepaired to assistantErrorPending and
assistantErrorRecoveryObserved, but one call site in this test kept the old
names, leaving main red on check-test-types-core-2. Its targeted PR CI never
selected this shard, so the break first appeared on a full main run.
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Sep 7, 2026
…openclaw#139733)

* fix(test): align runtime E2E failures with continuation recovery

Use a missing-model response for selected-provider fallback proof. Preserve one same-model continuation before a provider-advertised outage exceeds the normal recovery window, while retaining exact HTTP 500 Telegram settlement and raw-error redaction.

Recognize provider timeouts from their diagnostic classification instead of comparing clocks with different start boundaries. Require terminal error evidence and retain exactly one successful queued followup.

Proof: five focused E2E tests pass, root test typecheck and scoped lint pass, and independent review is clean. The aggregate changed gate encounters the pre-existing services shard root-count limit (721 versus 720); no test roots are added.

* fix(test): update history projection fixture fields

Align the false-valued pagination fixture with the projection field names introduced in openclaw#139753 after openclaw#139715. Preserve all pagination assertions.

The Gateway-root typecheck reproduces TS2353 before the change and passes afterward. All 30 history tests pass before and after; the full changed-file gate and independent P2 review pass.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling docs Improvements or additions to documentation gateway Gateway runtime maintainer Maintainer-authored PR P2 Normal backlog priority with limited blast radius. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: XL status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant