fix(gateway): never return an empty chat.history transcript#92383
Conversation
enforceChatHistoryFinalBudget returned an empty array when even a single oversized-message placeholder could not fit the chat-history byte budget (for example when the source message carried very large transcript metadata). An empty response renders as a blank transcript on the dashboard and reads to the operator as total history loss, even though the full transcript is intact on disk. Return a minimal, metadata-free sentinel message in that fallback instead, so the dashboard always renders at least a self-describing notice. Adds focused coverage for the budget tiers including the sentinel path.
|
Codex review: passed. Reviewed June 19, 2026, 12:15 AM ET / 04:15 UTC. Summary PR surface: Source +20, Tests +73. Total +93 across 2 files. Reproducibility: yes. Source inspection shows current main reaches Review metrics: none identified. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Next step before merge
Security Review detailsBest possible solution: Keep the shared helper-level sentinel and regression coverage so every Do we have a high-confidence way to reproduce the issue? Yes. Source inspection shows current main reaches Is this the best way to solve the issue? Yes. The shared final-budget helper is the narrowest maintainable boundary because Gateway WebSocket history, embedded TUI history, and the embedded Gateway stub all call it after the same replacement and byte-cap stages. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 78f948f76836. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +20, Tests +73. Total +93 across 2 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
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. How this review workflow works
|
|
/clownfish automerge |
|
Clownfish is on the reef for this PR. 🐠 I tagged A maintainer can call |
|
🦞✅ Source: What merged:
Automerge notes:
The automerge loop is complete. Automerge progress:
|
…#92383) Summary: - The PR changes gateway chat-history byte-budget fallback behavior to return a small metadata-free unavailable sentinel instead of an empty transcript, with focused budget tests. - PR surface: Source +20, Tests +73. Total +93 across 2 files. - Reproducibility: yes. Source inspection shows current main reaches `messages: []` when the full history, las ... d copied oversized placeholder all exceed `maxBytes`; I did not run tests because this review is read-only. Automerge notes: - PR branch already contained follow-up commit before automerge: test: access __openclaw via bracket notation for no-underscore-dangle Validation: - ClawSweeper review passed for head f2fa246. - Required merge gates passed before the squash merge. Prepared head SHA: f2fa246 Review: openclaw#92383 (comment) Co-authored-by: Hidetsugu55 <183473679+Hidetsugu55@users.noreply.github.com>
…#92383) Summary: - The PR changes gateway chat-history byte-budget fallback behavior to return a small metadata-free unavailable sentinel instead of an empty transcript, with focused budget tests. - PR surface: Source +20, Tests +73. Total +93 across 2 files. - Reproducibility: yes. Source inspection shows current main reaches `messages: []` when the full history, las ... d copied oversized placeholder all exceed `maxBytes`; I did not run tests because this review is read-only. Automerge notes: - PR branch already contained follow-up commit before automerge: test: access __openclaw via bracket notation for no-underscore-dangle Validation: - ClawSweeper review passed for head f2fa246. - Required merge gates passed before the squash merge. Prepared head SHA: f2fa246 Review: openclaw#92383 (comment) Co-authored-by: Hidetsugu55 <183473679+Hidetsugu55@users.noreply.github.com>
enforceChatHistoryFinalBudgetreturned an empty array when even a single oversized-message placeholder could not fit the chat-history byte budget — for example when the source transcript message carried very large metadata (a corrupted or huge__openclaw.id). An emptychat.historyresponse renders as a blank transcript on the dashboard, which reads to the operator as total history loss, even though the full transcript is intact on disk. This is one of the concrete causes behind "the whole chat history disappears".What changed
{ messages: [] }), return a minimal, metadata-free sentinel message instead. The dashboard now always renders at least a self-describing notice ([chat.history unavailable: transcript too large to display; the full history is preserved on disk]) rather than a blank pane.Tests
pnpm exec vitest run --config test/vitest/vitest.gateway.config.ts src/gateway/server-methods/chat-history-budget.test.ts— pass-through, last-message-only, per-message placeholder, and the new sentinel fallback (asserts the result is never empty).src/gateway/server.chat.gateway-server-chat-b.test.tsoversized-history coverage still passes../node_modules/.bin/oxfmt --checkon the changed files.Real behavior proof
Behavior or issue addressed: When a session transcript contained a message whose oversized-placeholder representation still exceeded the 6 MB chat-history budget,
enforceChatHistoryFinalBudgetreturned an empty array and the dashboard showed a completely blank transcript — indistinguishable from losing the whole conversation. The fix makes that fallback return a small sentinel notice so the history pane is never blank.Real environment tested: Ran the actual production chat-history budget pipeline from this branch on macOS (Darwin, Apple Silicon) via
node— the samereplaceOversizedChatHistoryMessagesandenforceChatHistoryFinalBudgetfunctions the gateway'schat.historyhandler calls — against a pathological transcript message carrying ~7.5 MB of metadata, at the production 6 MB budget.Exact steps or command run after this patch:
node --import tsx proof-empty-history.mts, which builds a real display message with a ~7.5 MB__openclaw.id, runs stage 1 (replaceOversizedChatHistoryMessages) and stage 2 (enforceChatHistoryFinalBudget) exactly aschat.historydoes, and prints the resulting transcript.Evidence after fix: Console output from running the production pipeline:
Observed result after fix: The pipeline returns a single sentinel message (
messages.length=1) carrying the self-describing notice and no oversized metadata. Before this patch the identical input producedmessages.length=0— a blank transcript on the dashboard. The on-disk transcript is untouched either way.What was not tested: This exercised the production budget functions directly via node rather than through a full websocket
chat.historyround-trip, because naturally producing a >6 MB single-message placeholder inside a live session is impractical; the surroundingchat.historywiring is covered by the existing gateway server-chat suite. No model or channel was involved.