Skip to content

Feat/chat summary compact phase - #1525

Merged
qdaxb merged 12 commits into
wecode-ai:mainfrom
kissghosts:feat/chat-summary-compact-phase
Jun 23, 2026
Merged

qdaxb merged 12 commits into
wecode-ai:mainfrom
kissghosts:feat/chat-summary-compact-phase

Conversation

@kissghosts

@kissghosts kissghosts commented Jun 23, 2026 •

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • Added summary-based context compaction to automatically rewrite conversation history to fit token budgets.
    • Introduced a “compacting context” indicator plus custom waiting/processing messaging during compaction.
    • Added termination_reason support to chat status/completion payloads (reflected in UI/state).
  • Bug Fixes

    • Improved AI-trigger failure handling to persist failed outcomes and surface consistent error information.
    • Finalized turn handling now properly resolves “unexecuted tool-call preview” blocks into error blocks when appropriate.
  • Improvements

    • Tool output guard is now enabled by default (settings toggle removed).

@coderabbitai

coderabbitai Bot commented Jun 23, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR introduces LLM-based summary compaction as a primary context-reduction strategy, propagates termination_reason and context_compaction events through the full backend-to-frontend stack, promotes the tool output guard to always-on (removing the user toggle), adds frontend compaction status indicators, fixes unresolved tool-call preview block finalization at session end, and deduplicates thinking blocks in done-state merging.

Changes

Summary Compaction, Termination Reason & Compaction UI

