Skip to content

fix(chat): stabilize replay and control events - #365

Merged
mira-2026 merged 4 commits into
mainfrom
mira/fix-chat-replay-stability
Aug 1, 2026
Merged

fix(chat): stabilize replay and control events#365
mira-2026 merged 4 commits into
mainfrom
mira/fix-chat-replay-stability

Conversation

@mira-2026

@mira-2026 mira-2026 commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • deliver task notifications with chat.inject followed by an immediate wake, without creating a synthetic human chat.send
  • keep control messages visible across live/history/reconnect flows while excluding them from replay retention and run/session identity
  • hydrate truncated Codex, OpenAI, and Synthetic history through bounded chat.message.get requests (four workers, 128-entry cache, 2M-character ceiling), including transcript-backed omitted images
  • stabilize response/thinking/tool/control row identity, commentary snapshots, provider tool-ID backfill, hydration cache invalidation, and attachment-modal scrolling

Behavior and regression coverage

  • notification injection completes before the wake request and targets the selected Gateway session
  • control events remain outside response runs, cannot displace retained replay content, and retain distinct provider identities even when their text matches
  • full-message hydration validates provider identity, preserves canonical identity and omitted images, refreshes when the preview changes, and falls back safely when unavailable
  • live-to-history settlement keeps stable row keys, preventing response/tool flashing and jumps
  • commentary snapshots replace while raw deltas append; the attachment modal owns exactly one scroll container

Verification

  • Repository lint: bun run lint
  • Repository formatting: bun run format:check
  • Frontend build/typecheck: bun run build:frontend
  • Frontend tests: full suite passed (680 tests, 4,410 expectations)
  • Frontend coverage collection was not rerun locally
  • Backend build/typecheck: bun run build:backend
  • Backend tests/coverage: full suite passed (719 tests, 4,612 expectations); the 85% coverage gate passed
  • Focused replay audit: frontend (323) and backend (160) tests passed; review-fix targets also passed, including the final PR-dev proxy assertion (2 tests, 39 expectations)
  • Manual UI/API smoke check: no live Gateway or production state was changed
  • git diff --check

Risk checklist

  • No secrets, tokens, .env files, database dumps, or runtime state committed
  • Gateway and runtime-ordering changes received an independent code/test audit
  • No new public API routes; existing route authentication and validation remain in place
  • No migrations or persistent application data-shape changes
  • Runtime/reconnect behavior preserves ordering, replay identity, recovery, and existing send idempotency
  • Visible UI changes are limited to stable chat/control rendering and single-container attachment scrolling

Deployment / operations

  • Standard Dashboard release/restart needed after merge
  • No config or secret changes needed
  • Rollback is a code revert; there are no migrations to unwind

Notes for reviewers

Please focus on inject-before-wake ordering, control retention boundaries, full-message identity/image hydration, and row identity during live-to-history settlement.

Verified against the installed and running OpenClaw v2026.7.1-2 (0790d9f): the public chat.inject schema exposes only sessionKey, optional agentId, message, and optional label, with additional properties disabled. Its installed validator rejects idempotencyKey. Dashboard therefore does not retry the combined inject-then-wake sequence after a partial failure; a wake failure is logged while the durable injected control remains visible.

Non-blocking coverage gaps: the inject/wake test uses a fake Gateway client; the four-worker hydration ceiling is not directly stress-tested; and the pre-ack runless-assistant row race has no dedicated regression test.

Native GitHub stack: #367 (bottom layer; #366 is stacked above this PR).

Dashboard task: #389

