Skip to content

refactor(renderer): restructure chat main and diagnostics#1994

Merged
zerob13 merged 14 commits into
devfrom
refactor/renderer-app-boundaries
Jul 18, 2026
Merged

refactor(renderer): restructure chat main and diagnostics#1994
zerob13 merged 14 commits into
devfrom
refactor/renderer-app-boundaries

Conversation

@zhangmo8

@zhangmo8 zhangmo8 commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR restructures the renderer around explicit main-window ownership, decomposes the chat page into feature-local units, and adds an opt-in local performance diagnostics pipeline. It also introduces architecture-baseline enforcement and refreshes generated runtime metadata.

Renderer application boundaries

  • Introduce apps/chat-main/ChatMainApp.vue as the main-window composition root; retain App.vue and existing Vite/HTML entries as compatibility shims.
  • Assign startup ownership explicitly: ChatMainApp owns shell, IPC, and chrome; ChatTabView owns bootstrap, route initialization, and initial-session loading.
  • Preserve bootstrap active-session priority by starting the initial session request only after shell hydration.
  • Remove unused legacy browser assets and the obsolete @browser configuration.
  • Add a generated renderer architecture baseline and CI enforcement for application-entry and settings-to-chat boundaries.

Chat page feature decomposition

  • Move ChatPage and its related model/composables into features/chat-page.
  • Extract event bridging, message actions, pending-input actions, tool interaction, and voice-input behavior into focused composables.
  • Localize display-message modeling to the chat-page feature and update imports, fixtures, and tests accordingly.
  • Preserve existing chat behavior while making feature ownership and test boundaries explicit.

Local performance diagnostics

  • Add structured timing records for app-shell startup, bootstrap/route phases, startup workload terminal states, and chat-session viewport phases.
  • Route renderer records through a typed IPC contract to main-process persistence.
  • Persist newline-delimited JSON at <userData>/logs/renderer-performance.ndjson only when local logging is enabled.
  • Enforce a strict allowlist and main-process revalidation: records exclude user content, session IDs, paths, model/provider configuration, arbitrary error details, and credentials.
  • Serialize appends, rotate the file at 10 MiB, and isolate diagnostics failures from startup, restore, and interaction flows.

Supporting maintenance

  • Add and update architecture/feature documentation and task records.
  • Refresh generated icon, provider, and ACP-registry metadata.
  • Adjust renderer TypeScript/Vitest configuration and build helpers for the new source boundaries.

Architecture overview

Main window
┌────────────────────────────────────────────────────────────┐
│ ChatMainApp                                                 │
│  ├─ app shell / IPC runtime / window chrome                 │
│  ├─ RendererPerformanceReporter                              │
│  └─ ChatTabView                                             │
│      ├─ bootstrap shell → route initialization → sessions   │
│      └─ ChatPage feature                                    │
│          └─ focused chat-page composables                   │
└────────────────────────────────────────────────────────────┘

Performance records
ChatMainApp / ChatTabView / ChatPage
              │ allowlisted phase and elapsed metadata
              ▼
   RendererPerformanceReporter
              ▼ typed IPC route
 RendererPerformanceLogService
              ▼
<userData>/logs/renderer-performance.ndjson

UI layout

BEFORE / AFTER (visual structure unchanged)
┌───────────────────────────────────────────────────────────┐
│ AppBar                                                    │
├─────────────┬─────────────────────────────────────────────┤
│ WindowSide- │ RouterView                                  │
│ Bar         │                                             │
└─────────────┴─────────────────────────────────────────────┘

Validation

Completed before the later no-local-validation instruction:

  • pnpm run format
  • targeted renderer tests: 100 passed
  • targeted main tests: 9 passed
  • pnpm run typecheck
  • pnpm run i18n
  • pnpm run lint

A subsequently started full renderer suite was interrupted (exit 130) before it completed. Per the current instruction, no additional local lint, test, or typecheck commands were run after the final review adjustment; rely on the PR’s remote checks for subsequent validation.

Summary by CodeRabbit

  • New Features

    • Added optional renderer startup and chat-session performance diagnostics, with privacy-safe local logging when enabled.
    • Improved voice input, tool interactions, queued messages, message actions, and chat event handling.
  • Bug Fixes

    • Startup now prioritizes hydrated sessions and avoids duplicate session loading.
    • Improved cleanup for runtime listeners and deferred chat operations.
  • Refactor

    • Reorganized chat application composition and display models without changing expected rendering behavior.
  • Tests

    • Expanded coverage for startup ordering, session restoration, performance reporting, and chat interactions.

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR restructures the renderer main-window composition around a new ChatMainApp.vue, decomposes ChatPage.vue into several composables (voice input, tool interaction, message/pending-input actions, event bridge, session restore), relocates display-message types into a feature model, adds a renderer performance-diagnostics pipeline (typed route, main-process NDJSON logging, reporter), replaces the legacy browser renderer directory with browser-overlay, and introduces an architecture baseline generator/checker enforced in CI.

Changes

Renderer architecture and chat boundaries

Layer / File(s) Summary
Architecture contracts and baseline enforcement
docs/architecture/renderer-application-boundaries/*, docs/architecture/baselines/renderer-application-boundaries-baseline.json, scripts/generate-renderer-architecture-baseline.mjs, package.json, .github/workflows/prcheck.yml, AGENTS.md, docs/architecture/chat-display-model-boundary/*, docs/architecture/chatpage-decomposition/*, docs/guides/getting-started.md
Introduces renderer boundary spec/plan/tasks docs, a baseline generator script, npm scripts, and a CI check enforcing the baseline; docs are updated for new module locations.
Chat-main composition and startup ordering
src/renderer/src/App.vue, src/renderer/src/apps/chat-main/ChatMainApp.vue, src/renderer/src/views/ChatTabView.vue, test/renderer/components/App.startup.test.ts, test/renderer/components/ChatTabView.test.ts, test/renderer/stores/sessionStore.test.ts, electron.vite.config.ts
App.vue becomes a thin facade delegating to ChatMainApp.vue, which now owns global UI, theming, onboarding, IPC/MCP wiring, and error toasts; ChatTabView.vue reorders bootstrap/route/session-fetch and records performance milestones.
Chat-page composables and display model
src/renderer/src/features/chat-page/**, src/renderer/src/components/message/*, src/renderer/src/components/chat/*, test/renderer/features/chat-page/**, test/renderer/components/message/*, test/fixtures/mockMessages.ts
ChatPage.vue is decomposed into useVoiceInput, useToolInteraction, useMessageActions, usePendingInputActions, useChatPageEventBridge, useSessionRestore, usePlanFloatLifecycle, useListGestures, useComposerSubmit; display-message types move to features/chat-page/model/displayMessage.ts, with imports updated across components and tests.
Browser-overlay tooling migration
tsconfig.app.json, tsconfig.app.tsgo.json, vitest.config.ts, vitest.config.renderer.ts, scripts/generate-icon-collections.mjs
Replaces browser directory references and the @browser alias with browser-overlay across TS configs, test configs, and icon-collection scanning.

Estimated code review effort: 4 (Complex) | ~75 minutes

Renderer performance diagnostics

Layer / File(s) Summary
Performance route contract and persistence
src/shared/contracts/routes/performance.routes.ts, src/shared/contracts/routes.ts, src/renderer/api/PerformanceClient.ts, src/renderer/src/platform/performance/rendererPerformance.ts, src/main/app/rendererPerformanceLogService.ts, src/main/app/routes.ts, src/main/app/composition.ts, docs/architecture/renderer-performance-diagnostics/*, test/main/app/rendererPerformanceLogService.test.ts, test/main/app/routes.test.ts, test/renderer/lib/rendererPerformance.test.ts
Adds a strict Zod-validated performance record schema and performanceRecordRendererRoute, a RendererPerformanceReporter with buffering/dedup, a PerformanceClient, and a main-process RendererPerformanceLogService writing rotating NDJSON logs, wired through routes and composition.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Sequence Diagram(s)

sequenceDiagram
  participant RendererPerformanceReporter
  participant PerformanceClient
  participant performanceRecordRendererRoute
  participant RendererPerformanceLogService
  RendererPerformanceReporter->>PerformanceClient: submit typed record
  PerformanceClient->>performanceRecordRendererRoute: call performance.recordRenderer
  performanceRecordRendererRoute->>RendererPerformanceLogService: record validated record
  RendererPerformanceLogService-->>performanceRecordRendererRoute: return accepted
Loading

Possibly related PRs

  • ThinkInAIXYZ/deepchat#1506: Both PRs restructure ChatTabView.vue's critical hydration and session-fetch timing during startup.
  • ThinkInAIXYZ/deepchat#1989: Continues the same ChatPage-to-composables decomposition, adding overlapping composables like useSessionRestore, usePlanFloatLifecycle, and useListGestures.
  • ThinkInAIXYZ/deepchat#1357: Overlaps in removing/renaming the legacy src/renderer/browser surface and the @browser alias.

Suggested reviewers: zerob13

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% 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 is concise and accurately reflects the main changes: renderer chat-main restructuring and added diagnostics.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/renderer-app-boundaries

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

🤖 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 `@src/renderer/src/apps/chat-main/ChatMainApp.vue`:
- Around line 157-160: Make toast dismissal idempotent in the onOpenChange
handler and its timeout path. Update handleErrorClosed so the active error ID is
checked or ensure queue advancement occurs through only one of these paths,
preventing dismiss() from advancing the error queue twice and skipping an error.

In `@test/renderer/components/ChatTabView.test.ts`:
- Line 104: Update the activeSessionId assignment in the bootstrap payload to
preserve an explicitly provided null bootstrapActiveSessionId, while still
falling back to sessionStore.activeSessionId only when the option is undefined.
Keep the existing activeSession clearing behavior unchanged.
🪄 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: 05be7731-9f61-4bd0-84ab-d23f71ea930a

📥 Commits

Reviewing files that changed from the base of the PR and between e84428b and ed9a424.

⛔ Files ignored due to path filters (4)
  • src/renderer/browser/assets/ChromeClose.svg is excluded by !**/*.svg
  • src/renderer/browser/assets/ChromeMaximize-1.svg is excluded by !**/*.svg
  • src/renderer/browser/assets/ChromeMaximize.svg is excluded by !**/*.svg
  • src/renderer/browser/assets/ChromeMinimize.svg is excluded by !**/*.svg
📒 Files selected for processing (19)
  • .github/workflows/prcheck.yml
  • AGENTS.md
  • docs/architecture/baselines/renderer-application-boundaries-baseline.json
  • docs/architecture/renderer-application-boundaries/plan.md
  • docs/architecture/renderer-application-boundaries/spec.md
  • docs/architecture/renderer-application-boundaries/tasks.md
  • electron.vite.config.ts
  • package.json
  • scripts/generate-renderer-architecture-baseline.mjs
  • src/renderer/src/App.vue
  • src/renderer/src/apps/chat-main/ChatMainApp.vue
  • src/renderer/src/views/ChatTabView.vue
  • test/renderer/components/App.startup.test.ts
  • test/renderer/components/ChatTabView.test.ts
  • test/renderer/stores/sessionStore.test.ts
  • tsconfig.app.json
  • tsconfig.app.tsgo.json
  • vitest.config.renderer.ts
  • vitest.config.ts
💤 Files with no reviewable changes (3)
  • vitest.config.ts
  • electron.vite.config.ts
  • vitest.config.renderer.ts

Comment thread src/renderer/src/apps/chat-main/ChatMainApp.vue Outdated
Comment thread test/renderer/components/ChatTabView.test.ts Outdated

@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: 3

Caution

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

⚠️ Outside diff range comments (1)
resources/model-db/providers.json (1)

254651-254736: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Fix the TTS model metadata
resources/model-db/providers.json:254651-254736 — these tts-1 entries should declare modalities.input: ["text"], modalities.output: ["audio"], and type: "tts".

🤖 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 `@resources/model-db/providers.json` around lines 254651 - 254736, Update the
tts-1-hd-1106, tts-1-hd, tts-1-1106, and tts-1 entries so modalities.input is
["text"], modalities.output is ["audio"], and each entry includes type set to
"tts", while preserving their existing limits, costs, and other metadata.
🤖 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 `@resources/acp-registry/registry.json`:
- Around line 1055-1062: Update the windows-aarch64 entry in the registry
configuration so its cmd value uses the Windows executable name
"./opencode.exe", matching the windows-x86_64 configuration; leave the archive,
args, and checksum unchanged.

In `@resources/model-db/providers.json`:
- Around line 258583-258609: Update the Meta Muse Spark 1.1 entry identified by
id "meta/muse-spark-1.1" to set its type to "chat" instead of "imageGeneration",
leaving the remaining model metadata unchanged.
- Around line 184112-184138: Update the token limits for the
openai/gpt-realtime-whisper entry to set context to 16,000 and output to 2,000,
leaving the surrounding model metadata unchanged.

---

Outside diff comments:
In `@resources/model-db/providers.json`:
- Around line 254651-254736: Update the tts-1-hd-1106, tts-1-hd, tts-1-1106, and
tts-1 entries so modalities.input is ["text"], modalities.output is ["audio"],
and each entry includes type set to "tts", while preserving their existing
limits, costs, and other metadata.
🪄 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: afa62975-d35d-4784-8f4d-9f37486abd39

📥 Commits

Reviewing files that changed from the base of the PR and between ed9a424 and d2124c8.

⛔ Files ignored due to path filters (2)
  • src/renderer/src/lib/icons/icon-collections.generated.ts is excluded by !**/*.generated.*
  • src/renderer/src/lib/icons/icon-whitelist.generated.ts is excluded by !**/*.generated.*
📒 Files selected for processing (3)
  • resources/acp-registry/registry.json
  • resources/model-db/providers.json
  • scripts/generate-icon-collections.mjs
💤 Files with no reviewable changes (1)
  • scripts/generate-icon-collections.mjs

Comment thread resources/acp-registry/registry.json
Comment thread resources/model-db/providers.json
Comment thread resources/model-db/providers.json
@zhangmo8
zhangmo8 marked this pull request as draft July 17, 2026 10:29
@zhangmo8 zhangmo8 changed the title refactor(renderer): establish app boundaries refactor(renderer): add app performance diagnostics Jul 18, 2026
@zhangmo8
zhangmo8 marked this pull request as ready for review July 18, 2026 05:11
@zhangmo8 zhangmo8 changed the title refactor(renderer): add app performance diagnostics refactor(renderer): restructure chat main and diagnostics Jul 18, 2026

@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: 9

🧹 Nitpick comments (1)
src/shared/contracts/routes/performance.routes.ts (1)

41-55: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Split this schema by scope
Use a discriminated union so startup, workload, and chat-session only accept the fields they actually emit. Keep startupRunId as an opaque string; the app treats it that way, so the generator’s exact format should stay out of this contract.

🤖 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 `@src/shared/contracts/routes/performance.routes.ts` around lines 41 - 55,
Split RendererPerformanceRecordSchema into a scope-discriminated union with
dedicated schemas for startup, workload, and chat-session records, restricting
each variant to only the fields it emits. Preserve the existing shared
validation and strictness, and keep startupRunId as the current opaque bounded
string without importing or enforcing generator-specific formatting.
🤖 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 `@src/renderer/src/features/chat-page/composables/useComposerSubmit.ts`:
- Around line 275-307: Update onSubmit and the additional async submission paths
around sendMessageWithOutgoingTurnFeedback to track the composer revision
represented by the submitted text, files, and skills. After each await, clear
message, attachedFiles, and composer skills only when the current revision still
matches that submitted revision; otherwise preserve newer edits, while retaining
the existing submission and failure behavior.
- Around line 223-229: Update the send failure handling around
chatClient.sendMessage in useComposerSubmit so a rejected send preserves
retryable user content: either retain a failed item with the captured payload or
restore that payload without overwriting newer composer edits. Keep the existing
optimistic-message cleanup as appropriate, but do not only log and swallow the
error after callers have cleared the draft.

In `@src/renderer/src/features/chat-page/composables/useListGestures.ts`:
- Around line 181-190: Update markWheelScrollIntent to notify
options.onGestureStart with 'wheel' before updating pagination intent, matching
the keyboard path. Ensure repeated keyboard events do not emit duplicate
'keyboard' start notifications before the corresponding settle-time
onGestureEnd, preserving balanced gesture lifecycle transitions.

In `@src/renderer/src/features/chat-page/composables/useMessageActions.ts`:
- Around line 93-101: Capture options.sessionId() once at the start of
onMessageEditSave and use that captured session for both editUserMessage and
onMessageRetry. Update the retry helper invocation, and its implementation if
needed, to accept and preserve the captured session so the edit and retry remain
bound to the same session across the await.

In `@src/renderer/src/features/chat-page/composables/usePendingInputActions.ts`:
- Around line 38-42: Update the replacement payload in useComposerSubmit when
calling pendingInputStore.updateQueueInput to include the existing queued
inlineItems from target.payload, preserving them while editing text. Keep the
current defaults for files and activeSkills unchanged.
- Around line 65-67: Capture the session ID once before awaiting
steerPendingInput in the pending-input action, then reuse that captured value
for both options.pendingInputStore.steerPendingInput and options.beginPlanTurn.
Update the surrounding try flow so steering and plan state always remain
associated with the same session.

In `@src/renderer/src/features/chat-page/composables/useSessionRestore.ts`:
- Around line 54-70: The restore flow in runRestore and restoreSessionMessages
must handle rejections from both loadMessagesForSession and
pendingInputStore.loadPendingInputs. Attach rejection handling immediately,
ensure failures do not become unhandled promises, and leave the restore/loading
state in its defined failure condition while preserving requestId guards for
stale restores.

In `@src/renderer/src/features/chat-page/composables/useToolInteraction.ts`:
- Around line 48-55: Strengthen parseSubagentProgress to runtime-validate the
parsed payload before returning it: verify every task, waiting interaction, and
action block has the expected object shape and field types, rejecting malformed
nested values such as null tasks. Ensure only validated SubagentProgressPayload
data reaches the rendering/list construction logic around the task processing
flow.

In `@src/renderer/src/platform/performance/rendererPerformance.ts`:
- Around line 61-77: Update setEnabled so enabling with an existing startup run
ID also submits buffered startup records whose startupRunId matches the active
run. Preserve startup records for other or not-yet-active runs, while continuing
to submit pending non-startup records as currently handled.

---

Nitpick comments:
In `@src/shared/contracts/routes/performance.routes.ts`:
- Around line 41-55: Split RendererPerformanceRecordSchema into a
scope-discriminated union with dedicated schemas for startup, workload, and
chat-session records, restricting each variant to only the fields it emits.
Preserve the existing shared validation and strictness, and keep startupRunId as
the current opaque bounded string without importing or enforcing
generator-specific formatting.
🪄 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: 74896967-68de-42e3-ba22-5c74d2c92595

📥 Commits

Reviewing files that changed from the base of the PR and between 2b9fe1e and 11a229f.

📒 Files selected for processing (82)
  • docs/architecture/chat-display-model-boundary/plan.md
  • docs/architecture/chat-display-model-boundary/spec.md
  • docs/architecture/chat-display-model-boundary/tasks.md
  • docs/architecture/chatpage-decomposition/spec.md
  • docs/architecture/chatpage-decomposition/tasks.md
  • docs/architecture/renderer-application-boundaries/plan.md
  • docs/architecture/renderer-application-boundaries/tasks.md
  • docs/architecture/renderer-performance-diagnostics/plan.md
  • docs/architecture/renderer-performance-diagnostics/spec.md
  • docs/architecture/renderer-performance-diagnostics/tasks.md
  • docs/guides/getting-started.md
  • package.json
  • resources/acp-registry/registry.json
  • resources/model-db/providers.json
  • scripts/agent-cleanup-guard.mjs
  • scripts/generate-architecture-baseline.mjs
  • src/main/app/composition.ts
  • src/main/app/rendererPerformanceLogService.ts
  • src/main/app/routes.ts
  • src/renderer/api/PerformanceClient.ts
  • src/renderer/src/apps/chat-main/ChatMainApp.vue
  • src/renderer/src/components/chat/ChatToolInteractionOverlay.vue
  • src/renderer/src/components/chat/MessageList.vue
  • src/renderer/src/components/chat/MessageListRow.vue
  • src/renderer/src/components/message/MessageBlockAction.vue
  • src/renderer/src/components/message/MessageBlockActivityGroup.vue
  • src/renderer/src/components/message/MessageBlockAudio.vue
  • src/renderer/src/components/message/MessageBlockContent.vue
  • src/renderer/src/components/message/MessageBlockError.vue
  • src/renderer/src/components/message/MessageBlockImage.vue
  • src/renderer/src/components/message/MessageBlockQuestionRequest.vue
  • src/renderer/src/components/message/MessageBlockThink.vue
  • src/renderer/src/components/message/MessageBlockToolCall.vue
  • src/renderer/src/components/message/MessageBlockVideo.vue
  • src/renderer/src/components/message/MessageContent.vue
  • src/renderer/src/components/message/MessageItemAssistant.vue
  • src/renderer/src/components/message/MessageItemUser.vue
  • src/renderer/src/components/message/messageActivityGroups.ts
  • src/renderer/src/composables/message/useMessageWindow.ts
  • src/renderer/src/composables/useArtifacts.ts
  • src/renderer/src/features/chat-page/ChatPage.vue
  • src/renderer/src/features/chat-page/composables/useChatPageEventBridge.ts
  • src/renderer/src/features/chat-page/composables/useChatSearch.ts
  • src/renderer/src/features/chat-page/composables/useComposerSubmit.ts
  • src/renderer/src/features/chat-page/composables/useDisplayMessages.ts
  • src/renderer/src/features/chat-page/composables/useListGestures.ts
  • src/renderer/src/features/chat-page/composables/useMessageActions.ts
  • src/renderer/src/features/chat-page/composables/useMessageVirtualization.ts
  • src/renderer/src/features/chat-page/composables/usePendingInputActions.ts
  • src/renderer/src/features/chat-page/composables/usePlanFloatLifecycle.ts
  • src/renderer/src/features/chat-page/composables/useSessionRestore.ts
  • src/renderer/src/features/chat-page/composables/useToolInteraction.ts
  • src/renderer/src/features/chat-page/composables/useVoiceInput.ts
  • src/renderer/src/features/chat-page/model/displayMessage.ts
  • src/renderer/src/platform/performance/rendererPerformance.ts
  • src/renderer/src/stores/ui/message.ts
  • src/renderer/src/views/ChatTabView.vue
  • src/shared/contracts/routes.ts
  • src/shared/contracts/routes/performance.routes.ts
  • test/fixtures/mockMessages.ts
  • test/main/app/rendererPerformanceLogService.test.ts
  • test/main/app/routes.test.ts
  • test/renderer/components/App.startup.test.ts
  • test/renderer/components/ChatPage.test.ts
  • test/renderer/components/ChatTabView.test.ts
  • test/renderer/components/MessageList.test.ts
  • test/renderer/components/message/MessageBlockActivityGroup.test.ts
  • test/renderer/components/message/MessageBlockBasics.test.ts
  • test/renderer/components/message/MessageBlockContent.test.ts
  • test/renderer/components/message/MessageBlockMedia.test.ts
  • test/renderer/components/message/MessageBlockToolCall.test.ts
  • test/renderer/components/message/MessageItemAssistant.test.ts
  • test/renderer/components/message/MessageItemUser.test.ts
  • test/renderer/components/message/messageActivityGroups.test.ts
  • test/renderer/composables/useMessageWindow.test.ts
  • test/renderer/features/chat-page/composables/useChatPageEventBridge.test.ts
  • test/renderer/features/chat-page/composables/useMessageActions.test.ts
  • test/renderer/features/chat-page/composables/usePendingInputActions.test.ts
  • test/renderer/features/chat-page/composables/useToolInteraction.test.ts
  • test/renderer/features/chat-page/composables/useVoiceInput.test.ts
  • test/renderer/lib/rendererPerformance.test.ts
  • test/renderer/performance/chatRendering.perf.test.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • package.json
  • docs/architecture/renderer-application-boundaries/tasks.md
  • resources/acp-registry/registry.json
  • test/renderer/components/ChatTabView.test.ts
  • src/renderer/src/apps/chat-main/ChatMainApp.vue

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

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 9

🧹 Nitpick comments (1)
src/shared/contracts/routes/performance.routes.ts (1)

41-55: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Split this schema by scope
Use a discriminated union so startup, workload, and chat-session only accept the fields they actually emit. Keep startupRunId as an opaque string; the app treats it that way, so the generator’s exact format should stay out of this contract.

🤖 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 `@src/shared/contracts/routes/performance.routes.ts` around lines 41 - 55,
Split RendererPerformanceRecordSchema into a scope-discriminated union with
dedicated schemas for startup, workload, and chat-session records, restricting
each variant to only the fields it emits. Preserve the existing shared
validation and strictness, and keep startupRunId as the current opaque bounded
string without importing or enforcing generator-specific formatting.
🤖 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 `@src/renderer/src/features/chat-page/composables/useComposerSubmit.ts`:
- Around line 275-307: Update onSubmit and the additional async submission paths
around sendMessageWithOutgoingTurnFeedback to track the composer revision
represented by the submitted text, files, and skills. After each await, clear
message, attachedFiles, and composer skills only when the current revision still
matches that submitted revision; otherwise preserve newer edits, while retaining
the existing submission and failure behavior.
- Around line 223-229: Update the send failure handling around
chatClient.sendMessage in useComposerSubmit so a rejected send preserves
retryable user content: either retain a failed item with the captured payload or
restore that payload without overwriting newer composer edits. Keep the existing
optimistic-message cleanup as appropriate, but do not only log and swallow the
error after callers have cleared the draft.

In `@src/renderer/src/features/chat-page/composables/useListGestures.ts`:
- Around line 181-190: Update markWheelScrollIntent to notify
options.onGestureStart with 'wheel' before updating pagination intent, matching
the keyboard path. Ensure repeated keyboard events do not emit duplicate
'keyboard' start notifications before the corresponding settle-time
onGestureEnd, preserving balanced gesture lifecycle transitions.

In `@src/renderer/src/features/chat-page/composables/useMessageActions.ts`:
- Around line 93-101: Capture options.sessionId() once at the start of
onMessageEditSave and use that captured session for both editUserMessage and
onMessageRetry. Update the retry helper invocation, and its implementation if
needed, to accept and preserve the captured session so the edit and retry remain
bound to the same session across the await.

In `@src/renderer/src/features/chat-page/composables/usePendingInputActions.ts`:
- Around line 38-42: Update the replacement payload in useComposerSubmit when
calling pendingInputStore.updateQueueInput to include the existing queued
inlineItems from target.payload, preserving them while editing text. Keep the
current defaults for files and activeSkills unchanged.
- Around line 65-67: Capture the session ID once before awaiting
steerPendingInput in the pending-input action, then reuse that captured value
for both options.pendingInputStore.steerPendingInput and options.beginPlanTurn.
Update the surrounding try flow so steering and plan state always remain
associated with the same session.

In `@src/renderer/src/features/chat-page/composables/useSessionRestore.ts`:
- Around line 54-70: The restore flow in runRestore and restoreSessionMessages
must handle rejections from both loadMessagesForSession and
pendingInputStore.loadPendingInputs. Attach rejection handling immediately,
ensure failures do not become unhandled promises, and leave the restore/loading
state in its defined failure condition while preserving requestId guards for
stale restores.

In `@src/renderer/src/features/chat-page/composables/useToolInteraction.ts`:
- Around line 48-55: Strengthen parseSubagentProgress to runtime-validate the
parsed payload before returning it: verify every task, waiting interaction, and
action block has the expected object shape and field types, rejecting malformed
nested values such as null tasks. Ensure only validated SubagentProgressPayload
data reaches the rendering/list construction logic around the task processing
flow.

In `@src/renderer/src/platform/performance/rendererPerformance.ts`:
- Around line 61-77: Update setEnabled so enabling with an existing startup run
ID also submits buffered startup records whose startupRunId matches the active
run. Preserve startup records for other or not-yet-active runs, while continuing
to submit pending non-startup records as currently handled.

---

Nitpick comments:
In `@src/shared/contracts/routes/performance.routes.ts`:
- Around line 41-55: Split RendererPerformanceRecordSchema into a
scope-discriminated union with dedicated schemas for startup, workload, and
chat-session records, restricting each variant to only the fields it emits.
Preserve the existing shared validation and strictness, and keep startupRunId as
the current opaque bounded string without importing or enforcing
generator-specific formatting.
🪄 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: 74896967-68de-42e3-ba22-5c74d2c92595

📥 Commits

Reviewing files that changed from the base of the PR and between 2b9fe1e and 11a229f.

📒 Files selected for processing (82)
  • docs/architecture/chat-display-model-boundary/plan.md
  • docs/architecture/chat-display-model-boundary/spec.md
  • docs/architecture/chat-display-model-boundary/tasks.md
  • docs/architecture/chatpage-decomposition/spec.md
  • docs/architecture/chatpage-decomposition/tasks.md
  • docs/architecture/renderer-application-boundaries/plan.md
  • docs/architecture/renderer-application-boundaries/tasks.md
  • docs/architecture/renderer-performance-diagnostics/plan.md
  • docs/architecture/renderer-performance-diagnostics/spec.md
  • docs/architecture/renderer-performance-diagnostics/tasks.md
  • docs/guides/getting-started.md
  • package.json
  • resources/acp-registry/registry.json
  • resources/model-db/providers.json
  • scripts/agent-cleanup-guard.mjs
  • scripts/generate-architecture-baseline.mjs
  • src/main/app/composition.ts
  • src/main/app/rendererPerformanceLogService.ts
  • src/main/app/routes.ts
  • src/renderer/api/PerformanceClient.ts
  • src/renderer/src/apps/chat-main/ChatMainApp.vue
  • src/renderer/src/components/chat/ChatToolInteractionOverlay.vue
  • src/renderer/src/components/chat/MessageList.vue
  • src/renderer/src/components/chat/MessageListRow.vue
  • src/renderer/src/components/message/MessageBlockAction.vue
  • src/renderer/src/components/message/MessageBlockActivityGroup.vue
  • src/renderer/src/components/message/MessageBlockAudio.vue
  • src/renderer/src/components/message/MessageBlockContent.vue
  • src/renderer/src/components/message/MessageBlockError.vue
  • src/renderer/src/components/message/MessageBlockImage.vue
  • src/renderer/src/components/message/MessageBlockQuestionRequest.vue
  • src/renderer/src/components/message/MessageBlockThink.vue
  • src/renderer/src/components/message/MessageBlockToolCall.vue
  • src/renderer/src/components/message/MessageBlockVideo.vue
  • src/renderer/src/components/message/MessageContent.vue
  • src/renderer/src/components/message/MessageItemAssistant.vue
  • src/renderer/src/components/message/MessageItemUser.vue
  • src/renderer/src/components/message/messageActivityGroups.ts
  • src/renderer/src/composables/message/useMessageWindow.ts
  • src/renderer/src/composables/useArtifacts.ts
  • src/renderer/src/features/chat-page/ChatPage.vue
  • src/renderer/src/features/chat-page/composables/useChatPageEventBridge.ts
  • src/renderer/src/features/chat-page/composables/useChatSearch.ts
  • src/renderer/src/features/chat-page/composables/useComposerSubmit.ts
  • src/renderer/src/features/chat-page/composables/useDisplayMessages.ts
  • src/renderer/src/features/chat-page/composables/useListGestures.ts
  • src/renderer/src/features/chat-page/composables/useMessageActions.ts
  • src/renderer/src/features/chat-page/composables/useMessageVirtualization.ts
  • src/renderer/src/features/chat-page/composables/usePendingInputActions.ts
  • src/renderer/src/features/chat-page/composables/usePlanFloatLifecycle.ts
  • src/renderer/src/features/chat-page/composables/useSessionRestore.ts
  • src/renderer/src/features/chat-page/composables/useToolInteraction.ts
  • src/renderer/src/features/chat-page/composables/useVoiceInput.ts
  • src/renderer/src/features/chat-page/model/displayMessage.ts
  • src/renderer/src/platform/performance/rendererPerformance.ts
  • src/renderer/src/stores/ui/message.ts
  • src/renderer/src/views/ChatTabView.vue
  • src/shared/contracts/routes.ts
  • src/shared/contracts/routes/performance.routes.ts
  • test/fixtures/mockMessages.ts
  • test/main/app/rendererPerformanceLogService.test.ts
  • test/main/app/routes.test.ts
  • test/renderer/components/App.startup.test.ts
  • test/renderer/components/ChatPage.test.ts
  • test/renderer/components/ChatTabView.test.ts
  • test/renderer/components/MessageList.test.ts
  • test/renderer/components/message/MessageBlockActivityGroup.test.ts
  • test/renderer/components/message/MessageBlockBasics.test.ts
  • test/renderer/components/message/MessageBlockContent.test.ts
  • test/renderer/components/message/MessageBlockMedia.test.ts
  • test/renderer/components/message/MessageBlockToolCall.test.ts
  • test/renderer/components/message/MessageItemAssistant.test.ts
  • test/renderer/components/message/MessageItemUser.test.ts
  • test/renderer/components/message/messageActivityGroups.test.ts
  • test/renderer/composables/useMessageWindow.test.ts
  • test/renderer/features/chat-page/composables/useChatPageEventBridge.test.ts
  • test/renderer/features/chat-page/composables/useMessageActions.test.ts
  • test/renderer/features/chat-page/composables/usePendingInputActions.test.ts
  • test/renderer/features/chat-page/composables/useToolInteraction.test.ts
  • test/renderer/features/chat-page/composables/useVoiceInput.test.ts
  • test/renderer/lib/rendererPerformance.test.ts
  • test/renderer/performance/chatRendering.perf.test.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • package.json
  • docs/architecture/renderer-application-boundaries/tasks.md
  • resources/acp-registry/registry.json
  • test/renderer/components/ChatTabView.test.ts
  • src/renderer/src/apps/chat-main/ChatMainApp.vue
🛑 Comments failed to post (9)
src/renderer/src/features/chat-page/composables/useComposerSubmit.ts (2)

223-229: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Do not swallow failed sends after clearing the draft.

Both direct-send callers clear composer state before awaiting this helper. When sendMessage rejects, the optimistic UI is removed and the error is only logged, leaving the user with neither their draft nor actionable feedback. Preserve a retryable failed item or restore the captured payload without overwriting newer edits.

🤖 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 `@src/renderer/src/features/chat-page/composables/useComposerSubmit.ts` around
lines 223 - 229, Update the send failure handling around chatClient.sendMessage
in useComposerSubmit so a rejected send preserves retryable user content: either
retain a failed item with the captured payload or restore that payload without
overwriting newer composer edits. Keep the existing optimistic-message cleanup
as appropriate, but do not only log and swallow the error after callers have
cleared the draft.

275-307: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Protect newer composer edits from stale async clears.

These paths snapshot some payload fields, await asynchronous work, then clear the current message, attachments, or skills. Input added while those promises are pending is therefore erased. Track a composer revision and only clear the submitted revision, or remove the captured state before awaiting and restore it safely on failure.

Also applies to: 310-340, 342-381

🤖 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 `@src/renderer/src/features/chat-page/composables/useComposerSubmit.ts` around
lines 275 - 307, Update onSubmit and the additional async submission paths
around sendMessageWithOutgoingTurnFeedback to track the composer revision
represented by the submitted text, files, and skills. After each await, clear
message, attachedFiles, and composer skills only when the current revision still
matches that submitted revision; otherwise preserve newer edits, while retaining
the existing submission and failure behavior.
src/renderer/src/features/chat-page/composables/useListGestures.ts (1)

181-190: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Balance wheel and keyboard gesture lifecycle notifications.

Wheel scrolling never calls onGestureStart('wheel'), while repeated keyboard events call onGestureStart('keyboard') multiple times before the single settle-time onGestureEnd(). This can leave auto-follow active for wheel input and produce mismatched controller transitions.

Proposed fix
 function markKeyboardScrollIntent(isUpward: boolean): void {
-  options.onGestureStart('keyboard')
+  if (!isListScrolling.value) {
+    options.onGestureStart('keyboard')
+  }
   updateUpwardPaginationIntent(isUpward)
   markListScrolling()
 }

 function markWheelScrollIntent(isUpward: boolean): void {
+  if (!isListScrolling.value) {
+    options.onGestureStart('wheel')
+  }
   updateUpwardPaginationIntent(isUpward)
   markListScrolling()
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

  function markKeyboardScrollIntent(isUpward: boolean): void {
    if (!isListScrolling.value) {
      options.onGestureStart('keyboard')
    }
    updateUpwardPaginationIntent(isUpward)
    markListScrolling()
  }

  /** Wheel intent: arm pagination direction and keep the scrolling state warm. */
  function markWheelScrollIntent(isUpward: boolean): void {
    if (!isListScrolling.value) {
      options.onGestureStart('wheel')
    }
    updateUpwardPaginationIntent(isUpward)
    markListScrolling()
🤖 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 `@src/renderer/src/features/chat-page/composables/useListGestures.ts` around
lines 181 - 190, Update markWheelScrollIntent to notify options.onGestureStart
with 'wheel' before updating pagination intent, matching the keyboard path.
Ensure repeated keyboard events do not emit duplicate 'keyboard' start
notifications before the corresponding settle-time onGestureEnd, preserving
balanced gesture lifecycle transitions.
src/renderer/src/features/chat-page/composables/useMessageActions.ts (1)

93-101: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Keep edit and retry bound to the same session.

Line 100 targets the session active when editing starts, but Line 101 rereads sessionId after the await. If the user switches sessions meanwhile, the retry is sent with the old message ID and new session ID. Capture the session once and pass it through the retry helper.

🤖 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 `@src/renderer/src/features/chat-page/composables/useMessageActions.ts` around
lines 93 - 101, Capture options.sessionId() once at the start of
onMessageEditSave and use that captured session for both editUserMessage and
onMessageRetry. Update the retry helper invocation, and its implementation if
needed, to accept and preserve the captured session so the edit and retry remain
bound to the same session across the await.
src/renderer/src/features/chat-page/composables/usePendingInputActions.ts (2)

38-42: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Preserve queued inlineItems when editing text.

useComposerSubmit includes inlineItems in queued payloads, but this replacement payload omits them. Editing queued text can therefore discard workspace references or other inline metadata.

Proposed fix
     await options.pendingInputStore.updateQueueInput(options.sessionId(), payload.itemId, {
       text: payload.text,
       files: target.payload.files ?? [],
-      activeSkills: target.payload.activeSkills ?? []
+      activeSkills: target.payload.activeSkills ?? [],
+      inlineItems: target.payload.inlineItems ?? []
     })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    await options.pendingInputStore.updateQueueInput(options.sessionId(), payload.itemId, {
      text: payload.text,
      files: target.payload.files ?? [],
      activeSkills: target.payload.activeSkills ?? [],
      inlineItems: target.payload.inlineItems ?? []
    })
🤖 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 `@src/renderer/src/features/chat-page/composables/usePendingInputActions.ts`
around lines 38 - 42, Update the replacement payload in useComposerSubmit when
calling pendingInputStore.updateQueueInput to include the existing queued
inlineItems from target.payload, preserving them while editing text. Keep the
current defaults for files and activeSkills unchanged.

65-67: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Keep steering and plan state bound to the same session.

The session ID is read before and after the await. If navigation occurs while steering, the request targets the old session but beginPlanTurn targets the new one.

Proposed fix
+    const sessionId = options.sessionId()
     try {
-      await options.pendingInputStore.steerPendingInput(options.sessionId(), itemId)
-      options.beginPlanTurn(options.sessionId())
+      await options.pendingInputStore.steerPendingInput(sessionId, itemId)
+      options.beginPlanTurn(sessionId)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    const sessionId = options.sessionId()
    try {
      await options.pendingInputStore.steerPendingInput(sessionId, itemId)
      options.beginPlanTurn(sessionId)
🤖 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 `@src/renderer/src/features/chat-page/composables/usePendingInputActions.ts`
around lines 65 - 67, Capture the session ID once before awaiting
steerPendingInput in the pending-input action, then reuse that captured value
for both options.pendingInputStore.steerPendingInput and options.beginPlanTurn.
Update the surrounding try flow so steering and plan state always remain
associated with the same session.
src/renderer/src/features/chat-page/composables/useSessionRestore.ts (1)

54-70: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Handle failures from both restore operations.

runRestore() is invoked with its promise discarded, while loadMessagesForSession has no catch and pendingInputsPromise.then(...) has no rejection handler. Either store failure becomes an unhandled rejection; attach error handling immediately and leave the restore/loading state in a defined condition.

🤖 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 `@src/renderer/src/features/chat-page/composables/useSessionRestore.ts` around
lines 54 - 70, The restore flow in runRestore and restoreSessionMessages must
handle rejections from both loadMessagesForSession and
pendingInputStore.loadPendingInputs. Attach rejection handling immediately,
ensure failures do not become unhandled promises, and leave the restore/loading
state in its defined failure condition while preserving requestId guards for
stale restores.
src/renderer/src/features/chat-page/composables/useToolInteraction.ts (1)

48-55: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Validate parsed subagent progress before dereferencing it.

The type assertion does not validate JSON. A payload such as {"tasks":[null]} passes Line 55 and crashes at Line 114, while malformed nested blocks can escape into rendering. Add runtime validation for each task, waiting interaction, and action block before constructing the list.

Also applies to: 113-127

🤖 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 `@src/renderer/src/features/chat-page/composables/useToolInteraction.ts` around
lines 48 - 55, Strengthen parseSubagentProgress to runtime-validate the parsed
payload before returning it: verify every task, waiting interaction, and action
block has the expected object shape and field types, rejecting malformed nested
values such as null tasks. Ensure only validated SubagentProgressPayload data
reaches the rendering/list construction logic around the task processing flow.
src/renderer/src/platform/performance/rendererPerformance.ts (1)

61-77: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Flush buffered startup records when enabling with an existing run ID.

If recordStartup(..., { startupRunId }) runs while disabled, its flush is skipped. A later setEnabled(true) retains those startup records indefinitely because it only submits non-startup records.

Proposed fix
     if (!enabled) {
       this.pendingRecords = []
       return
     }

+    if (this.startupRunId) {
+      this.flushPendingStartupRecords()
+      return
+    }
+
     const pendingNonStartupRecords = this.pendingRecords.filter(
       (record) => record.scope !== 'startup'
     )
🤖 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 `@src/renderer/src/platform/performance/rendererPerformance.ts` around lines 61
- 77, Update setEnabled so enabling with an existing startup run ID also submits
buffered startup records whose startupRunId matches the active run. Preserve
startup records for other or not-yet-active runs, while continuing to submit
pending non-startup records as currently handled.

@zerob13
zerob13 merged commit a9ea0e7 into dev Jul 18, 2026
4 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