Layer / File(s) Summary
Data contracts, configuration, and shared defaults
chat_shell/chat_shell/compression/config.py, chat_shell/chat_shell/compression/context_metrics.py, chat_shell/chat_shell/core/config.py, backend/app/core/config.py, shared/models/responses_api.py, shared/models/responses_api_emitter.py, shared/models/execution.py, shared/models/openai_converter.py, frontend/src/types/socket.ts, frontend/src/types/api.ts, chat_shell/chat_shell/services/streaming/core.py
ModelContextConfig gains reserved_output_tokens, output_tokens_cap_enabled, auto_compact_token_limit, and updated trigger_limit/target_limit properties; new ProviderUsageBaseline and ContextCompactionEvent types; ResponsesAPIEventBuilder.status_updated accepts context_compaction; enable_tool_output_guard defaults to True; tool_output_guard_enabled removed from UserPreferences; SubtaskResult gains termination_reason; TOOL_OUTPUT_GUARD_ENABLED removed from backend Settings; StreamingState gains context_compactions and termination_reason.
SummaryCompactor: LLM-based history compaction
chat_shell/chat_shell/compression/summary_compactor.py
New SummaryCompactor class with iterative oldest-history removal, LLM invocation, tool message sanitization (orphan removal and unresolved tool-call filtering), recent-user message token-budget selection, message truncation, and replacement history assembly with compaction metadata flags; raises SummaryCompactNotApplicable when compact budget is too small or LLM returns empty summary.
Context metrics: provider usage baseline and delta estimation
chat_shell/chat_shell/compression/context_metrics.py, chat_shell/tests/test_context_metrics.py
calculate_context_metrics accepts optional usage_baseline and routes token counting through _estimate_used_input_tokens; ContextMetricsTracker gains record_provider_usage, invalidate_provider_usage_baseline, usage_baseline property, and emit_status method for emitting unthrottled status updates with context_compaction payloads; capture() invalidates baseline on message prefix mismatch.
UnifiedContextGuard: summary compaction pass and token-based trigger
chat_shell/chat_shell/guard/context_guard.py, chat_shell/tests/test_context_guard.py
UnifiedContextGuard accepts optional summary_compactor; _evaluate runs _apply_summary_compaction_pass first when over trigger (token-estimated), falls back to legacy MessageCompressor only if compaction produced no updates or view remains over trigger, and conditionally annotates compaction record with used_legacy_fallback; adds trigger_limit, target_limit, context_compactions public accessors and _dict_to_state_message helper for message-dict conversion; over-trigger/fail-fast calculations switch to token-estimate heuristic.
LangGraphAgentBuilder: on_model_usage callback and termination classification
chat_shell/chat_shell/agent.py, chat_shell/chat_shell/agents/graph_builder.py, chat_shell/tests/test_graph_builder_usage_metrics.py
AgentConfig gains on_model_usage callback; LangGraphAgentBuilder stores callback and invokes it asynchronously on on_chat_model_end with extracted provider input tokens and normalized model input messages; tracks _last_termination_reason with structured classification at stream completion; GraphRecursionError recovery path uses new _build_limit_recovery_messages_chain to construct persisted message chain when on_chain_end was not emitted; adds helpers for model-input extraction, token extraction, and callback invocation with exception swallowing.
Streaming pipeline: state, emitters, and context compaction event propagation
chat_shell/chat_shell/services/streaming/core.py, backend/app/services/execution/emitters/status_updating.py, backend/app/services/execution/emitters/websocket.py, backend/app/services/execution/inprocess_executor.py, backend/app/services/execution/dispatcher.py, backend/app/services/chat/webpage_ws_chat_emitter.py
StreamingState gains context_compactions and termination_reason with conditional inclusion in get_current_result(); StatusUpdatingEmitter terminal handlers return Optional[Dict] and update event.result with finalized result before forwarding; WebSocketResultEmitter extracts and normalizes context_compaction from event data and forwards to emit_chat_status_updated; extract_completed_result returns termination_reason and context_compactions fields.
Session block finalization and termination reason
backend/app/services/chat/storage/session.py, backend/app/services/chat/trigger/lifecycle.py, backend/tests/services/chat/storage/test_session_blocks.py
finalize_and_get_blocks accepts kw-only termination_reason parameter; new _finalize_unresolved_preview_tool_blocks rewrites preview-only tool blocks lacking tool_output into ERROR blocks with appropriate unresolved-preview messages keyed by termination reason; collect_completed_result derives and passes termination_reason into finalization; new async tests verify preview finalization behavior including message selection based on termination_reason.
Failed AI trigger persistence
backend/app/api/ws/chat_namespace.py, backend/tests/api/ws/test_chat_namespace_task_join.py
New _finalize_failed_ai_trigger helper collects completed result for a FAILED subtask and persists it; the on_chat_send exception handler classifies the error into error_code, calls _finalize_failed_ai_trigger, and emits CHAT_ERROR with error_code type instead of just calling format_error_message(e); on_task_join includes cached status_updated in the join acknowledgement for streaming recovery.
Chat service wiring and KnowledgeBaseTool
chat_shell/chat_shell/services/chat_service.py, chat_shell/chat_shell/tools/builtin/knowledge_base.py
_process_chat conditionally builds SummaryCompactor when message compression and summary compaction are both enabled, wires it into UnifiedContextGuard, assigns context_metrics_tracker.record_provider_usage to on_model_usage, and persists _last_termination_reason and context_guard.context_compactions into streaming state after streaming; KnowledgeBaseTool._get_reserved_output_tokens returns reserved_output_tokens from updated ModelContextConfig with optional override.
Frontend: isCompacting state, waitingMessage prop chain, and toolbar indicator
frontend/src/features/tasks/hooks/useChatStatusIndicator.ts, frontend/src/features/tasks/components/chat/ChatArea.tsx, frontend/src/features/tasks/components/input/ChatToolbarStatus.tsx, frontend/src/features/tasks/components/message/MessagesArea.tsx, frontend/src/features/tasks/components/message/MessageBubble.tsx, frontend/src/features/tasks/components/message/thinking/MixedContentView.tsx, frontend/src/i18n/locales/..., frontend/src/__tests__/...
useChatStatusIndicator tracks compactingTaskIds per task and returns isCompacting boolean driven by context_compaction.status socket events (started sets, completed/fallback clears); ChatArea derives localized compactingWaitMessage when compacting and passes to MessagesArea as waitingMessage; MessagesArea, MessageBubble, and MixedContentView propagate waitingMessage/processingMessage through component tree; ChatToolbarStatus renders compacting spinner row with label beneath status title when active, adds tooltip wrappers, and simplifies icon styling; i18n adds chat_status.compacting and chat_status.tooltip strings to both en and zh-CN locales.
Tool output guard promotion and settings UI removal
backend/app/services/execution/request_builder.py, frontend/src/features/settings/components/NotificationSettings.tsx, backend/tests/services/execution/test_request_builder_user_mcp.py
_is_tool_output_guard_enabled now unconditionally returns True instead of consulting settings.TOOL_OUTPUT_GUARD_ENABLED or user preferences; NotificationSettings removes toolOutputGuardEnabled state variable, preferences-sync effect, toggle handler, and rendered UI section entirely; tests updated to verify guard is always True regardless of missing/invalid user preferences.
Model context configuration token budgeting overhaul
chat_shell/chat_shell/compression/config.py, chat_shell/tests/test_compression.py
Adds module-level RESERVED_OUTPUT_* constants for flat-buffer budgeting ratio/bounds; ModelContextConfig.reserved_output_tokens computes a fixed output reserve (context_window ratio + min/max clamp, optionally capped by model's max_output_tokens); available_tokens = context_window - reserved_output_tokens; trigger_limit and target_limit now use auto_compact_token_limit override when set (clamped to available), with new _apply_auto_compact_limit helper; updated get_model_context_config applies auto-compact override consistently across CRD/built-in/fallback configs.
Tests
backend/tests/..., chat_shell/tests/..., frontend/src/__tests__/...
Comprehensive test coverage: SummaryCompactor retry-on-context-length, message sanitization, truncation, and compaction-not-applicable; context guard summary-first flow and usage-baseline caching; context metrics provider baseline delta estimation and auto-compact clamping; ModelContextConfig reserved token bounds and capping logic; agent telemetry helpers for message extraction and token counting; streaming state and emitter payload fields; session block preview finalization; StatusUpdatingEmitter result forwarding; WebSocket emitter context_compaction handling; extract_completed_result termination_reason preservation; request builder always-on tool guard; task state thinking-block deduplication; useChatStatusIndicator isCompacting transitions; ChatArea and toolbar waiting/compacting message rendering; failed AI trigger persistence; socket context status caching and replay.

Thinking Block Deduplication Fix

Layer / File(s) Summary
hasDuplicateBlockContent fix and test
packages/chat-core/src/task-state/TaskStateMachine.blockMerging.ts, packages/chat-core/src/task-state/TaskStateMachine.test.ts
Replaces hasDuplicateTextContent with hasDuplicateBlockContent to extend merge-time deduplication to thinking blocks in addition to text blocks; adds a test confirming handleChatDone replay of backend blocks does not duplicate an already-completed inline thinking block.

Sequence Diagram(s)

sequenceDiagram
  participant ChatService
  participant UnifiedContextGuard
  participant SummaryCompactor
  participant LLM
  participant ContextMetricsTracker
  participant Socket
  participant StreamingState
  participant Emitter

  ChatService->>UnifiedContextGuard: __call__(state, view)
  UnifiedContextGuard->>UnifiedContextGuard: _current_used_input_tokens >= trigger_limit?
  UnifiedContextGuard->>ContextMetricsTracker: emit_status(phase=started, context_compaction={status:started})
  Socket-->>Socket: chat:status_updated {context_compaction:{status:started}}
  UnifiedContextGuard->>SummaryCompactor: compact(messages, preserve_initial_context)
  SummaryCompactor->>LLM: ainvoke(system+prior+compact prompt)
  LLM-->>SummaryCompactor: summary_text
  SummaryCompactor-->>UnifiedContextGuard: SummaryCompactResult
  UnifiedContextGuard->>ContextMetricsTracker: emit_status(phase=completed, context_compaction={status:completed})
  Socket-->>Socket: chat:status_updated {context_compaction:{status:completed}}
  ChatService->>StreamingState: state.context_compactions = guard.context_compactions
  ChatService->>StreamingState: state.termination_reason = agent._last_termination_reason
  StreamingState->>Emitter: finalize() → done(context_compactions, termination_reason)
Loading
sequenceDiagram
  participant Socket
  participant useChatStatusIndicator
  participant ChatArea
  participant MessagesArea
  participant MessageBubble

  Socket->>useChatStatusIndicator: onChatStatusUpdated(context_compaction={status:started})
  useChatStatusIndicator->>useChatStatusIndicator: compactingTaskIds[taskId]=true
  ChatArea->>useChatStatusIndicator: isCompacting=true
  ChatArea->>ChatArea: compactingWaitMessage=t(chat_status.compacting)
  ChatArea->>MessagesArea: waitingMessage=compactingWaitMessage
  MessagesArea->>MessageBubble: waitingMessage
  MessageBubble->>MessageBubble: StreamingWaitIndicator(message=waitingMessage)
  Socket->>useChatStatusIndicator: onChatChunk/onChatMessage/onChatDone
  useChatStatusIndicator->>useChatStatusIndicator: clearCompacting(taskId)
  ChatArea->>MessagesArea: waitingMessage=undefined
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

  • wecode-ai/Wegent#1415: Both PRs modify collect_completed_result in backend/app/services/chat/trigger/lifecycle.py—this one adds termination_reason-aware finalization and failure persistence; that one normalizes final_result["value"] from text blocks.
  • wecode-ai/Wegent#1466: Both PRs update ChatToolbarStatus.tsx compact-mode rendering—this one adds the compaction-state spinner row and tooltip; that one suppresses the standalone loading spinner.
  • wecode-ai/Wegent#1324: Both PRs modify on_task_join in backend/app/api/ws/chat_namespace.py to cache and propagate chat:status_updated snapshots for socket streaming recovery.
  • wecode-ai/Wegent#1502: Related through shared on_task_join flow modifications for task state reconstruction and streaming.

Suggested reviewers

  • qdaxb
  • sunnights

🐇 Hops through context windows wide,
Summaries trimmed so thoughts can glide,
"Compacting context…" blinks the screen,
While rabbits keep the history clean!
Tool guards ON by default now stay—
Less config, more hop-along today! 🌿

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 53.99% 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 'Feat/chat summary compact phase' is directly and specifically related to the main changeset, which implements a new summary-compact compaction phase for chat context management across the backend, frontend, and shared models.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed due to a network error.


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: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/app/services/execution/emitters/websocket.py (1)

266-274: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Persist context_compaction in the cached status snapshot as well.

_emit_status_updated() emits context_compaction live, but the cached snapshot omits it. Any reconnect/recovery flow using cached status can lose compacting state.

🐛 Proposed fix
         await session_manager.save_context_metrics(
             event.subtask_id,
             {
                 "task_id": event.task_id,
                 "subtask_id": event.subtask_id,
                 "phase": phase,
                 "context_metrics": context_metrics,
+                "context_compaction": (
+                    context_compaction
+                    if isinstance(context_compaction, dict)
+                    else None
+                ),
             },
         )
🤖 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 `@backend/app/services/execution/emitters/websocket.py` around lines 266 - 274,
The context_compaction state is being emitted live but is not included in the
cached status snapshot saved by session_manager.save_context_metrics(). Add
context_compaction to the dictionary argument passed to
session_manager.save_context_metrics() alongside the existing fields like
task_id, subtask_id, phase, and context_metrics to ensure the compacting state
persists in the cache for recovery flows.
🧹 Nitpick comments (11)
chat_shell/tests/test_context_guard.py (1)

1036-1042: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert compaction strategy type alongside status in runtime-event checks.

These assertions only validate status; adding type == "summary_compact" tightens the contract and avoids false positives if other compaction event types are emitted later.

Suggested test tightening
-        compaction_statuses = [
-            call.kwargs["context_compaction"]["status"]
+        compaction_events = [
+            call.kwargs["context_compaction"]
             for call in emitter.status_updated.await_args_list
             if call.kwargs.get("context_compaction") is not None
         ]
-        assert compaction_statuses == ["started", "completed"]
+        assert [event["type"] for event in compaction_events] == [
+            "summary_compact",
+            "summary_compact",
+        ]
+        assert [event["status"] for event in compaction_events] == [
+            "started",
+            "completed",
+        ]
-        compaction_statuses = [
-            call.kwargs["context_compaction"]["status"]
+        compaction_events = [
+            call.kwargs["context_compaction"]
             for call in emitter.status_updated.await_args_list
             if call.kwargs.get("context_compaction") is not None
         ]
-        assert compaction_statuses == ["started", "fallback"]
+        assert [event["type"] for event in compaction_events] == [
+            "summary_compact",
+            "summary_compact",
+        ]
+        assert [event["status"] for event in compaction_events] == [
+            "started",
+            "fallback",
+        ]

Also applies to: 1069-1074

🤖 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 `@chat_shell/tests/test_context_guard.py` around lines 1036 - 1042, The
compaction status assertions only validate the status field but should also
verify that the compaction type is "summary_compact" to avoid false positives
from other compaction event types. Add an additional filter condition in the
list comprehension that extracts compaction_statuses to check that
call.kwargs["context_compaction"]["type"] == "summary_compact", and apply this
same tightening to the second similar assertion block mentioned in the comment
(around lines 1069-1074) to ensure both test validations check both the type and
status fields.
chat_shell/tests/test_truncation_detection.py (1)

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

Complete the new test signature/docs for guideline compliance.

test_get_current_result_includes_context_compactions is missing an explicit -> None annotation and a docstring. As per coding guidelines, **/*.py must "Use type hints for all function parameters and return values" and "include docstrings for all public functions and classes".

🤖 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 `@chat_shell/tests/test_truncation_detection.py` at line 104, The function
test_get_current_result_includes_context_compactions is missing required type
hints and documentation. Add an explicit `-> None` return type annotation to the
function signature and include a docstring that describes what the test
verifies. This ensures compliance with the coding guidelines that require type
hints for all function return values and docstrings for all public functions.

Source: Coding guidelines

chat_shell/tests/test_session.py (1)

142-144: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a return type annotation to the new async test.

test_finalize_includes_messages_chain_and_context_compactions should declare -> None for consistency with the Python typing rule. As per coding guidelines, **/*.py must "Use type hints for all function parameters and return values".

🤖 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 `@chat_shell/tests/test_session.py` around lines 142 - 144, Add a return type
annotation of `-> None` to the
`test_finalize_includes_messages_chain_and_context_compactions` method signature
to comply with the Python typing requirement that all function parameters and
return values must have type hints. This should be added after the method
parameters and before the colon in the method definition.

Source: Coding guidelines

chat_shell/tests/test_knowledge_injection_strategy_clean.py (1)

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

Add an explicit return type annotation on the new test method.

test_reserved_output_tokens_align_with_context_governance_budget should use -> None for guideline compliance and consistency with typed test signatures. As per coding guidelines, **/*.py must "Use type hints for all function parameters and return values".

🤖 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 `@chat_shell/tests/test_knowledge_injection_strategy_clean.py` at line 261, The
test method `test_reserved_output_tokens_align_with_context_governance_budget`
is missing an explicit return type annotation. Add `-> None` to the method
signature after the closing parenthesis of the method parameters to comply with
the coding guideline that requires type hints for all function return values.

Source: Coding guidelines

chat_shell/tests/test_new_messages_from_state.py (1)

242-243: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Annotate the new test methods with -> None.

Both newly added test methods in TestLimitRecoveryMessagesChain are missing explicit return type annotations. As per coding guidelines, **/*.py must "Use type hints for all function parameters and return values".

Also applies to: 257-258

🤖 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 `@chat_shell/tests/test_new_messages_from_state.py` around lines 242 - 243, Add
explicit return type annotations to the two newly added test methods in the
TestLimitRecoveryMessagesChain class. Both
test_falls_back_to_limit_notice_when_collected_state_missing and the test method
around line 257-258 are missing `-> None` return type annotations in their
method signatures. Update each method definition to include `-> None` after the
parameter list and before the colon to comply with the coding guidelines
requiring type hints for all function return values.

Source: Coding guidelines

backend/tests/services/chat/storage/test_session_blocks.py (1)

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

Add missing docstrings and -> None annotations to the new async tests.

The four new async test functions are missing explicit return type annotations and docstrings; aligning these keeps test style consistent with repo Python standards. As per coding guidelines, **/*.py must "Use type hints for all function parameters and return values" and "include docstrings for all public functions and classes".

Also applies to: 308-308, 336-336, 388-388

🤖 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 `@backend/tests/services/chat/storage/test_session_blocks.py` at line 270, The
four new async test functions at lines 270, 308, 336, and 388 are missing both
return type annotations and docstrings. Add `-> None` return type annotation to
each of these async test function signatures
(test_finalize_and_get_blocks_marks_unresolved_preview_tool_blocks_error, and
the three other async test functions) and add descriptive docstrings to each
function body that explain what the test is validating, following the existing
docstring style used in the test file for consistency with Python coding
standards.

Source: Coding guidelines

chat_shell/tests/test_serialize_messages_chain.py (1)

208-209: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add -> None return annotations for the newly added tests.

test_summary_compacted_markers_are_persisted and test_detects_summary_compacted_live_state should be explicitly annotated. As per coding guidelines, **/*.py must "Use type hints for all function parameters and return values".

Also applies to: 231-232

🤖 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 `@chat_shell/tests/test_serialize_messages_chain.py` around lines 208 - 209,
Add `-> None` return type annotations to the two test methods
`test_summary_compacted_markers_are_persisted` and
`test_detects_summary_compacted_live_state` in the test class. Both methods are
missing explicit return type hints which are required by the coding guidelines
for all functions. Update the method definitions to include `-> None` after the
parameter list and before the colon.

Source: Coding guidelines

chat_shell/chat_shell/compression/summary_compactor.py (1)

148-203: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Trace the new compaction model-call path.

compact() / _generate_summary() are the new LLM-backed rewrite path; add the shared async tracing decorator or span events so compaction latency and failures are correlated with the turn. As per coding guidelines, “Use @trace_async or @trace_sync decorators from shared.telemetry.decorators for tracing async and sync functions respectively; use add_span_event() and set_span_attribute() for adding details to spans.”

🤖 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 `@chat_shell/chat_shell/compression/summary_compactor.py` around lines 148 -
203, The async methods compact() and _generate_summary() lack tracing
instrumentation for the new LLM-backed rewrite path. Import the `@trace_async`
decorator from shared.telemetry.decorators and apply it to both the compact()
method and _generate_summary() method to enable tracing of compaction latency
and failures. If additional context is needed, use add_span_event() and
set_span_attribute() from the telemetry module to annotate the spans with
relevant details.

Source: Coding guidelines

chat_shell/chat_shell/guard/context_guard.py (1)

707-827: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Split and trace the summary-compaction stage.

_apply_summary_compaction_pass now handles event construction, metrics snapshots, fallback records, rewrite generation, and logging in one ~120-line function. Extract small helpers for event/record/update construction and add tracing around the async stage. As per coding guidelines, “Functions should not exceed 50 lines of code (preferred maximum)” and “Use @trace_async or @trace_sync decorators from shared.telemetry.decorators for tracing async and sync functions respectively.”

🤖 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 `@chat_shell/chat_shell/guard/context_guard.py` around lines 707 - 827, The
`_apply_summary_compaction_pass` function exceeds 120 lines and combines
multiple responsibilities including event/record construction, metrics
snapshots, fallback handling, and logging. Refactor by extracting helper
functions for distinct tasks: one for constructing the initial
ContextCompactionEvent when the compaction starts, one for handling the
exception case (determining failure_reason, appending to _context_compactions,
and emitting fallback status), one for constructing the replacement_view and
updates from the successful result, and one for constructing the final
ContextCompactionEvent and logging when compaction completes successfully. Keep
each helper function under 50 lines and add the `@trace_async` decorator to the
main `_apply_summary_compaction_pass` function and `@trace_sync` decorators to the
helper functions for proper tracing as per coding guidelines.

Source: Coding guidelines

chat_shell/chat_shell/agents/graph_builder.py (1)

295-311: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add tracing decorator to the new async usage-callback helper.

_invoke_model_usage_callback() is a new async execution path but currently untraced, which weakens observability for provider-usage baseline failures.

♻️ Proposed change
+@trace_async(
+    span_name="agent_builder.invoke_model_usage_callback",
+    tracer_name="chat_shell.agents",
+)
 async def _invoke_model_usage_callback(
     callback: Callable[..., Any],
     messages: list[dict[str, Any]],
     *,
     input_tokens: int,
 ) -> None:

As per coding guidelines, "**/*.py: ... Use @trace_async or @trace_sync decorators from shared.telemetry.decorators for tracing async and sync functions respectively ...".

🤖 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 `@chat_shell/chat_shell/agents/graph_builder.py` around lines 295 - 311, The
`_invoke_model_usage_callback()` async function is missing the required tracing
decorator for observability. Add the `@trace_async` decorator from
shared.telemetry.decorators to the top of the `_invoke_model_usage_callback()`
function definition to enable tracing of this async execution path and improve
observability for provider-usage baseline failures.

Source: Coding guidelines

backend/app/api/ws/chat_namespace.py (1)

91-97: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add tracing to this new async failure-finalization path.

This helper persists terminal failed state for background AI triggers, but it is not decorated, so this failure path will be missing its own span.

As per coding guidelines, “Use @trace_async or @trace_sync decorators from shared.telemetry.decorators for tracing async and sync functions respectively; use add_span_event() and set_span_attribute() for adding details to spans.”

🤖 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 `@backend/app/api/ws/chat_namespace.py` around lines 91 - 97, The
_finalize_failed_ai_trigger async function is missing a tracing decorator which
means this failure-finalization path will not generate its own span. Add the
`@trace_async` decorator from shared.telemetry.decorators above the function
definition to enable tracing for this background AI trigger failure path,
following the established coding guidelines for async functions.

Source: Coding guidelines

🤖 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/app/services/chat/trigger/lifecycle.py`:
- Around line 421-433: The termination_reason is correctly normalized from
runtime_result with a fallback to existing_result before being passed to
finalize_and_get_blocks, but when final_result is built later in the code, it
likely uses the unnormalized runtime_result value instead of the normalized
termination_reason variable. To fix this inconsistency, ensure that the
final_result dictionary is constructed using the normalized termination_reason
variable that was already computed and used for finalize_and_get_blocks, so that
the persisted result contains the same valid termination_reason value as the
finalized blocks.

In `@chat_shell/chat_shell/compression/config.py`:
- Around line 82-87: The target_limit method currently returns min(base_limit,
self.trigger_limit) when auto_compact_token_limit is not None, which can result
in the target equaling the trigger_limit. Since the metrics check uses >=
trigger_limit to determine an over-trigger condition, a successful compaction
landing on its target would immediately re-trigger compaction. Fix this by
ensuring the returned value is strictly less than self.trigger_limit when
applying the auto_compact_token_limit constraint, such as subtracting 1 or using
a similar adjustment to keep the target below the trigger threshold.

In `@chat_shell/chat_shell/compression/summary_compactor.py`:
- Around line 196-203: The `_generate_summary` method does not validate whether
the LLM response is empty, which would result in losing conversation history by
returning a blank summary. After calling `_extract_text(result).strip()`, add a
validation check to ensure the returned summary string is not empty. If it is
empty, raise `SummaryCompactNotApplicable` instead of returning the empty
string, so the system falls back to appropriate error handling instead of
silently replacing history with blank content.

In `@chat_shell/chat_shell/guard/context_guard.py`:
- Around line 1011-1012: The _is_over_trigger method uses a strict greater-than
comparison (>) while calculate_context_metrics uses a greater-than-or-equal-to
comparison (>=) for the trigger boundary, causing inconsistency at exactly the
trigger limit. Change the comparison operator in the return statement of
_is_over_trigger from > to >= so that both methods consistently trigger at the
same boundary condition when used_input_tokens reaches or exceeds trigger_limit.
- Around line 780-789: Add a new static helper method _ensure_message_ids to the
class that iterates through a list of messages and assigns a UUID string ID to
any message that lacks an ID attribute. Then, before the existing code that
creates the replacement_view and builds the removal updates list, call this
helper on both original_messages and result.replacement_history to ensure all
messages have stable IDs assigned. This ensures that RemoveMessage operations
are created for all original messages and that summary_message_id is properly
captured with a valid ID.

In `@chat_shell/chat_shell/services/chat_service.py`:
- Around line 320-323: The condition that checks
request.enable_tool_output_guard allows stale user preferences to override the
always-on tool guard requirement, meaning users who previously disabled this
setting will continue to bypass tool-output compaction. Remove the
request.enable_tool_output_guard check from this conditional statement so that
only the deployment-level kill switch settings.DISABLE_TOOL_OUTPUT_GUARD gates
whether the tool output guard runs, or alternatively remove the preference read
entirely from the upstream TaskRequestBuilder._is_tool_output_guard_enabled()
method to prevent stale settings from being propagated to this decision point.

In `@frontend/src/__tests__/features/tasks/hooks/useChatStatusIndicator.test.tsx`:
- Around line 16-22: The registeredHandlers object contains duplicate property
declarations for each handler (onChatChunk, onChatMessage, onBlockCreated,
onBlockUpdated, onChatDone, onChatError, and onChatCancelled), which causes
TypeScript duplicate property errors. Remove all duplicate handler declarations
in the registeredHandlers object, keeping only a single declaration for each
handler with the appropriate parameter types for task_id (either optional or
required as needed for each handler).

In `@frontend/src/features/tasks/components/input/ChatToolbarStatus.tsx`:
- Around line 271-332: The icon-only status trigger buttons at lines 277 and 304
lack accessible names, causing screen readers to announce them as unnamed
controls. Add an aria-label attribute to both Button components that contain
only the Coins icon (the first one with variant="ghost" size="icon" and the
second one with variant="ghost" and gap-1.5 className) to provide screen readers
with descriptive button names, using the same tooltip translation key
common:chat_status.tooltip or similar accessible description.

---

Outside diff comments:
In `@backend/app/services/execution/emitters/websocket.py`:
- Around line 266-274: The context_compaction state is being emitted live but is
not included in the cached status snapshot saved by
session_manager.save_context_metrics(). Add context_compaction to the dictionary
argument passed to session_manager.save_context_metrics() alongside the existing
fields like task_id, subtask_id, phase, and context_metrics to ensure the
compacting state persists in the cache for recovery flows.

---

Nitpick comments:
In `@backend/app/api/ws/chat_namespace.py`:
- Around line 91-97: The _finalize_failed_ai_trigger async function is missing a
tracing decorator which means this failure-finalization path will not generate
its own span. Add the `@trace_async` decorator from shared.telemetry.decorators
above the function definition to enable tracing for this background AI trigger
failure path, following the established coding guidelines for async functions.

In `@backend/tests/services/chat/storage/test_session_blocks.py`:
- Line 270: The four new async test functions at lines 270, 308, 336, and 388
are missing both return type annotations and docstrings. Add `-> None` return
type annotation to each of these async test function signatures
(test_finalize_and_get_blocks_marks_unresolved_preview_tool_blocks_error, and
the three other async test functions) and add descriptive docstrings to each
function body that explain what the test is validating, following the existing
docstring style used in the test file for consistency with Python coding
standards.

In `@chat_shell/chat_shell/agents/graph_builder.py`:
- Around line 295-311: The `_invoke_model_usage_callback()` async function is
missing the required tracing decorator for observability. Add the `@trace_async`
decorator from shared.telemetry.decorators to the top of the
`_invoke_model_usage_callback()` function definition to enable tracing of this
async execution path and improve observability for provider-usage baseline
failures.

In `@chat_shell/chat_shell/compression/summary_compactor.py`:
- Around line 148-203: The async methods compact() and _generate_summary() lack
tracing instrumentation for the new LLM-backed rewrite path. Import the
`@trace_async` decorator from shared.telemetry.decorators and apply it to both the
compact() method and _generate_summary() method to enable tracing of compaction
latency and failures. If additional context is needed, use add_span_event() and
set_span_attribute() from the telemetry module to annotate the spans with
relevant details.

In `@chat_shell/chat_shell/guard/context_guard.py`:
- Around line 707-827: The `_apply_summary_compaction_pass` function exceeds 120
lines and combines multiple responsibilities including event/record
construction, metrics snapshots, fallback handling, and logging. Refactor by
extracting helper functions for distinct tasks: one for constructing the initial
ContextCompactionEvent when the compaction starts, one for handling the
exception case (determining failure_reason, appending to _context_compactions,
and emitting fallback status), one for constructing the replacement_view and
updates from the successful result, and one for constructing the final
ContextCompactionEvent and logging when compaction completes successfully. Keep
each helper function under 50 lines and add the `@trace_async` decorator to the
main `_apply_summary_compaction_pass` function and `@trace_sync` decorators to the
helper functions for proper tracing as per coding guidelines.

In `@chat_shell/tests/test_context_guard.py`:
- Around line 1036-1042: The compaction status assertions only validate the
status field but should also verify that the compaction type is
"summary_compact" to avoid false positives from other compaction event types.
Add an additional filter condition in the list comprehension that extracts
compaction_statuses to check that call.kwargs["context_compaction"]["type"] ==
"summary_compact", and apply this same tightening to the second similar
assertion block mentioned in the comment (around lines 1069-1074) to ensure both
test validations check both the type and status fields.

In `@chat_shell/tests/test_knowledge_injection_strategy_clean.py`:
- Line 261: The test method
`test_reserved_output_tokens_align_with_context_governance_budget` is missing an
explicit return type annotation. Add `-> None` to the method signature after the
closing parenthesis of the method parameters to comply with the coding guideline
that requires type hints for all function return values.

In `@chat_shell/tests/test_new_messages_from_state.py`:
- Around line 242-243: Add explicit return type annotations to the two newly
added test methods in the TestLimitRecoveryMessagesChain class. Both
test_falls_back_to_limit_notice_when_collected_state_missing and the test method
around line 257-258 are missing `-> None` return type annotations in their
method signatures. Update each method definition to include `-> None` after the
parameter list and before the colon to comply with the coding guidelines
requiring type hints for all function return values.

In `@chat_shell/tests/test_serialize_messages_chain.py`:
- Around line 208-209: Add `-> None` return type annotations to the two test
methods `test_summary_compacted_markers_are_persisted` and
`test_detects_summary_compacted_live_state` in the test class. Both methods are
missing explicit return type hints which are required by the coding guidelines
for all functions. Update the method definitions to include `-> None` after the
parameter list and before the colon.

In `@chat_shell/tests/test_session.py`:
- Around line 142-144: Add a return type annotation of `-> None` to the
`test_finalize_includes_messages_chain_and_context_compactions` method signature
to comply with the Python typing requirement that all function parameters and
return values must have type hints. This should be added after the method
parameters and before the colon in the method definition.

In `@chat_shell/tests/test_truncation_detection.py`:
- Line 104: The function test_get_current_result_includes_context_compactions is
missing required type hints and documentation. Add an explicit `-> None` return
type annotation to the function signature and include a docstring that describes
what the test verifies. This ensures compliance with the coding guidelines that
require type hints for all function return values and docstrings for all public
functions.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: b62dfbdc-0940-4add-8724-8f465b342685

📥 Commits

Reviewing files that changed from the base of the PR and between fd355dc and 282a2d4.

📒 Files selected for processing (60)
  • backend/app/api/ws/chat_namespace.py
  • backend/app/core/config.py
  • backend/app/services/chat/storage/session.py
  • backend/app/services/chat/trigger/lifecycle.py
  • backend/app/services/chat/webpage_ws_chat_emitter.py
  • backend/app/services/execution/dispatcher.py
  • backend/app/services/execution/emitters/status_updating.py
  • backend/app/services/execution/emitters/websocket.py
  • backend/app/services/execution/inprocess_executor.py
  • backend/app/services/execution/request_builder.py
  • backend/tests/api/ws/test_chat_namespace_task_join.py
  • backend/tests/core/test_config.py
  • backend/tests/services/chat/storage/test_session_blocks.py
  • backend/tests/services/execution/test_emitters.py
  • backend/tests/services/execution/test_extract_completed_result.py
  • backend/tests/services/execution/test_request_builder_user_mcp.py
  • backend/tests/services/execution/test_responses_api_event_parser.py
  • backend/tests/services/execution/test_status_updating_emitter.py
  • chat_shell/chat_shell/agent.py
  • chat_shell/chat_shell/agents/graph_builder.py
  • chat_shell/chat_shell/compression/config.py
  • chat_shell/chat_shell/compression/context_metrics.py
  • chat_shell/chat_shell/compression/summary_compactor.py
  • chat_shell/chat_shell/core/config.py
  • chat_shell/chat_shell/guard/context_guard.py
  • chat_shell/chat_shell/services/chat_service.py
  • chat_shell/chat_shell/services/streaming/core.py
  • chat_shell/chat_shell/tools/builtin/knowledge_base.py
  • chat_shell/tests/test_compression.py
  • chat_shell/tests/test_context_guard.py
  • chat_shell/tests/test_context_metrics.py
  • chat_shell/tests/test_graph_builder_usage_metrics.py
  • chat_shell/tests/test_knowledge_injection_strategy.py
  • chat_shell/tests/test_knowledge_injection_strategy_clean.py
  • chat_shell/tests/test_new_messages_from_state.py
  • chat_shell/tests/test_serialize_messages_chain.py
  • chat_shell/tests/test_session.py
  • chat_shell/tests/test_summary_compactor.py
  • chat_shell/tests/test_truncation_detection.py
  • frontend/src/__tests__/features/tasks/components/chat/ChatArea.pipeline-next-step.test.tsx
  • frontend/src/__tests__/features/tasks/components/chat/ChatArea.queue-message-handler.test.tsx
  • frontend/src/__tests__/features/tasks/components/input/ChatToolbarStatus.test.tsx
  • frontend/src/__tests__/features/tasks/hooks/useChatStatusIndicator.test.tsx
  • frontend/src/features/settings/components/NotificationSettings.tsx
  • frontend/src/features/tasks/components/chat/ChatArea.tsx
  • frontend/src/features/tasks/components/input/ChatToolbarStatus.tsx
  • frontend/src/features/tasks/components/message/MessageBubble.tsx
  • frontend/src/features/tasks/components/message/MessagesArea.tsx
  • frontend/src/features/tasks/components/message/thinking/MixedContentView.tsx
  • frontend/src/features/tasks/hooks/useChatStatusIndicator.ts
  • frontend/src/i18n/locales/en/common.json
  • frontend/src/i18n/locales/zh-CN/common.json
  • frontend/src/types/api.ts
  • frontend/src/types/socket.ts
  • packages/chat-core/src/task-state/TaskStateMachine.blockMerging.ts
  • packages/chat-core/src/task-state/TaskStateMachine.test.ts
  • shared/models/execution.py
  • shared/models/openai_converter.py
  • shared/models/responses_api.py
  • shared/models/responses_api_emitter.py
💤 Files with no reviewable changes (3)
  • backend/tests/core/test_config.py
  • backend/app/core/config.py
  • frontend/src/features/settings/components/NotificationSettings.tsx

Comment thread backend/app/services/chat/trigger/lifecycle.py
Comment thread chat_shell/chat_shell/compression/config.py Outdated
Comment thread chat_shell/chat_shell/compression/summary_compactor.py Outdated
Comment thread chat_shell/chat_shell/guard/context_guard.py
Comment thread chat_shell/chat_shell/guard/context_guard.py Outdated
Comment thread chat_shell/chat_shell/services/chat_service.py Outdated
Comment thread frontend/src/features/tasks/components/input/ChatToolbarStatus.tsx

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

🧹 Nitpick comments (1)
chat_shell/chat_shell/services/chat_service.py (1)

349-352: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the summary compaction ratio into a named constant.

0.5 in the new recent_user_token_limit calculation is a magic number; promote it to a module-level constant so tuning stays explicit and centralized.

♻️ Proposed change
+SUMMARY_RECENT_USER_TARGET_RATIO = 0.5
+
 ...
                 summary_compactor = SummaryCompactor(
                     llm=summary_llm,
                     token_counter=guard_counter,
                     recent_user_token_limit=min(
                         DEFAULT_RECENT_USER_TOKEN_LIMIT,
-                        max(1, int(context_config.target_limit * 0.5)),
+                        max(
+                            1,
+                            int(
+                                context_config.target_limit
+                                * SUMMARY_RECENT_USER_TARGET_RATIO
+                            ),
+                        ),
                     ),
                     max_compact_input_tokens=context_config.available_tokens,
                 )

As per coding guidelines, "Extract magic numbers to named constants".

🤖 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 `@chat_shell/chat_shell/services/chat_service.py` around lines 349 - 352, The
magic number 0.5 used in the recent_user_token_limit calculation needs to be
extracted to a module-level constant. Define a new constant at the top of the
chat_service.py module with a descriptive name that reflects its purpose as the
summary compaction ratio, then replace the hardcoded 0.5 value in the
recent_user_token_limit calculation with this named constant to make the tuning
explicit and centralized.

Source: Coding guidelines

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

Nitpick comments:
In `@chat_shell/chat_shell/services/chat_service.py`:
- Around line 349-352: The magic number 0.5 used in the recent_user_token_limit
calculation needs to be extracted to a module-level constant. Define a new
constant at the top of the chat_service.py module with a descriptive name that
reflects its purpose as the summary compaction ratio, then replace the hardcoded
0.5 value in the recent_user_token_limit calculation with this named constant to
make the tuning explicit and centralized.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f36a6669-d27c-4c6d-9818-a7546d58d1bb

📥 Commits

Reviewing files that changed from the base of the PR and between 282a2d4 and e7d3fd0.

📒 Files selected for processing (15)
  • backend/app/services/chat/trigger/lifecycle.py
  • backend/app/services/execution/emitters/websocket.py
  • backend/app/services/execution/request_builder.py
  • backend/tests/api/ws/test_chat_namespace_task_join.py
  • backend/tests/services/chat/trigger/test_lifecycle_completed_result.py
  • backend/tests/services/execution/test_emitters.py
  • backend/tests/services/execution/test_request_builder_user_mcp.py
  • chat_shell/chat_shell/compression/config.py
  • chat_shell/chat_shell/compression/summary_compactor.py
  • chat_shell/chat_shell/guard/context_guard.py
  • chat_shell/chat_shell/services/chat_service.py
  • chat_shell/tests/test_compression.py
  • chat_shell/tests/test_context_guard.py
  • chat_shell/tests/test_summary_compactor.py
  • frontend/src/features/tasks/components/input/ChatToolbarStatus.tsx
🚧 Files skipped from review as they are similar to previous changes (10)
  • backend/app/services/chat/trigger/lifecycle.py
  • backend/tests/services/execution/test_emitters.py
  • backend/app/services/execution/emitters/websocket.py
  • chat_shell/tests/test_summary_compactor.py
  • frontend/src/features/tasks/components/input/ChatToolbarStatus.tsx
  • chat_shell/tests/test_compression.py
  • chat_shell/tests/test_context_guard.py
  • chat_shell/chat_shell/compression/config.py
  • chat_shell/chat_shell/compression/summary_compactor.py
  • chat_shell/chat_shell/guard/context_guard.py

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

🧹 Nitpick comments (2)
frontend/src/contexts/SocketContext.tsx (1)

230-245: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Consider clearing cached status on disconnect.

latestChatStatusRef is not cleared when the socket disconnects (unlike joinedTasksRef at line 281). This means handlers registered after reconnection but before rejoining tasks will receive potentially stale cached status.

Example scenario:

  1. Join task, cache status {phase: 'running'}
  2. Disconnect
  3. Reconnect
  4. Register handlers → receives stale {phase: 'running'}
  5. Rejoin task → receives fresh {phase: 'done'}

The window is brief and fresh status arrives after rejoin, but worth considering whether to clear the cache on disconnect:

if (!state.isConnected) {
  joinedTasksRef.current.clear()
  latestChatStatusRef.current = {}
}
🤖 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/contexts/SocketContext.tsx` around lines 230 - 245, The
latestChatStatusRef cache is not being cleared when the socket disconnects,
which can cause handlers to receive stale cached chat status after reconnection
but before rejoining tasks. Find where joinedTasksRef is cleared on disconnect
(around line 281 in the SocketProvider component) and add a statement to also
clear latestChatStatusRef by setting it to an empty object at that same
location. This ensures both the joined tasks set and the cached chat status are
synchronized and reset together on disconnect.
frontend/src/__tests__/contexts/SocketContext.reconnect.test.tsx (1)

44-53: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Mock socket limitation: only supports one handler per event.

The mock implementation stores only one handler per event in socketHandlers Map. Real Socket.IO allows multiple handlers per event. If future tests need to register multiple handlers for the same event, this mock will need enhancement to track an array of handlers per event.

Current tests appear to work with this limitation.

🤖 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/__tests__/contexts/SocketContext.reconnect.test.tsx` around
lines 44 - 53, The mock socket implementation uses a Map (socketHandlers) that
stores only a single handler per event, whereas real Socket.IO supports multiple
handlers for the same event. If future tests need to register multiple handlers
for the same event, enhance the socketHandlers to store an array of handlers per
event instead of a single handler, and update the off method to properly add
handlers to the array in the on method and remove specific handlers from the
array in the off method rather than deleting the entire event entry. This will
align the mock behavior more closely with actual Socket.IO functionality.
🤖 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.

Nitpick comments:
In `@frontend/src/__tests__/contexts/SocketContext.reconnect.test.tsx`:
- Around line 44-53: The mock socket implementation uses a Map (socketHandlers)
that stores only a single handler per event, whereas real Socket.IO supports
multiple handlers for the same event. If future tests need to register multiple
handlers for the same event, enhance the socketHandlers to store an array of
handlers per event instead of a single handler, and update the off method to
properly add handlers to the array in the on method and remove specific handlers
from the array in the off method rather than deleting the entire event entry.
This will align the mock behavior more closely with actual Socket.IO
functionality.

In `@frontend/src/contexts/SocketContext.tsx`:
- Around line 230-245: The latestChatStatusRef cache is not being cleared when
the socket disconnects, which can cause handlers to receive stale cached chat
status after reconnection but before rejoining tasks. Find where joinedTasksRef
is cleared on disconnect (around line 281 in the SocketProvider component) and
add a statement to also clear latestChatStatusRef by setting it to an empty
object at that same location. This ensures both the joined tasks set and the
cached chat status are synchronized and reset together on disconnect.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 248e7b1d-034f-4dbd-8188-6cccf4bae8e6

📥 Commits

Reviewing files that changed from the base of the PR and between e7d3fd0 and b273107.

📒 Files selected for processing (6)
  • backend/app/api/ws/chat_namespace.py
  • backend/app/api/ws/events.py
  • backend/tests/api/ws/test_chat_namespace_task_join.py
  • frontend/src/__tests__/contexts/SocketContext.reconnect.test.tsx
  • frontend/src/contexts/SocketContext.tsx
  • frontend/src/types/socket.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • backend/tests/api/ws/test_chat_namespace_task_join.py
  • backend/app/api/ws/chat_namespace.py

@qdaxb
qdaxb merged commit 78398e3 into wecode-ai:main Jun 23, 2026
24 checks passed
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.

2 participants