@mira-2026
mira-2026 requested a review from rajohan as a code owner August 1, 2026 05:51
@mira-2026 mira-2026 added priority: high High-priority work for the next active cycle status: needs-review Ready for human or agent review type: bugfix Fixes broken or incorrect behavior area: chat Chat UI, streaming, messages, attachments, or voice UX area: sessions OpenClaw session list, history, or session actions area: notifications Notification ingestion, bell UI, and alert maintenance area: openclaw OpenClaw gateway/API integration and runtime behavior area: frontend Frontend UI, client state, routing, and browser behavior area: backend Backend API, server routes, services, and integrations labels Aug 1, 2026
@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Session control notices now appear reliably without creating duplicate response runs.
    • Truncated chat history can be expanded to recover complete messages when available.
    • Assistant preamble updates are displayed as commentary with improved append and replacement behavior.
  • Bug Fixes

    • Improved chat continuity and message identity during interrupted-run recovery.
    • Prevented internal control events from appearing as synthetic retained runs.
    • Improved attachment preview scrolling with a single, consistent scroll area.

Walkthrough

The change adds canonical control and commentary events, separates their runtime state from response runs, hydrates truncated history messages through chat.message.get, and centralizes attachment preview scrolling.

Changes

Control and commentary events

Layer / File(s) Summary
Canonical control and commentary contracts
contracts/chat/*
OpenClaw injected messages now use control metadata. Preamble items now produce append or replace commentary events.
Gateway injection and retention
backend/src/chat/*, backend/src/gateway.ts, backend/src/routes/taskRoutes.ts
The gateway sends chat.inject followed by wake. Control events do not create run associations or retained synthetic runs.
Frontend control and commentary state
frontend/src/components/features/chat/domain/*, frontend/src/components/features/chat/useChatRuntime.ts, frontend/src/components/features/chat/chatTypes.ts
Runtime state stores bounded controls and commentary. Projection, reconciliation, replay, and settlement process both event types.

History message hydration

Layer / File(s) Summary
Full-message history contracts
contracts/chatCanonicalHistory.ts, contracts/chat/openClawHistoryPageAdapter.ts
History rows include message identity and truncation metadata. Full-message responses support validated success and unavailable results.
Gateway retrieval and frontend hydration
backend/src/gateway.ts, frontend/src/components/features/chat/transport/*
The loader fetches truncated messages with bounded concurrency, caching, size limits, identity validation, and fallback handling. Gateway policy and integration tests cover chat.message.get.

Attachment preview scrolling

Layer / File(s) Summary
Parent-owned preview scrolling
frontend/src/components/features/chat/*, frontend/src/components/features/files/viewers/*, frontend/src/components/ui/Modal.tsx
Attachment dialogs assign scrolling to one content container. JSON and Markdown previews can delegate scrolling to their parent.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant TaskRoutes
  participant Gateway
  participant OpenClaw
  participant ChatRuntime
  TaskRoutes->>Gateway: sendSessionControlEvent(sessionKey, message)
  Gateway->>OpenClaw: chat.inject
  Gateway->>OpenClaw: wake
  OpenClaw-->>ChatRuntime: canonical control event
  ChatRuntime->>ChatRuntime: store session control and settle selected session
Loading

Possibly related PRs

Suggested labels: area: files, type: tests

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely summarizes the main replay and control-event stabilization changes.
Description check ✅ Passed The description includes all required sections, concrete behavior, verification results, risks, deployment details, and known coverage gaps.

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.

@codecov

codecov Bot commented Aug 1, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.61371% with 41 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...d/src/components/features/chat/domain/chatState.ts 88.65% 22 Missing ⚠️
.../components/features/chat/domain/chatProjection.ts 92.50% 12 Missing ⚠️
...end/src/components/features/chat/useChatRuntime.ts 81.08% 7 Missing ⚠️

📢 Thoughts on this report? Let us know!

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7b3014b03c

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread backend/src/gateway.ts
Comment thread frontend/src/components/features/chat/domain/chatProjection.ts
Comment thread frontend/src/components/features/chat/transport/openClawHistoryLoader.ts Outdated

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 2

🧹 Nitpick comments (4)
contracts/chatCanonicalHistory.ts (1)

36-47: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Use v.variant instead of v.union for this discriminated union.

canonicalChatHistoryMessageResultSchema is a textbook discriminated union on the ok field (v.literal(true) vs v.literal(false)). Valibot's own documentation recommends variant over union for this exact case.

For better performance, more type safety, and a more targeted output of issues, you can use variant for discriminated unions... we recommend using variant over union whenever possible.

♻️ Proposed refactor
-export const canonicalChatHistoryMessageResultSchema = v.union([
-    v.strictObject({
+export const canonicalChatHistoryMessageResultSchema = v.variant("ok", [
+    v.strictObject({
         message: canonicalChatHistoryRowSchema,
         ok: v.literal(true),
         schemaVersion: v.literal(CANONICAL_CHAT_HISTORY_SCHEMA_VERSION),
     }),
     v.strictObject({
         ok: v.literal(false),
         schemaVersion: v.literal(CANONICAL_CHAT_HISTORY_SCHEMA_VERSION),
         unavailableReason: v.picklist(["not_found", "not_visible", "oversized"]),
     }),
 ]);

Please confirm v.variant is available and behaves as documented in valibot 1.4.2, as declared for this project.

🤖 Prompt for 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.

In `@contracts/chatCanonicalHistory.ts` around lines 36 - 47, Update
canonicalChatHistoryMessageResultSchema to use Valibot’s v.variant
discriminated-union API keyed by the ok field, preserving both strictObject
branches, their literals, and unavailableReason validation. Confirm the
project’s Valibot 1.4.2 API supports this usage.
frontend/src/components/features/chat/transport/openClawHistoryLoader.ts (1)

375-409: 🚀 Performance & Scalability | 🔵 Trivial | 🏗️ Heavy lift

Hydration fully blocks history() before any messages are returned.

#loadFresh (Line 527) and #load (Lines 586-588) both await this.#hydrateRows(...) before returning. #hydrateRows (Lines 375-409) processes every truncated row through a 4-worker pool, one chat.message.get round trip per row. Since #pagesUntil walks pages until hasMore is false rather than stopping at limit, this hydration step can cover the full session transcript, not just a page.

For a session with many truncated rows (large tool outputs, long file reads), the caller of history() waits for all of them to hydrate sequentially in batches of 4 before the transcript renders at all, on every fresh load and on every reconnect (since reset() clears #fullMessageCache). Consider returning the bounded previews immediately and patching in hydrated content as it resolves, so a handful of large truncated messages cannot delay the entire transcript from appearing.

Please confirm how many truncated rows a typical long-running session accumulates, since that determines whether this is a real-world latency risk or a rare edge case.

Also applies to: 518-535, 586-588

🤖 Prompt for 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.

In `@frontend/src/components/features/chat/transport/openClawHistoryLoader.ts`
around lines 375 - 409, Change `#loadFresh` and `#load` so history() returns bounded
preview rows without awaiting full `#hydrateRows` completion. Refactor
`#hydrateRows` to hydrate truncated rows asynchronously and patch each resolved
message into the displayed/cached history through the existing update path,
preserving ordering and avoiding stale updates after reset or reconnect. Ensure
hydration failures do not block or replace the initial preview response, and
verify typical long-running sessions’ truncated-row counts to validate the
latency impact.
contracts/chat/openClawHistoryPageAdapter.ts (1)

34-36: 🗄️ Data Integrity & Integration | 🔵 Trivial | 🏗️ Heavy lift

Confirm the OpenClaw truncation wording is part of the stable contract.

isLightweightPreview depends on exact ...(truncated)... suffix or [chat.history omitted: message too large] equality. If OpenClaw changes wording, normalizedText.trimEnd() may still normalize the text enough that these exact matches fail, leaving truncated unset and preventing full-message hydration because OpenClawHistoryLoader.#fullMessage() only requests full content when preview.truncated is truthy.

🤖 Prompt for 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.

In `@contracts/chat/openClawHistoryPageAdapter.ts` around lines 34 - 36, Ensure
the truncation detection in isLightweightPreview does not depend solely on exact
OpenClaw suffix or placeholder wording, so wording changes still set
preview.truncated and allow OpenClawHistoryLoader.#fullMessage() to hydrate full
content. Preserve recognition of the existing CHAT_HISTORY_TRUNCATION_SUFFIX and
CHAT_HISTORY_OVERSIZED_PLACEHOLDER values while adding robust detection for
equivalent truncation markers.
frontend/src/components/features/chat/domain/chatState.ts (1)

763-783: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Clarify the tautological name comparison at line 780.

call.name === (result.name || call.name) always evaluates to true when result.name is falsy, because the right side then becomes call.name. This is logically equivalent to !result.name || call.name === result.name, which intentionally falls back to an unconditional match when result.name is absent, relying on uniqueMatchingDiagnosticIndex to keep the match safe when only one candidate qualifies.

Write the condition in its equivalent explicit form. This removes the "compares a value to itself" pattern that a future maintainer could mistake for a bug and "fix," which would silently break the single-candidate name-fallback path.

✏️ Proposed clarification
             index = uniqueMatchingDiagnosticIndex(
                 diagnostics,
                 (entry) =>
                     entry.message.toolCalls?.some(
                         (call) =>
                             !call.id &&
                             !call.toolResult &&
-                            call.name === (result.name || call.name)
+                            (!result.name || call.name === result.name)
                     ) === true
             );
🤖 Prompt for 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.

In `@frontend/src/components/features/chat/domain/chatState.ts` around lines 763 -
783, In the result-matching predicate inside uniqueMatchingDiagnosticIndex,
replace the tautological call.name === (result.name || call.name) expression
with its explicit equivalent: match when result.name is absent or call.name
equals result.name. Preserve the existing !call.id and !call.toolResult
constraints.
🤖 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 `@backend/src/gateway.ts`:
- Around line 1690-1699: Update sendSessionControlEvent so both chat.inject and
wake use the same stable notification idempotency key for a single control
event, or use the protocol’s supported idempotent chat.inject mechanism. Ensure
retries after wake failure do not create duplicate durable control notices,
while preserving the existing request order and timeout behavior.

In `@contracts/chat/openClawHistoryPageAdapter.ts`:
- Around line 216-254: Update canonicalizeOpenClawHistoryMessageResult to
validate the raw message identity before calling canonicalHistoryRow: read the
id from message.__openclaw and require it to match the trimmed
options.messageId. Reject missing or mismatched ids through the existing
invalid/unavailable error path so the response cannot be accepted under the
requested identity.

---

Nitpick comments:
In `@contracts/chat/openClawHistoryPageAdapter.ts`:
- Around line 34-36: Ensure the truncation detection in isLightweightPreview
does not depend solely on exact OpenClaw suffix or placeholder wording, so
wording changes still set preview.truncated and allow
OpenClawHistoryLoader.#fullMessage() to hydrate full content. Preserve
recognition of the existing CHAT_HISTORY_TRUNCATION_SUFFIX and
CHAT_HISTORY_OVERSIZED_PLACEHOLDER values while adding robust detection for
equivalent truncation markers.

In `@contracts/chatCanonicalHistory.ts`:
- Around line 36-47: Update canonicalChatHistoryMessageResultSchema to use
Valibot’s v.variant discriminated-union API keyed by the ok field, preserving
both strictObject branches, their literals, and unavailableReason validation.
Confirm the project’s Valibot 1.4.2 API supports this usage.

In `@frontend/src/components/features/chat/domain/chatState.ts`:
- Around line 763-783: In the result-matching predicate inside
uniqueMatchingDiagnosticIndex, replace the tautological call.name ===
(result.name || call.name) expression with its explicit equivalent: match when
result.name is absent or call.name equals result.name. Preserve the existing
!call.id and !call.toolResult constraints.

In `@frontend/src/components/features/chat/transport/openClawHistoryLoader.ts`:
- Around line 375-409: Change `#loadFresh` and `#load` so history() returns bounded
preview rows without awaiting full `#hydrateRows` completion. Refactor
`#hydrateRows` to hydrate truncated rows asynchronously and patch each resolved
message into the displayed/cached history through the existing update path,
preserving ordering and avoiding stale updates after reset or reconnect. Ensure
hydration failures do not block or replace the initial preview response, and
verify typical long-running sessions’ truncated-row counts to validate the
latency impact.
🪄 Autofix (Beta)

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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7a416527-347b-471a-827d-fcf39e3a2e38

📥 Commits

Reviewing files that changed from the base of the PR and between 30fca6d and 7b3014b.

📒 Files selected for processing (39)
  • backend/src/chat/openClawChatBridge.ts
  • backend/src/chat/openClawChatRetention.ts
  • backend/src/gateway.ts
  • backend/src/routes/taskRoutes.ts
  • backend/test/gatewayBehavior.test.ts
  • backend/test/openClawChatBridge.test.ts
  • backend/test/routeAndServiceBehavior.test.ts
  • backend/test/serviceBehavior.test.ts
  • contracts/chat/openClawAdapterValues.ts
  • contracts/chat/openClawHistoryNormalizer.ts
  • contracts/chat/openClawHistoryPageAdapter.ts
  • contracts/chat/openClawRuntimeAdapter.ts
  • contracts/chat/openClawToolAdapter.ts
  • contracts/chatCanonical.ts
  • contracts/chatCanonicalHistory.ts
  • contracts/chatCanonicalTurn.ts
  • frontend/src/components/features/chat/AttachmentPreviewModal.tsx
  • frontend/src/components/features/chat/ChatAttachmentPickerModal.tsx
  • frontend/src/components/features/chat/ChatMessagesList.tsx
  • frontend/src/components/features/chat/chatTypes.ts
  • frontend/src/components/features/chat/domain/chatCanonicalProjection.ts
  • frontend/src/components/features/chat/domain/chatPresentation.ts
  • frontend/src/components/features/chat/domain/chatProjection.ts
  • frontend/src/components/features/chat/domain/chatState.ts
  • frontend/src/components/features/chat/transport/openClawHistoryLoader.ts
  • frontend/src/components/features/chat/transport/useOpenClawChatTransport.ts
  • frontend/src/components/features/chat/useChatRuntime.ts
  • frontend/src/components/features/files/viewers/JsonPreview.tsx
  • frontend/src/components/features/files/viewers/MarkdownPreview.tsx
  • frontend/src/components/ui/Modal.tsx
  • frontend/src/test/chatCanonicalHistory.test.ts
  • frontend/src/test/chatCanonicalProjection.test.ts
  • frontend/src/test/chatProjection.test.ts
  • frontend/src/test/chatRuntimeController.test.tsx
  • frontend/src/test/chatState.test.ts
  • frontend/src/test/componentBehavior.test.tsx
  • frontend/src/test/openClawAdapterVariants.test.ts
  • frontend/src/test/openClawHistoryLoader.test.ts
  • frontend/src/test/support/canonicalChatHistory.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
  • GitHub Check: backend-checks
  • GitHub Check: Analyze JavaScript and TypeScript
🔇 Additional comments (41)
frontend/src/components/features/chat/AttachmentPreviewModal.tsx (1)

71-95: LGTM!

Also applies to: 196-197, 227-241, 258-258

frontend/src/components/features/chat/ChatAttachmentPickerModal.tsx (1)

175-193: LGTM!

frontend/src/components/features/files/viewers/JsonPreview.tsx (1)

25-35: LGTM!

frontend/src/components/features/files/viewers/MarkdownPreview.tsx (1)

8-8: LGTM!

Also applies to: 22-27

frontend/src/components/ui/Modal.tsx (1)

15-15: LGTM!

Also applies to: 36-36, 82-90

frontend/src/test/componentBehavior.test.tsx (1)

549-560: LGTM!

Also applies to: 1270-1275

contracts/chat/openClawHistoryPageAdapter.ts (1)

54-73: LGTM!

contracts/chatCanonicalHistory.ts (1)

16-22: LGTM!

Also applies to: 69-81

frontend/src/components/features/chat/transport/openClawHistoryLoader.ts (1)

260-286: LGTM!

Also applies to: 320-373

frontend/src/components/features/chat/transport/useOpenClawChatTransport.ts (1)

29-32: LGTM!

frontend/src/test/openClawHistoryLoader.test.ts (1)

6-31: LGTM!

Also applies to: 64-118

frontend/src/test/support/canonicalChatHistory.ts (1)

1-6: LGTM!

Also applies to: 50-62

frontend/src/test/chatCanonicalHistory.test.ts (1)

16-94: LGTM!

contracts/chat/openClawAdapterValues.ts (1)

187-208: LGTM!

contracts/chat/openClawHistoryNormalizer.ts (1)

49-63: LGTM!

Also applies to: 428-428, 446-456

contracts/chat/openClawRuntimeAdapter.ts (1)

15-15: LGTM!

Also applies to: 118-133, 234-235, 362-395, 424-426, 528-531, 586-593

contracts/chat/openClawToolAdapter.ts (1)

18-18: LGTM!

Also applies to: 240-244

contracts/chatCanonical.ts (1)

77-80: LGTM!

Also applies to: 129-139

backend/src/chat/openClawChatBridge.ts (1)

1336-1343: LGTM!

backend/src/gateway.ts (1)

5-8: LGTM!

Also applies to: 1341-1351, 1784-1784

frontend/src/test/openClawAdapterVariants.test.ts (1)

917-966: LGTM!

Also applies to: 1026-1061

backend/test/serviceBehavior.test.ts (1)

6460-6462: 🎯 Functional Correctness

No change needed.

The enclosing test callback is already async, and the un-awaited chained .rejects matcher is valid in this test file; awaiting it is optional for this assertion.

contracts/chatCanonicalTurn.ts (1)

30-30: LGTM!

backend/src/routes/taskRoutes.ts (1)

323-328: LGTM!

frontend/src/components/features/chat/ChatMessagesList.tsx (1)

545-545: LGTM!

frontend/src/components/features/chat/chatTypes.ts (1)

624-628: LGTM!

frontend/src/components/features/chat/domain/chatCanonicalProjection.ts (1)

143-148: LGTM!

frontend/src/components/features/chat/domain/chatPresentation.ts (1)

73-74: LGTM!

Also applies to: 346-352

frontend/src/components/features/chat/domain/chatProjection.ts (3)

126-146: LGTM!

Also applies to: 233-262


1413-1439: LGTM!

Also applies to: 1768-1830, 1842-1854, 1902-1902, 1929-1933


2150-2161: LGTM!

Also applies to: 2180-2198

frontend/src/test/chatCanonicalProjection.test.ts (1)

650-650: LGTM!

Also applies to: 960-960, 1204-1241

backend/src/chat/openClawChatRetention.ts (1)

83-88: LGTM!

backend/test/gatewayBehavior.test.ts (1)

623-653: LGTM!

backend/test/openClawChatBridge.test.ts (1)

3546-3621: LGTM!

backend/test/routeAndServiceBehavior.test.ts (1)

944-947: LGTM!

Also applies to: 964-964

frontend/src/components/features/chat/domain/chatState.ts (1)

30-31: LGTM!

Also applies to: 44-44, 66-66, 110-118, 165-166, 312-312, 352-352, 649-669, 683-699, 910-985, 1027-1058, 1115-1115, 1274-1303, 1350-1352, 1388-1390, 1417-1437, 1614-1614

frontend/src/components/features/chat/useChatRuntime.ts (1)

95-95: LGTM!

Also applies to: 124-162, 286-291, 292-315, 317-562

frontend/src/test/chatProjection.test.ts (1)

947-1194: LGTM!

Also applies to: 2567-2573

frontend/src/test/chatRuntimeController.test.tsx (1)

105-143: LGTM!

frontend/src/test/chatState.test.ts (1)

61-155: LGTM!

Comment thread backend/src/gateway.ts
Comment thread contracts/chat/openClawHistoryPageAdapter.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a0e86690fa

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 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 `@backend/test/pullRequestPreviewGatewayProxy.test.ts`:
- Around line 470-480: Await the promise returned by socket.next() in the
allowed-full-message assertion before applying toMatchObject, ensuring the test
waits for the proxy response and validates the forwarding result.
🪄 Autofix (Beta)

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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 0a737ede-1e7c-45a0-9892-0507212e905e

📥 Commits

Reviewing files that changed from the base of the PR and between 7b3014b and 27ecb98.

📒 Files selected for processing (14)
  • backend/src/development/developmentGatewayPolicy.ts
  • backend/src/gateway.ts
  • backend/test/bunNativeServerBehavior.test.ts
  • backend/test/gatewayBehavior.test.ts
  • backend/test/pullRequestPreviewGatewayProxy.test.ts
  • backend/test/utilityBehavior.test.ts
  • contracts/chat/openClawHistoryPageAdapter.ts
  • contracts/chatCanonicalHistory.ts
  • frontend/src/components/features/chat/chatUtilities.ts
  • frontend/src/components/features/chat/domain/chatState.ts
  • frontend/src/components/features/chat/transport/openClawHistoryLoader.ts
  • frontend/src/test/chatCanonicalHistory.test.ts
  • frontend/src/test/chatProjection.test.ts
  • frontend/src/test/openClawHistoryLoader.test.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • contracts/chat/openClawHistoryPageAdapter.ts
  • backend/src/gateway.ts
  • frontend/src/test/openClawHistoryLoader.test.ts
  • contracts/chatCanonicalHistory.ts
  • frontend/src/components/features/chat/transport/openClawHistoryLoader.ts
  • frontend/src/components/features/chat/domain/chatState.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
  • GitHub Check: Analyze JavaScript and TypeScript
  • GitHub Check: backend-checks
  • GitHub Check: frontend-checks
🔇 Additional comments (8)
backend/src/development/developmentGatewayPolicy.ts (1)

3-3: LGTM!

Also applies to: 23-23

backend/test/bunNativeServerBehavior.test.ts (1)

493-507: LGTM!

backend/test/gatewayBehavior.test.ts (1)

223-244: LGTM!

Also applies to: 645-675, 1510-1592

backend/test/pullRequestPreviewGatewayProxy.test.ts (1)

526-526: LGTM!

Also applies to: 549-549, 565-565

backend/test/utilityBehavior.test.ts (1)

674-674: LGTM!

Also applies to: 695-703

frontend/src/test/chatProjection.test.ts (1)

83-92: LGTM!

Also applies to: 957-1016, 1018-1076, 1078-1149, 1151-1231, 2604-2610

frontend/src/components/features/chat/chatUtilities.ts (1)

741-747: 🎯 Functional Correctness

No change needed.

applyControlEvent assigns a session-unique runtimeKey (control:${controlId || event.id || event.sequence}) when controlId is absent, so runtime controls keep distinct messageIdentity keys and are not deduplicated as the same text-only control row.

frontend/src/test/chatCanonicalHistory.test.ts (1)

96-112: LGTM!

Also applies to: 114-135

Comment thread backend/test/pullRequestPreviewGatewayProxy.test.ts Outdated
@mira-2026
mira-2026 merged commit d32705a into main Aug 1, 2026
7 checks passed
@mira-2026
mira-2026 deleted the mira/fix-chat-replay-stability branch August 1, 2026 18:58
mira-2026 added a commit that referenced this pull request Aug 1, 2026
## Summary

- split the agents, cache refresh, Docker updater, log rotation, and
pull-request services into explicit source, producer, policy,
persistence, runtime, and scheduler seams while preserving their public
entry modules
- extract Delivery, Docker, Jobs, Task detail, ChatComposer, and
chat-projection controllers/panels/models from large frontend units
- close the remaining P2 seams with per-producer cache modules, separate
Docker update policy/notifications, a session-file source, and
post-stability chat projection identity/diagnostics

This PR is the top layer of native GitHub stack #367 and is based on
#365 (`b5a51638`). Its diff contains only the decomposition work. `git
range-diff` reports all ten decomposition commits as patch-equivalent
after the final rebase.

## Behavior and regression coverage

- public imports and route/service contracts are preserved; this is an
internal restructuring with no intended API or product behavior change
- cache scheduling, serial execution, and producer registration now have
explicit boundaries
- Docker discovery, registry polling, update policy, compose
transactions, notifications, persistence, and scheduling are
independently reviewable
- the chat projection seams were re-extracted from the verified
replay-stability implementation so stable control/response/tool
identities are retained

## Verification

- [x] Repository lint: `bun run lint`
- [x] Repository formatting: `bun run format:check`
- [x] Frontend build/typecheck: `bun run build:frontend`
- [x] Frontend tests/coverage: full suite passed (**680 tests**, 4,413
expectations); the 85% coverage gate passed on head `08eea037`
- [x] Backend build/typecheck: `bun run build:backend`
- [x] Backend tests/coverage: full suite passed (**719 tests**, 4,612
expectations); the 85% coverage gate passed on head `08eea037`
- [x] Focused regression tests: projection integration (**265**) and
backend service seams (**208**) passed before the patch-equivalent final
rebase
- [x] Rebase audit: all ten commits match their pre-rebase patches via
`git range-diff`
- [x] Native stack audit: stack #367 is `main ← #365 b5a5163#366
08eea03`, with `needsRebase: false` for both layers
- [ ] Manual UI/API smoke check: not run; no live state was changed
- [x] `git diff --check`

## Risk checklist

- [x] No secrets, tokens, `.env` files, database dumps, runtime state,
lockfiles, or configuration changes committed
- [x] Gateway, Docker, filesystem, scheduling, and notification
boundaries were reviewed carefully
- [x] No new or changed public API routes
- [x] No migrations or persisted data-shape changes
- [x] Runtime and scheduling behavior remains behind the original public
entry points and characterization tests
- [x] Frontend changes are boundary extractions with no intended visual
change, so screenshots are not applicable

## Deployment / operations

- [x] Standard Dashboard release/restart needed after the stack merges
- [x] No config or secret changes needed
- [x] Rollback is commit/PR reversion; there are no migrations to unwind

## Notes for reviewers

Reviewing commit-by-commit follows the original seam batches: log
rotation, agents, cache refresh, Docker updater, Delivery/pull requests,
frontend controllers, and chat projection.

Large cohesive internals such as the security-sensitive log-rotation
core and GitHub client were deliberately retained rather than split
solely by line count.

GitHub Dashboard/CodeQL workflows are filtered to `main`-based pull
requests, so this stacked PR relies on the full local verification above
until its base advances to `main`.

Dashboard task: #388
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: backend Backend API, server routes, services, and integrations area: chat Chat UI, streaming, messages, attachments, or voice UX area: frontend Frontend UI, client state, routing, and browser behavior area: notifications Notification ingestion, bell UI, and alert maintenance area: openclaw OpenClaw gateway/API integration and runtime behavior area: sessions OpenClaw session list, history, or session actions priority: high High-priority work for the next active cycle status: needs-review Ready for human or agent review type: bugfix Fixes broken or incorrect behavior

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants