Skip to content

PR Summary: Chat Mode Persistence & Code Review Fixes#3142

Open
nourzakhama2003 wants to merge 21 commits intodyad-sh:mainfrom
nourzakhama2003:fix/chat-mode-code-review-followup
Open

PR Summary: Chat Mode Persistence & Code Review Fixes#3142
nourzakhama2003 wants to merge 21 commits intodyad-sh:mainfrom
nourzakhama2003:fix/chat-mode-code-review-followup

Conversation

@nourzakhama2003
Copy link
Copy Markdown
Collaborator

@nourzakhama2003 nourzakhama2003 commented Apr 6, 2026

Closes #2926

Summary
This PR introduces robust per-chat mode persistence, addressing all code review feedback (Dyadbot + Devin) and modernizing the chat mode architecture for flexibility, reliability, and maintainability.

Key Improvements

  1. Per-Chat Mode Persistence
    Each chat now stores its own chatMode in the database (optional for backward compatibility).
    Existing chats with chatMode: null default to the global user setting.
    UI updates both the immediate session (settings.selectedChatMode) and persists to DB (ipc.chat.updateChatMode()).
  2. Reliable Mode Resolution
    On chat switch, the app restores the mode from the chat’s DB value if set, otherwise falls back to the global setting.
    Stream path uses a non-blocking formula:
    effectiveStreamMode = chat.chatMode ?? settings.selectedChatMode
    All updates are coordinated to prevent race conditions between DB and settings.
  3. In-Flight & Optimistic Updates
    “Build + Send” and keyboard shortcut operations are now optimistic: UI updates instantly, with rollback if DB persist fails.
    Re-entrancy guard prevents rapid keypresses from queuing multiple async operations.
  4. Architectural & Code Quality Enhancements
    ChatPanel restore effect uses a cleanup flag pattern to prevent stale async results from overwriting state.
    useSelectChat fallback now waits for DB persist before showing notifications, preventing repeated toasts.
    ChatModeSelector’s complex ternary logic is now a readable helper function, improving maintainability.
  5. User Experience
    Users can assign and persist different modes per chat, with seamless fallback and clear feedback if a mode becomes unavailable.
    All eligibility checks and fallbacks are coordinated to ensure a smooth, predictable experience.

Testing
E2E:
npm run e2e -- chat_mode_persistence.spec.ts
All core scenarios and regressions are covered, including mode switching, persistence, eligibility fallback, and rapid user actions.


Open with Devin

@wwwillchen
Copy link
Copy Markdown
Collaborator

@BugBot run

gemini-code-assist[bot]

This comment was marked as resolved.

dyad-assistant[bot]

This comment was marked as resolved.

@dyad-assistant
Copy link
Copy Markdown
Contributor

dyad-assistant bot commented Apr 6, 2026

🔍 Dyadbot Code Review Summary

Verdict: ⛔ NO - Do NOT merge

Reviewed by 3 independent agents: Correctness Expert, Code Health Expert, UX Wizard. Three HIGH-severity issues involve race conditions / rollback paths in the new per-chat mode persistence, and a fourth puts the user back into the broken state they were trying to escape. Multiple MEDIUM issues compound the risk.

Issues Summary

Severity File Issue
🔴 HIGH src/hooks/useChatModeToggle.ts:124 onError rollback can clobber a different chat's mode after navigation
🔴 HIGH src/components/ChatPanel.tsx:146 Mode-restore effect re-runs on chatsList invalidation and reverts in-flight mode changes
🔴 HIGH src/components/ChatPanel.tsx:297 "Switch to Build" rollback dumps the user back into the unusable mode the banner was escaping
🟡 MEDIUM src/hooks/useChatModeToggle.ts:69 Empty availableModesupdateSettings({selectedChatMode: undefined})
🟡 MEDIUM src/hooks/useSelectChat.ts:85 Fallback persist races with chat switching; trailing .catch() is dead code; toast shows raw enum
🟡 MEDIUM src/ipc/handlers/chat_stream_handlers.ts:563 Legacy-chat migration imprints transient req.chatMode permanently (e.g. usePlanImplementation hardcodes local-agent)
🟡 MEDIUM src/components/ChatModeSelector.tsx Hard-coded English toast/tooltip strings bypass i18n
🟡 MEDIUM 4 files Optimistic-update + persist + rollback pattern duplicated 4× — needs a shared helper
🟡 MEDIUM src/hooks/useChatModeToggle.ts:74 localAgentUnavailableReason toast fires even when user voluntarily cycles away from local-agent
🟡 MEDIUM src/components/ChatModeSelector.tsx disabled={isPersisting} freezes trigger with no spinner — looks broken on slow IPC
🟢 Low Priority Notes (6 items)
  • Doc-comment typos / stale "REPLACES" list in useInitialChatMode.ts, chatModeUtils.ts, useChats.ts
  • useInitialChatMode hook called mid-component in ChatInput.tsx:598 — should be hoisted with other hooks
  • Disabled local-agent SelectItem in ChatModeSelector.tsx has no tooltip explaining why
  • updateChatMode handler lacks chat-ownership check (consistent with other handlers, but worth flagging)
  • queryClient imported in ChatPanel.tsx just for one invalidation — reuse invalidateChats() from useChats
  • Shadowing: local isBasicAgentMode const in local_agent_handler.ts collides with the function of the same name previously imported
🚫 Dropped False Positives (3 items)
  • Skeleton flicker on every settings change — Dropped: isLoadingSettings is !settings and settings are only nullish on first load; invalidations don't re-null.
  • chatId memo recreated every render from unstable search ref — Dropped: TanStack Router returns stable references for unchanged search state; no evidence the memo is actually churning.
  • Non-Pro users hit undefined initialChatMode window causing null chat_mode writes — Dropped: The write happens on first send, by which time quota/settings have resolved in practice.

Generated by Dyadbot multi-agent code review

cubic-dev-ai[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

@wwwillchen
Copy link
Copy Markdown
Collaborator

@BugBot run

@socket-security
Copy link
Copy Markdown

socket-security bot commented Apr 6, 2026

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Action Severity Alert  (click "▶" to expand/collapse)
Warn Critical
Critical CVE: Axios has a NO_PROXY Hostname Normalization Bypass Leads to SSRF

CVE: GHSA-3p68-rc4w-qgx5 Axios has a NO_PROXY Hostname Normalization Bypass Leads to SSRF (CRITICAL)

Affected versions: < 1.15.0

Patched version: 1.15.0

From: package-lock.jsonnpm/@neondatabase/api-client@2.2.0npm/axios@1.11.0

ℹ Read more on: This package | This alert | What is a critical CVE?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Remove or replace dependencies that include known critical CVEs. Consumers can use dependency overrides or npm audit fix --force to remove vulnerable dependencies.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/axios@1.11.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn Critical
Critical CVE: Axios has Unrestricted Cloud Metadata Exfiltration via Header Injection Chain

CVE: GHSA-fvcv-3m26-pcqx Axios has Unrestricted Cloud Metadata Exfiltration via Header Injection Chain (CRITICAL)

Affected versions: >= 1.0.0 < 1.15.0; < 0.31.0

Patched version: 1.15.0

From: package-lock.jsonnpm/@neondatabase/api-client@2.2.0npm/axios@1.11.0

ℹ Read more on: This package | This alert | What is a critical CVE?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Remove or replace dependencies that include known critical CVEs. Consumers can use dependency overrides or npm audit fix --force to remove vulnerable dependencies.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/axios@1.11.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
HTTP dependency: npm @electron/rebuild depends on https://github.com/electron/node-gyp#06b29aafb7708acef8b3669835c8a7857ebc92d2

Dependency: @electron/node-gyp@https://github.com/electron/node-gyp#06b29aafb7708acef8b3669835c8a7857ebc92d2

Location: Package overview

From: package-lock.jsonnpm/@electron-forge/maker-rpm@7.11.1npm/@electron-forge/plugin-auto-unpack-natives@7.11.1npm/@electron-forge/maker-deb@7.11.1npm/@electron-forge/publisher-github@7.11.1npm/@electron-forge/plugin-vite@7.11.1npm/@electron-forge/plugin-fuses@7.11.1npm/@electron-forge/maker-squirrel@7.11.1npm/@electron-forge/cli@7.11.1npm/@electron-forge/maker-zip@7.11.1npm/@electron/rebuild@3.7.2

ℹ Read more on: This package | This alert | What are http dependencies?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Publish the HTTP URL dependency to a public or private package repository and consume it from there.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@electron/rebuild@3.7.2. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

dyad-assistant[bot]

This comment was marked as resolved.

@dyad-assistant
Copy link
Copy Markdown
Contributor

dyad-assistant bot commented Apr 6, 2026

🔍 Dyadbot Code Review Summary

Verdict: 🤔 NOT SURE - Potential issues

Reviewed by 3 independent agents: Correctness Expert, Code Health Expert, UX Wizard. Most of the high-severity concerns raised in earlier review rounds (the race conditions around optimistic rollback, quota-banner switch ordering, legacy-migration imprint, and the chatModeUtils onError throw) are already tracked in existing unresolved comments — I did not re-flag them. A few new, smaller issues were found; none are clear merge blockers on their own, but the per-chat mode logic remains delicate enough that I'd land this once the existing HIGH comments are resolved.

New Issues Summary

Severity File Issue
🟡 MEDIUM src/hooks/useChatModeToggle.ts:61 Quota loading state treated as available, lets non-Pro users toggle into local-agent mid-load
🟡 MEDIUM src/hooks/useChatModeToggle.ts:92 Auto-cycle toast interpolates raw mode ids (build, local-agent) instead of display labels; tangled fallback logic
🟢 LOW src/hooks/useChatModeToggle.ts:26 chatId memoized as { id } wrapper object instead of a plain number | undefined
🚫 Dropped / Not re-flagged (already covered by earlier comments)
  • effectiveStreamMode prefers req.chatMode over DB — Covered by the existing chat_stream_handlers.ts:580 legacy-imprint comment.
  • ChatPanel restore effect overwrites selectedChatMode on every chatsList refetch — Covered by ChatPanel.tsx:193 existing HIGH race comment.
  • useSelectChat fallback persist race / trailing .catch() dead code — Covered by useSelectChat.ts:93/114.
  • Rollback in FreeAgentQuotaBanner dumps user back into unusable mode — Covered by ChatPanel.tsx:306.
  • ChatModeSelector freshness comparison speculative race — Dropped: the in-place chatId/path check plus isCancelled flag pattern already handles the realistic cases; further hardening would be speculative.
  • Hardcoded English strings for chat-mode toasts (i18n) — Dropped: the rest of the codebase also mixes raw strings and t(); this isn't a regression introduced here.
  • isChatModeAllowed default freeAgentQuotaAvailable = false footgun — Dropped: intentionally conservative default, all callers in this PR pass the value explicitly.
  • Inline "ask" | "build" | "local-agent" | "plan" union in ChatList/ChatSearchDialog/get_model_client — Dropped: nit-level consistency; worth cleaning up but not blocking.
  • SecurityPanel new chats use user default mode — Dropped: couldn't confirm the pre-existing behavior differed, and the PR's changes in this area look like unrelated cleanup rather than a regression.
  • ChatPanel mode-restore duplicates useSelectChat logic — Dropped: would be a nice refactor but not a correctness issue on its own; flagged separately in the existing HIGH race comment.

Generated by Dyadbot multi-agent code review

chatgpt-codex-connector[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

@wwwillchen
Copy link
Copy Markdown
Collaborator

@BugBot run

@dyad-assistant
Copy link
Copy Markdown
Contributor

dyad-assistant bot commented Apr 6, 2026

🔍 Dyadbot Code Review Summary

Verdict: 🤔 NOT SURE - Potential issues

Reviewed by 3 independent agents: Correctness Expert, Code Health Expert, UX Wizard. Findings below are new — issues already flagged by other reviewers (gemini-code-assist, dyad-assistant, cubic-dev-ai, chatgpt-codex, devin) were deduplicated.

🔴 HIGH

File Issue
src/hooks/useResolveMergeConflictsWithAI.ts Merge-conflict resolver may run in the wrong mode. The new chat uses useInitialChatMode() (user's current default). If that default is ask (read-only) or plan, or undefined while quota/settings load, the auto-generated prompt asks the model to rewrite conflict markers but no file-writing tools are available — conflicts stay unresolved silently. Hardcode build (or local-agent for Pro) for this automated task.
src/components/ChatPanel.tsx (deep-link restore effect) Mode chip flashes on cold deep-link. ChatPanel renders with the stale global settings.selectedChatMode, then async-fetches the chat and calls updateSettings. Users see the wrong chip (and any action taken in that window runs under the wrong mode). Restore from the cached chats query before first paint, or gate input on restore complete.

🟡 MEDIUM

File Issue
src/hooks/usePlanEvents.ts Plan-to-implementation chat can be silently downgraded from local-agent. selectChat({chatMode:'local-agent'}) is rewritten to the fallback by useSelectChat for non-Pro / quota-exceeded users, but usePlanImplementation still forces chatMode:'local-agent' on the stream — the stream then fails and the chat is left in an inconsistent state. Validate eligibility up front with a clear error.
src/components/ChatPanel.tsx (FreeAgentQuotaBanner retry) Retry becomes a dead end after a second failure. The Retry toast action duplicates the persistChatMode call inline, but its nested onPersistError shows a toast without a Retry action. Extract a single switchToBuildMode function so repeated failures keep offering retry.
src/hooks/useChatModeToggle.ts (keyboard shortcut path) Shortcut toggle has no visible feedback and uses toast.error for informational outcomes. ChatModeSelector shows a spinner while persisting, but shortcut-driven toggles don't — users see a silent delay, and rapid presses are swallowed by toggleInFlightRef with no hint. Also, "switched to Build" is fired via toast.error despite being a successful fallback — use toast.info.
src/hooks/useSelectChat.ts (fallback toast) Fallback toast hard-codes "Agent mode unavailable" regardless of which mode was actually disallowed. Key the message off the disallowed mode so future modes (plan/ask) don't produce misleading copy.
src/components/chat/ChatTabs.tsx Tab switching can spam fallback toasts. Every click on a tab whose stored mode is currently disallowed re-fires the fallback path in useSelectChat, producing a new toast and a new DB write. Debounce per-chatId per-session.
src/components/ChatModeSelector.tsx (disallowed select) Disallowed selection silently reverts. The handler returns after toast.error and never updates state, so the Radix Select just closes — no aria-live announcement for screen-reader users. Make the disabled state authoritative: align SelectItem's disabled with localAgentUnavailableMessage so the toast path is unreachable.
src/hooks/usePersistChatMode.ts Over-engineered options surface. The hook exposes optimistic, rollbackOnError, rollbackGuardOnRouteChange, previousMode, onPersistSuccess, onPersistError, onGuardedSuccess — but across all call sites rollbackOnError is always false, previousMode/rollbackGuardOnRouteChange are effectively dead, and optimistic:true is used only once. Drop dead parameters until a caller actually needs them.
src/components/ChatPanel.tsx vs. src/hooks/useSelectChat.ts Two independent mode-restoration paths. ChatPanel's cold deep-link path applies chat.chatMode without the eligibility validation / fallback logic useSelectChat runs. Funnel both through one helper to avoid divergence.
🟢 Low Priority Notes
  • src/ipc/handlers/chat_stream_handlers.tslegacyImprintMode !== null is a dead guard (the type is non-null by construction). Remove it and add a why-comment explaining that the imprint intentionally ignores req.chatMode.
  • src/hooks/useSelectChat.ts / src/hooks/useChatModeToggle.tsmodeLabels map is duplicated. Extract a shared getChatModeLabel helper in src/lib/chatModeUtils.ts.
  • src/hooks/useChatModeToggle.ts — Hard-coded English strings ('Failed to save chat mode to database', etc.) while ChatModeSelector was migrated to t() in this same PR.
  • src/hooks/useSelectChat.ts"database error" in the user-facing toast is jargony; prefer "Couldn't save chat mode. Please try again." with a Retry action.
  • src/components/ChatModeSelector.tsx'{{count}} MCP server{{suffix}} enabled' with suffix = 's'/'' is not real i18n pluralization; use i18next _one/_other keys.
  • src/components/ChatModeSelector.tsx — Skeleton chip h-6 w-16 is narrower than the real chip with translated labels, causing layout shift on settings load.
  • src/hooks/useChats.ts — Garbled comment on invalidateChats contradicts the new return-the-promise behavior.
  • src/hooks/useInitialChatMode.ts — The REPLACES ... JSDoc line is a historical note that will rot; remove it.
  • src/lib/chatModeUtils.tspersistChatModeToDb wrapper is a thin layer between usePersistChatMode and the IPC call; the double indirection doesn't add reuse. Inline one of the layers.
🚫 Dropped / Duplicates
  • useSelectChat fallback not guarded against chat switch — Already covered by existing dyad-assistant / cubic-dev-ai comments.
  • chat_stream_handlers legacy imprint stamps stale settings — Overlaps with the existing "Legacy migration imprints transient req.chatMode" thread.
  • localAgentUnavailableReason nested ternaries — Overlaps with the existing "microcopy / complexity" comment on the auto-cycle block.
  • inputSchema as any casts in chat_stream_handlers — Unrelated to this PR's core changes.

Generated by Dyadbot multi-agent code review

cubic-dev-ai[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

@github-actions github-actions bot added the needs-human:review-issue ai agent flagged an issue that requires human review label Apr 6, 2026
@nourzakhama2003 nourzakhama2003 marked this pull request as draft April 6, 2026 16:44
Copy link
Copy Markdown
Contributor

@dyad-assistant dyad-assistant bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Multi-agent review: 2 new issues found (1 HIGH, 1 MEDIUM). See summary comment for details.

Comment on lines +240 to +247
(effectiveChatMode !== "local-agent" && effectiveChatMode !== undefined) ||
(settings?.selectedChatMode !== "local-agent" &&
effectiveChatMode === undefined &&
lastMessage?.role === "assistant" &&
!lastMessage.approvalState &&
!!proposal &&
proposal.type === "code-proposal" &&
messageId === lastMessage.id);
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.

🔴 HIGH | correctness / regression

disableSendButton unconditionally disables send for all non-agent modes

The new first OR clause (effectiveChatMode !== "local-agent" && effectiveChatMode !== undefined) forces disableSendButton = true whenever effectiveChatMode is anything other than "local-agent". Because ChatPanel.tsx:59-60 always resolves effectiveChatMode = currentChat?.chatMode ?? settings?.selectedChatMode ?? "build" (never undefined), this first clause is true for every build / ask / plan chat, which blocks the send button entirely. The second OR clause (with the proposal checks) is only reachable when effectiveChatMode === undefined — which never happens from ChatPanel.

The original pre-PR logic only blocked sends when there was an unapproved assistant code proposal. This PR's attempt to fix an earlier review comment introduced a full regression: users cannot send any message in Build, Ask, or Plan mode.

💡 Suggestion: Replace with a single proposal-gated check that uses the effective mode:

const resolvedMode = effectiveChatMode ?? settings?.selectedChatMode;
const disableSendButton =
  resolvedMode !== "local-agent" &&
  lastMessage?.role === "assistant" &&
  !lastMessage.approvalState &&
  !!proposal &&
  proposal.type === "code-proposal" &&
  messageId === lastMessage.id;

Comment on lines 152 to +170
<span className="text-sm">{messages.length} Queued</span>
<span className="text-xs text-muted-foreground">- {statusText}</span>
{!isPaused && (
<span className="text-xs text-muted-foreground">
- {statusText}
</span>
)}
{isPaused && (
<span className="text-xs px-2 py-0.5 rounded bg-yellow-500/20 text-yellow-700 dark:text-yellow-400 font-medium">
Paused
</span>
)}
</div>
<div className="flex items-center gap-2 flex-shrink-0 ml-3">
{isExpanded ? (
<ChevronUp className="w-4 h-4 text-muted-foreground" />
) : (
<ChevronDown className="w-4 h-4 text-muted-foreground" />
)}
<button
type="button"
onClick={isPaused ? onResumeQueue : onPauseQueue}
aria-label={isPaused ? "Resume queue" : "Pause queue"}
title={isPaused ? "Resume queue" : "Pause queue"}
className={cn(
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.

🟡 MEDIUM | i18n

Queue header introduces several hard-coded English strings

The new queue/pause UI adds user-visible English strings that are not translated: statusText variants ("will send after a successful response", "will send after current response", "ready to send") at L122-126; the "{n} Queued" label at L152; the "Paused" badge at L160; and the button aria-label / title values "Pause queue" / "Resume queue" at L168-169 and "Collapse" / "Expand" at L149 and L183. Non-English users (pt-BR, zh-CN) will see mixed-language UI every time the queue renders, and screen-reader labels remain English-only.

💡 Suggestion: Move these strings into chat.json under a chatMode.queue.* (or similar) namespace and translate into pt-BR/zh-CN, matching the pattern used elsewhere in this file/PR.

@dyad-assistant
Copy link
Copy Markdown
Contributor

🔍 Dyadbot Code Review Summary

Verdict: ⛔ NO - Do NOT merge

Reviewed by 3 independent agents: Correctness Expert, Code Health Expert, UX Wizard. Validated against 279 existing inline comments and 117 issue comments from prior review rounds.

Issues Summary

Severity File Issue
🔴 HIGH src/components/chat/ChatInput.tsx:239-247 disableSendButton unconditionally disables send for all non-agent modes (regression)
🟡 MEDIUM src/components/chat/QueuedMessagesList.tsx:122-183 Queue header introduces hard-coded English strings (Queued, Paused, statusText, aria-labels)

The HIGH issue is a blocker: the refactor from settings?.selectedChatMode to effectiveChatMode introduced an unconditional disableSendButton = true branch. Because effectiveChatMode is always defined (fallback ?? "build" in ChatPanel), users cannot send messages in Build, Ask, or Plan modes.

🟢 Low Priority Notes (6 items)
  • useCallback calls nested inside returned object literalsrc/hooks/useStreamChat.ts:539-578. Works today, but mixing hook calls into an object literal obscures hook ordering and is fragile under future edits. Extract each callback to a top-level const before returning.
  • clearPauseOnly and resumeQueue duplicate the same pause-clearing state updatesrc/hooks/useStreamChat.ts:547-570. Have resumeQueue delegate to clearPauseOnly.
  • Mode display span is aria-hidden="true" unconditionallysrc/components/ChatModeSelector.tsx:232. The only non-hidden content is the sr-only "Saving..." string that appears during isPersisting. Screen-reader users rely on the trigger's own aria-label to learn the selected mode; consider keeping the span readable or ensuring SelectTrigger announces the mode name.
  • Queue header has two focus stops for the same toggle actionsrc/components/chat/QueuedMessagesList.tsx:138-190. The role="button" div on the left and the chevron <button> both toggle isExpanded. Keyboard users tab through both for one action.
  • modeLabels object is not exhaustive over ChatModeSchemasrc/hooks/useChatModeToggle.ts:121-128. TypeScript won't catch a missing key if a new mode is added (type is inferred, not Record<ChatMode, string>). A new mode would produce Agent mode unavailable — switched to undefined toasts. Prefer the existing getChatModeLabelKey helper (which is exhaustive).
  • Em-dash vs hyphen inconsistency across localessrc/i18n/locales/pt-BR/chat.json, src/i18n/locales/zh-CN/chat.json. en uses in modeUnavailableFallback/restoreModeTimedOut; pt-BR/zh-CN mix and - across the same keys. Normalize for visual consistency.
🚫 Dropped False Positives / Duplicates (10 items)
  • sameRoute computed from stale getCurrentChatIdFromRoute()src/hooks/usePersistChatMode.ts:57. Dropped: already flagged by chatgpt-codex-connector[bot] ("Re-evaluate active route after mode persistence").
  • lastRestoredChatIdRef set before persist resolvessrc/hooks/useRestoreChatMode.ts:144. Dropped: already flagged by dyad-assistant[bot] / cubic-dev-ai[bot] in prior rounds.
  • resolveAllowedChatMode hardcoded "build" fallback without validationsrc/lib/chatModeUtils.ts:63. Dropped: already flagged by dyad-assistant[bot].
  • Queue processor dispatches without explicit chatModesrc/hooks/useQueueProcessor.ts:75. Dropped: overlaps with existing chat_stream_handlers.ts comments about legacy imprint from req.chatMode.
  • Summarize button uses wrong chatModeLoading i18n keysrc/components/chat/SummarizeInNewChatButton.tsx:27. Dropped: already flagged by dyad-assistant[bot] ("Wrong i18n key path — translation is bypassed").
  • Mode tooltip includes the mode name that's already visiblesrc/components/ChatModeSelector.tsx:247. Dropped: stylistic, not a correctness issue.
  • Local-agent badge can grow long with MCP+quota infosrc/components/ChatModeSelector.tsx:277-293. Dropped: speculative layout concern without observed wrap/overflow.
  • Restoring-chat-mode banner causes layout shiftsrc/components/ChatPanel.tsx:291. Dropped: already flagged by dyad-assistant[bot] in prior round.
  • Restore-timeout toast can show stale mode namesrc/hooks/useRestoreChatMode.ts:82-88. Dropped: adjacent L91 comment already flags the timeout toast; the stale-mode angle is speculative.
  • ChatHeader.handleNewChat silently falls back to Build when initial mode is loadingsrc/components/chat/ChatHeader.tsx:95. Dropped: already flagged by dyad-assistant[bot] ("Three different fallback patterns for initialChatMode").

Generated by Dyadbot multi-agent code review

…ion, and UX fixes

- Wait for quota readiness before picking conflict chat mode to prevent quota errors
- Set lastRestoredChatIdRef for legacy chats to prevent repeated IPC calls
- Pass isProEnabled to getChatModeLabelKey so non-Pro users see correct label
- Remove dead persistTimeoutRef and cleanup effect from ChatModeSelector
- Revert all E2E snapshot changes to main branch state
- Remove resolve-git-conflict.ts fixture file

This commit removes unrelated changes from the chat mode persistence PR,
keeping only the core functionality.
- Revert getEffectiveDefaultChatMode to allow non-pro users with quota to use local-agent mode (as requested by maintainer)
- Remove chat mode selection complexity from useResolveMergeConflictsWithAI (as requested by maintainer)
- E2E snapshot changes already reverted to main in previous commit
@github-actions
Copy link
Copy Markdown
Contributor

🎭 Playwright Test Results

❌ Some tests failed

OS Passed Failed Flaky Skipped
🍎 macOS 307 94 6 134
🪟 Windows 306 40 5 134

Summary: 613 passed, 134 failed, 11 flaky, 268 skipped

Failed Tests

🍎 macOS

Show all 94 failures
  • annotator.spec.ts > annotator - capture and submit screenshot
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • astro.spec.ts > astro
    • Error: expect(string).toMatchSnapshot(expected) failed
  • attach_image.spec.ts > attach image - chat
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • attach_image.spec.ts > attach image - chat - upload to codebase
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • attach_image.spec.ts > attach image via drag - chat
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • cancelled_message.spec.ts > cancelled message shows cancelled indicator and is excluded from context
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • chat_history.spec.ts > should open, navigate, and select from history menu
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • chat_image_generation.spec.ts > generate image from chat - full flow
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • chat_input.spec.ts > send button disabled during pending proposal
    • Error: expect(locator).toBeEnabled() failed
  • chat_input.spec.ts > send button disabled during pending proposal - reject
    • Error: expect(locator).toBeVisible() failed
  • chat_mode.spec.ts > chat mode selector - default build mode
    • Error: expect(string).toMatchSnapshot(expected) failed
  • chat_mode.spec.ts > chat mode selector - ask mode
    • Error: expect(locator).toBeVisible() failed
  • context_compaction.spec.ts > local-agent - context compaction triggers and shows summary
    • Error: expect(locator).toBeVisible() failed
  • context_compaction.spec.ts > local-agent - context compaction can run mid-turn
    • Error: expect(locator).toBeVisible() failed
  • context_manage.spec.ts > manage context - default
    • Error: expect(received).toEqual(expected) // deep equality
  • context_manage.spec.ts > manage context - smart context
    • Error: expect(received).toBe(expected) // Object.is equality
  • context_manage.spec.ts > manage context - smart context - auto-includes only
    • Error: expect(received).toBe(expected) // Object.is equality
  • context_manage.spec.ts > manage context - exclude paths
    • Error: expect(received).toEqual(expected) // deep equality
  • context_manage.spec.ts > manage context - exclude paths with smart context
    • Error: expect(received).toBe(expected) // Object.is equality
  • context_window.spec.ts > context window
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • dyad_tags_parsing.spec.ts > dyad tags handles nested < tags
    • Error: expect(string).toMatchSnapshot(expected) failed
  • engine.spec.ts > send message to engine
    • Error: expect(string).toMatchSnapshot(expected) failed
  • engine.spec.ts > send message to engine - openai gpt-5
    • Error: expect(string).toMatchSnapshot(expected) failed
  • engine.spec.ts > send message to engine - anthropic claude sonnet 4
    • Error: expect(string).toMatchSnapshot(expected) failed
  • engine.spec.ts > smart auto should send message to engine
    • Error: expect(string).toMatchSnapshot(expected) failed
  • fix_error.spec.ts > fix error with AI
    • Error: expect(locator).toMatchAriaSnapshot(expected) failed
  • free_agent_quota.spec.ts > free agent quota - full flow: mode availability, quota tracking, exceeded banner, switch to build
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • free_agent_quota.spec.ts > free agent quota - quota resets after 24 hours
    • Error: expect(locator).toBeVisible() failed
  • github.spec.ts > github clear integration settings
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • import.spec.ts > import app with AI rules
    • Error: expect(string).toMatchSnapshot(expected) failed
  • local_agent_advanced.spec.ts > local-agent - mcp tool call
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • local_agent_ask.spec.ts > local-agent ask mode
    • Error: expect(locator).toBeVisible() failed
  • local_agent_basic.spec.ts > local-agent - read then edit
    • Error: expect(locator).toBeVisible() failed
  • local_agent_basic.spec.ts > local-agent - parallel tool calls
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • local_agent_connection_retry.spec.ts > local-agent - recovers from connection drop
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • local_agent_consent.spec.ts > local-agent - add_dependency consent: always allow
    • Error: expect(locator).toBeVisible() failed
  • local_agent_consent.spec.ts > local-agent - add_dependency consent: allow once
    • Error: expect(locator).toBeVisible() failed
  • local_agent_consent.spec.ts > local-agent - add_dependency consent: decline
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • local_agent_generate_image.spec.ts > local-agent - generate image
    • Error: expect(locator).toBeVisible() failed
  • local_agent_grep.spec.ts > local-agent - grep search
    • Error: expect(locator).toBeVisible() failed
  • local_agent_list_files.spec.ts > local-agent - list_files
    • Error: expect(locator).toBeVisible() failed
  • local_agent_list_files.spec.ts > local-agent - list_files include_hidden
    • Error: expect(locator).toBeVisible() failed
  • local_agent_persistent_todos.spec.ts > local-agent - persistent todos across turns
    • Error: expect(locator).toBeVisible() failed
  • local_agent_run_type_checks.spec.ts > local-agent - run_type_checks updates problems panel
    • Error: expect(locator).toBeVisible() failed
  • local_agent_search_replace.spec.ts > local-agent - search_replace edit
    • Error: expect(locator).toBeVisible() failed
  • local_agent_step_limit.spec.ts > local-agent - step limit pause
    • Error: expect(locator).toBeVisible() failed
  • local_agent_summarize.spec.ts > local-agent - summarize to new chat works
    • Error: expect(locator).toBeVisible() failed
  • local_agent_web_fetch.spec.ts > local-agent - web fetch
    • Error: expect(locator).toBeVisible() failed
  • mcp.spec.ts > mcp - call calculator
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • mcp.spec.ts > mcp - call calculator via http
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • mention_files.spec.ts > reference file from editor file tree
    • Error: expect(string).toMatchSnapshot(expected) failed
  • partial_response.spec.ts > partial message is resumed
    • Error: No dump path found in messages list
  • pause_queue.spec.ts > pause queue > pause queue prevents dequeuing after current stream completes
    • Error: expect(locator).toContainText(expected) failed
  • pause_queue.spec.ts > pause queue > stopping while paused keeps queue and resume starts sending
    • Error: expect(locator).toContainText(expected) failed
  • pause_queue.spec.ts > pause queue > sending while stopped with paused queue sends immediately and keeps queue
    • Error: expect(locator).toContainText(expected) failed
  • plan_mode.spec.ts > plan mode - accept plan redirects to new chat and saves to disk
    • Error: expect(locator).toBeVisible() failed
  • plan_mode.spec.ts > plan mode - questionnaire flow
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • plan_mode.spec.ts > plan mode - add and review plan annotations
    • Error: locator.click: Element is not visible
  • plan_mode.spec.ts > plan mode - view plan button opens preview panel when collapsed
    • Error: expect(locator).toBeVisible() failed
  • problems.spec.ts > problems auto-fix - enabled
    • Error: No dump path found in messages list
  • problems.spec.ts > problems auto-fix - gives up after 2 attempts
    • Error: No dump path found in messages list
  • problems.spec.ts > problems auto-fix - complex delete-rename-write
    • Error: No dump path found in messages list
  • problems.spec.ts > problems - fix all
    • Error: expect(locator).toMatchAriaSnapshot(expected) failed
  • queued_message.spec.ts > editing queued message restores attachments and selected components
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • queued_message.spec.ts > canceling queued message edit clears restored components
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • queued_message.spec.ts > queued messages > gets added and sent after stream completes
    • Error: expect(locator).toBeVisible() failed
  • queued_message.spec.ts > queued messages > can be reordered, deleted, and edited
    • Error: expect(locator).toBeVisible() failed
  • queued_message.spec.ts > queued messages > fires queued message while on another page
    • Error: expect(locator).toBeVisible() failed
  • rename_edit.spec.ts > rename then edit works
    • Error: expect(string).toMatchSnapshot(expected) failed
  • security_review.spec.ts > security review
    • Error: expect(string).toMatchSnapshot(expected) failed
  • security_review.spec.ts > security review - edit and use knowledge
    • Error: expect(string).toMatchSnapshot(expected) failed
  • security_review.spec.ts > security review - multi-select and fix issues
    • Error: expect(locator).toMatchAriaSnapshot(expected) failed
  • select_component.spec.ts > select component
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • select_component.spec.ts > select multiple components
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • select_component.spec.ts > deselect component
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • select_component.spec.ts > select component next.js
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • smart_context_deep.spec.ts > smart context deep - read write read
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • socket_firewall.spec.ts > build mode - safe npm package installs through the real socket firewall path
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • socket_firewall.spec.ts > build mode - blocked unsafe npm package shows the real socket verdict and preserves app files
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • supabase_client.spec.ts > supabase client is generated
    • Error: expect(string).toMatchSnapshot(expected) failed
  • supabase_migrations.spec.ts > supabase migrations
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • supabase_migrations.spec.ts > supabase migrations with native git
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • telemetry.spec.ts > telemetry - accept
    • Error: expect(string).toMatchSnapshot(expected) failed
  • telemetry.spec.ts > telemetry - reject
    • Error: expect(string).toMatchSnapshot(expected) failed
  • telemetry.spec.ts > telemetry - later
    • Error: expect(string).toMatchSnapshot(expected) failed
  • thinking_budget.spec.ts > thinking budget
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • turbo_edits_v2.spec.ts > turbo edits v2 - search-replace dump
    • Error: expect(string).toMatchSnapshot(expected) failed
  • turbo_edits_v2.spec.ts > turbo edits v2 - search-replace fallback
    • Error: expect(string).toMatchSnapshot(expected) failed
  • undo.spec.ts > undo
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • undo.spec.ts > undo with native git
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • undo.spec.ts > undo after assistant with no code
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • version_integrity.spec.ts > version integrity (git isomorphic)
    • Error: expect(string).toMatchSnapshot(expected) failed
  • version_integrity.spec.ts > version integrity (git native)
    • Error: expect(string).toMatchSnapshot(expected) failed
  • visual_editing.spec.ts > swap image via URL
    • Error: expect(locator).toBeVisible() failed

🪟 Windows

Show all 40 failures
  • astro.spec.ts > astro
    • Error: expect(string).toMatchSnapshot(expected) failed
  • attach_image.spec.ts > attach image - chat
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • attach_image.spec.ts > attach image - chat - upload to codebase
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • attach_image.spec.ts > attach image via drag - chat
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • cancelled_message.spec.ts > cancelled message shows cancelled indicator and is excluded from context
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • chat_history.spec.ts > should open, navigate, and select from history menu
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • chat_image_generation.spec.ts > generate image from chat - full flow
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • chat_input.spec.ts > send button disabled during pending proposal
    • Error: expect(locator).toBeEnabled() failed
  • chat_input.spec.ts > send button disabled during pending proposal - reject
    • Error: expect(locator).toBeVisible() failed
  • chat_mode.spec.ts > chat mode selector - default build mode
    • Error: expect(string).toMatchSnapshot(expected) failed
  • chat_mode.spec.ts > chat mode selector - ask mode
    • Error: expect(locator).toBeVisible() failed
  • chat_tabs.spec.ts > right-click context menu: Close tabs to the right
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • context_limit_banner.spec.ts > context limit banner shows 'running out' when near context limit
    • Error: expect(locator).toBeVisible() failed
  • context_manage.spec.ts > manage context - default
    • Error: expect(received).toEqual(expected) // deep equality
  • context_manage.spec.ts > manage context - smart context
    • Error: expect(received).toBe(expected) // Object.is equality
  • context_manage.spec.ts > manage context - smart context - auto-includes only
    • Error: expect(received).toBe(expected) // Object.is equality
  • context_manage.spec.ts > manage context - exclude paths
    • Error: expect(received).toEqual(expected) // deep equality
  • context_manage.spec.ts > manage context - exclude paths with smart context
    • Error: expect(received).toBe(expected) // Object.is equality
  • edit_code.spec.ts > edit code edits the right file during rapid switches
    • Error: expect(received).toEqual(expected) // deep equality
  • github.spec.ts > create and sync to new repo
    • TimeoutError: page.waitForSelector: Timeout 5000ms exceeded.
  • github.spec.ts > create repo with spaces in name - should normalize to hyphens
    • TimeoutError: page.waitForSelector: Timeout 5000ms exceeded.
  • github.spec.ts > create and sync to existing repo
    • Error: expect(locator).toMatchAriaSnapshot(expected) failed
  • github.spec.ts > create and sync to existing repo - custom branch
    • Error: expect(locator).toMatchAriaSnapshot(expected) failed
  • github.spec.ts > github clear integration settings
    • Error: expect(locator).toBeVisible() failed
  • mention_files.spec.ts > reference file from editor file tree
    • Error: expect(string).toMatchSnapshot(expected) failed
  • partial_response.spec.ts > partial message is resumed
    • Error: No dump path found in messages list
  • pause_queue.spec.ts > pause queue > pause queue prevents dequeuing after current stream completes
    • Error: expect(locator).toContainText(expected) failed
  • pause_queue.spec.ts > pause queue > stopping while paused keeps queue and resume starts sending
    • Error: expect(locator).toContainText(expected) failed
  • pause_queue.spec.ts > pause queue > sending while stopped with paused queue sends immediately and keeps queue
    • Error: expect(locator).toContainText(expected) failed
  • problems.spec.ts > problems auto-fix - enabled
    • Error: No dump path found in messages list
  • problems.spec.ts > problems auto-fix - gives up after 2 attempts
    • Error: No dump path found in messages list
  • problems.spec.ts > problems auto-fix - complex delete-rename-write
    • Error: No dump path found in messages list
  • queued_message.spec.ts > queued messages > gets added and sent after stream completes
    • Error: expect(locator).toBeVisible() failed
  • queued_message.spec.ts > queued messages > can be reordered, deleted, and edited
    • Error: expect(locator).toBeVisible() failed
  • queued_message.spec.ts > queued messages > fires queued message while on another page
    • Error: expect(locator).toBeVisible() failed
  • rename_edit.spec.ts > rename then edit works
    • Error: expect(string).toMatchSnapshot(expected) failed
  • security_review.spec.ts > security review - multi-select and fix issues
    • Error: expect(locator).toMatchAriaSnapshot(expected) failed
  • telemetry.spec.ts > telemetry - accept
    • Error: expect(string).toMatchSnapshot(expected) failed
  • telemetry.spec.ts > telemetry - reject
    • Error: expect(string).toMatchSnapshot(expected) failed
  • telemetry.spec.ts > telemetry - later
    • Error: expect(string).toMatchSnapshot(expected) failed

📋 Re-run Failing Tests (macOS)

Copy and paste to re-run all failing spec files locally:

npm run e2e \
  e2e-tests/annotator.spec.ts \
  e2e-tests/astro.spec.ts \
  e2e-tests/attach_image.spec.ts \
  e2e-tests/cancelled_message.spec.ts \
  e2e-tests/chat_history.spec.ts \
  e2e-tests/chat_image_generation.spec.ts \
  e2e-tests/chat_input.spec.ts \
  e2e-tests/chat_mode.spec.ts \
  e2e-tests/context_compaction.spec.ts \
  e2e-tests/context_manage.spec.ts \
  e2e-tests/context_window.spec.ts \
  e2e-tests/dyad_tags_parsing.spec.ts \
  e2e-tests/engine.spec.ts \
  e2e-tests/fix_error.spec.ts \
  e2e-tests/free_agent_quota.spec.ts \
  e2e-tests/github.spec.ts \
  e2e-tests/import.spec.ts \
  e2e-tests/local_agent_advanced.spec.ts \
  e2e-tests/local_agent_ask.spec.ts \
  e2e-tests/local_agent_basic.spec.ts \
  e2e-tests/local_agent_connection_retry.spec.ts \
  e2e-tests/local_agent_consent.spec.ts \
  e2e-tests/local_agent_generate_image.spec.ts \
  e2e-tests/local_agent_grep.spec.ts \
  e2e-tests/local_agent_list_files.spec.ts \
  e2e-tests/local_agent_persistent_todos.spec.ts \
  e2e-tests/local_agent_run_type_checks.spec.ts \
  e2e-tests/local_agent_search_replace.spec.ts \
  e2e-tests/local_agent_step_limit.spec.ts \
  e2e-tests/local_agent_summarize.spec.ts \
  e2e-tests/local_agent_web_fetch.spec.ts \
  e2e-tests/mcp.spec.ts \
  e2e-tests/mention_files.spec.ts \
  e2e-tests/partial_response.spec.ts \
  e2e-tests/pause_queue.spec.ts \
  e2e-tests/plan_mode.spec.ts \
  e2e-tests/problems.spec.ts \
  e2e-tests/queued_message.spec.ts \
  e2e-tests/rename_edit.spec.ts \
  e2e-tests/security_review.spec.ts \
  e2e-tests/select_component.spec.ts \
  e2e-tests/smart_context_deep.spec.ts \
  e2e-tests/socket_firewall.spec.ts \
  e2e-tests/supabase_client.spec.ts \
  e2e-tests/supabase_migrations.spec.ts \
  e2e-tests/telemetry.spec.ts \
  e2e-tests/thinking_budget.spec.ts \
  e2e-tests/turbo_edits_v2.spec.ts \
  e2e-tests/undo.spec.ts \
  e2e-tests/version_integrity.spec.ts \
  e2e-tests/visual_editing.spec.ts

⚠️ Flaky Tests

🍎 macOS

  • git_collaboration.spec.ts > Git Collaboration > should create, switch, rename, merge, and delete branches (passed after 1 retry)
  • local_agent_advanced.spec.ts > local-agent - security review fix (passed after 1 retry)
  • local_agent_basic.spec.ts > local-agent - dump request (passed after 2 retries)
  • local_agent_code_search.spec.ts > local-agent - code search (passed after 1 retry)
  • local_agent_connection_retry.spec.ts > local-agent - recovers when drop happens after tool-call stream (passed after 1 retry)
  • local_agent_read_logs.spec.ts > local-agent - read logs with filters (passed after 2 retries)

🪟 Windows

  • chat_tabs.spec.ts > tabs appear after navigating between chats (passed after 2 retries)
  • chat_tabs.spec.ts > closing a tab removes it and selects adjacent tab (passed after 2 retries)
  • edit_code.spec.ts > edit code (passed after 1 retry)
  • git_collaboration.spec.ts > Git Collaboration > should create, switch, rename, merge, and delete branches (passed after 1 retry)
  • github.spec.ts > should connect to GitHub using device flow (passed after 2 retries)

📊 View full report

@wwwillchen
Copy link
Copy Markdown
Collaborator

@BugBot run

@socket-security
Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​modelcontextprotocol/​sdk@​1.18.1997010099100
Added@​storybook/​react-vite@​8.6.159910066100100
Added@​types/​glob@​8.1.01001007081100
Added@​types/​uuid@​10.0.01001007081100
Updated@​typescript-eslint/​parser@​8.30.1 ⏵ 5.62.0100 +11007098100
Added@​electron-forge/​cli@​7.11.1991007095100
Added@​electron-forge/​plugin-auto-unpack-natives@​7.11.11001007196100
Added@​typescript/​native-preview@​7.0.0-dev.20260107.110010071100100
Added@​electron-forge/​maker-zip@​7.11.11001007196100
Added@​types/​better-sqlite3@​7.6.131001007180100
Added@​tailwindcss/​vite@​4.1.131001007298100
Added@​ai-sdk/​openai@​3.0.36721008898100
Added@​types/​node-fetch@​2.6.131001007381100
Added@​flakiness/​playwright@​1.0.0731009993100
Added@​storybook/​react@​8.6.159310073100100
Added@​electron-forge/​maker-squirrel@​7.11.11001007496100
Added@​ai-sdk/​mcp@​1.0.18991007498100
Added@​ai-sdk/​azure@​3.0.26741008398100
Added@​electron-forge/​maker-rpm@​7.11.11001007496100
Updated@​types/​react-dom@​19.2.3 ⏵ 19.1.9100 +11007587 +1100
Added@​electron-forge/​maker-deb@​7.11.11001007596100
Added@​types/​fs-extra@​11.0.41001007580100
Added@​ai-sdk/​provider-utils@​4.0.13931007698100
Added@​vscode/​ripgrep@​1.17.0911007789100
Added@​electron-forge/​publisher-github@​7.11.1971007796100
Added@​ai-sdk/​anthropic@​3.0.35781008798100
Added@​storybook/​blocks@​8.6.149210078100100
Added@​electron-forge/​plugin-fuses@​7.11.11001007896100
Updated@​typescript-eslint/​eslint-plugin@​8.30.1 ⏵ 5.62.095 -410079 -198100
Addedbabel-plugin-react-compiler@​1.0.01001007997100
Added@​ai-sdk/​google@​3.0.20791008798100
Added@​types/​kill-port@​2.0.3971008379100
Updated@​types/​react@​19.2.8 ⏵ 19.1.12100 +110080 +192 -2100
See 27 more rows in the dashboard

View full report

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fd97e9ed55

ℹ️ About Codex in GitHub

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

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

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

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

Comment on lines +173 to +178
if (!appId) {
console.warn(
`Skipping chat mode persist for chat ${chatId}: appId not available`,
);
// Still apply the resolved mode to settings even without DB persistence
shouldUpdateSelectedChatMode = true;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reset restoring state in no-appId fallback path

When a disallowed mode is restored on a deep-linked chat (appId is null), this branch skips persistence and sets shouldUpdateSelectedChatMode, but never clears isRestoringMode. If the banner was shown (e.g., cache miss + slow getChat), the "Restoring chat mode..." indicator can remain visible indefinitely for that chat. Please clear restoring state in the !appId fallback path as well.

Useful? React with 👍 / 👎.

@dyad-assistant
Copy link
Copy Markdown
Contributor

🔍 Dyadbot Code Review Summary

Verdict: 🤔 NOT SURE — a handful of net-new issues worth addressing

Reviewed by 3 independent agents: Correctness Expert, Code Health Expert, UX Wizard. Files were shuffled per reviewer to reduce ordering bias.

This PR has 284+ prior inline review comments across many rounds. Agents were instructed to deduplicate aggressively against existing feedback. The issues below are net-new — not previously flagged — and each was validated against the current source.

New Issues

Severity File Issue
🔴 HIGH src/hooks/useResolveMergeConflictsWithAI.ts:57 createChat/streamMessage called without chatMode — users in Ask/Plan mode silently fail to resolve conflicts
🟡 MEDIUM src/lib/schemas.ts:504 isBasicAgentMode export is now dead code after all imports removed
🟡 MEDIUM src/hooks/useRestoreChatMode.ts:69 Restore timeout branch doesn't set lastRestoredChatIdRef, so effect re-runs can retry restore + show duplicate toast for same chat
🟡 MEDIUM src/hooks/usePlanEvents.ts:135 planAgentUnavailable defaultValue in code differs from all 3 locale files — translations and developer-visible copy drift
🟡 MEDIUM src/components/chat/ChatInput.tsx:502 "We've switched you to a new chat for a clean context" is hard-coded English even though switchedToNewChatCleanContext i18n key already exists in en/pt-BR/zh-CN
🟡 MEDIUM src/components/preview_panel/SecurityPanel.tsx:796 Security-review warning toast always says "Agent" even for non-Pro users (selector labels the same mode "Basic Agent")
🟡 MEDIUM src/hooks/useSelectChat.ts:80 resolvedMode.usedFallback === true is silently applied with no toast — inconsistent with useRestoreChatMode, which does show modeUnavailableFallback for the same scenario
🟡 MEDIUM src/components/ChatModeSelector.tsx:340 Dropdown ModeOption renders every icon with text-foreground/80 — Ask/Plan lose the purple/blue color coding the trigger uses, so trigger and dropdown disagree
🟢 Low Priority Notes (3 items)
  • Orphaned comment fragmentsrc/components/ChatPanel.tsx:188. The comment "but only if the user was following (at bottom) during the stream." floats above useEffect with no preceding sentence.
  • Disabled Summarize button has no explanationsrc/components/chat/ContextLimitBanner.tsx:60. When isChatModeLoading is true the button disables but tooltip still says "Summarize to new chat".
  • ModeOption weakly-typed t: any + redundant content varsrc/components/ChatModeSelector.tsx:317,330. ModeOption could call useTranslation('chat') itself instead of accepting t: any; the content variable is assigned then immediately returned.
🚫 Dropped False Positives / Duplicates (5 items)
  • disableSendButton regression — already flagged multiple times at ChatInput.tsx:241/247.
  • SummarizeInNewChatButton wrong i18n key chatModeLoading — already flagged.
  • Home submit creates chat without initialChatMode — already flagged around home.tsx:169-174.
  • needsFreshPlanChat creates chat without initialChatMode — stream handler's legacy-imprint path writes selectedChatMode (which is "plan" here) on first send, so this works correctly in practice.
  • Restore timeout can fire toast while banner visible — overlaps with the lastRestoredChatIdRef issue and existing comments about timeout UX; collapsed.

Generated by Dyadbot multi-agent code review

Copy link
Copy Markdown
Contributor

@dyad-assistant dyad-assistant bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Multi-agent review: 8 issue(s) flagged (1 HIGH, 7 MEDIUM). See summary comment for the full breakdown including dropped false positives.

const newChatId = await ipc.chat.createChat(appId);
const newChatId = await ipc.chat.createChat({
appId,
initialChatMode: undefined,
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.

🔴 HIGH | interaction

Merge-conflict resolution silently breaks in Ask/Plan mode

createChat is called with initialChatMode: undefined, so the newly-created resolution chat inherits the user's global selectedChatMode (e.g. ask or plan). Further down, chatStream.start is called without a chatMode override, so the stream also runs in whatever mode the user has set globally. In Ask mode the model cannot edit files, and in Plan mode it only proposes changes — neither can actually resolve the merge conflict.

Users whose default mode is Ask or Plan will click the resolve button and get an apparently-successful response that doesn't touch the files on disk, with no indication of why.

💡 Suggestion: Pass initialChatMode: "build" (or "local-agent" where appropriate) to createChat, and pass a matching chatMode to chatStream.start, so the resolution chat always runs in a mode that can edit files regardless of the user's global preference.

Comment thread src/lib/schemas.ts
if (!freeAgentQuotaAvailable) return false;
// For non-Pro users, local-agent is only allowed when OpenAI or Anthropic
// is configured; otherwise basic quota should not enable the agent mode.
return isOpenAIOrAnthropicSetup(settings, envVars);
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.

🟡 MEDIUM | dead-code

isBasicAgentMode helper (just below at line 504) is now dead code

This PR removes every import of isBasicAgentMode — see the -import { isBasicAgentMode } lines in src/components/ChatPanel.tsx, src/ipc/handlers/chat_stream_handlers.ts, and the other handler file. A repo-wide search turns up no remaining call sites: every other match is a locally-scoped const isBasicAgentMode = ... variable, not a reference to this exported function.

The exported helper in src/lib/schemas.ts:504-508 is now unused infrastructure and should be removed.

💡 Suggestion: Delete the exported isBasicAgentMode function. If it's being kept intentionally for future use, add a comment explaining why.

}),
{ id: `restore-timeout-${chatId}` },
);
setIsRestoringMode(false);
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.

🟡 MEDIUM | logic

Timeout branch doesn't mark the chat as restored, allowing duplicate toasts and reruns

In the success branches of this effect, lastRestoredChatIdRef.current = chatId is set so the restore logic won't re-run for the same chat. The timeout/error branch here calls setIsRestoringMode(false) and shows the restoreModeTimedOut toast, but never updates lastRestoredChatIdRef.current.

If the effect re-runs for the same chatId (e.g. a late settings update, a stream count bump, or a dependency change), the restore will fire again and the user sees the warning toast a second time.

💡 Suggestion: Set lastRestoredChatIdRef.current = chatId in the timeout branch too, so the restore is marked as terminally attempted.

showError(
t("chatMode.planAgentUnavailable", {
defaultValue:
"Agent mode is unavailable for plan implementation. Configure an OpenAI or Anthropic provider, or upgrade to Pro.",
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.

🟡 MEDIUM | i18n

defaultValue contradicts the actual locale copy

The inline defaultValue here is "Agent mode is unavailable for plan implementation. Configure an OpenAI or Anthropic provider, or upgrade to Pro.", but every locale file (en, pt-BR, zh-CN) defines chatMode.planAgentUnavailable with different remediation copy ("...Please enable an eligible mode and try again.") that makes no mention of providers or Pro.

In practice users will see the translated string, but if the key is ever missing or renamed they'll get a fundamentally different message. The two copies will also silently drift further apart over time.

💡 Suggestion: Either drop the defaultValue entirely (let t() fall back to the key) or align it with the locale copy so both describe the same remediation.

@@ -496,6 +504,7 @@ export function ChatInput({ chatId }: { chatId?: number }) {
await streamMessage({
prompt: promptWithImages,
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.

🟡 MEDIUM | i18n

Hardcoded English string a few lines above this hunk (line 502)

Just above this added line — at src/components/chat/ChatInput.tsx:502 — is showInfo("We've switched you to a new chat for a clean context"), still hardcoded in English. The switchedToNewChatCleanContext key is already defined in all three locale files (en, pt-BR, zh-CN) specifically for this message, so non-English users see English here while the rest of the UI is translated.

💡 Suggestion: Replace with showInfo(t("switchedToNewChatCleanContext")) (or the appropriate namespaced key).

if (settings?.selectedChatMode !== writableMode) {
const modeLabel =
writableMode === "local-agent"
? t("chatMode.agent", { defaultValue: "Agent" })
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.

🟡 MEDIUM | consistency

Warning toast shows "Agent" even when the selector shows "Basic Agent"

This uses t("chatMode.agent", { defaultValue: "Agent" }) unconditionally, but non-Pro users see the same mode labeled as "Basic Agent" in ChatModeSelector (which branches on isProEnabled). A non-Pro user who triggers this warning is told the review "runs in Agent mode" but the selector only offers "Basic Agent" — two names for the same thing, one more place users have to reconcile.

💡 Suggestion: Branch on isDyadProEnabled(settings) (the same condition the selector uses) and pick chatMode.basicAgent vs chatMode.agent accordingly.

});

updateSettingsRef
.current({ selectedChatMode: resolvedMode.mode })
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.

🟡 MEDIUM | consistency

Fallback mode silently overrides the user's selection with no toast

When resolvedMode.usedFallback === true, this block quietly writes the fallback mode via updateSettings({ selectedChatMode: resolvedMode.mode }) and moves on. The parallel code path in useRestoreChatMode handles the same scenario by showing a chatMode.modeUnavailableFallback toast so the user understands why their chosen mode was overridden.

As-is, a user switching between chat tabs (or jumping to a chat whose persisted mode is no longer allowed) can have their selected mode changed with no visible feedback — a UX regression from the symmetric hook.

💡 Suggestion: Check resolvedMode.usedFallback and show the same modeUnavailableFallback toast here, so the two hooks behave consistently.

)}
>
<div className="flex items-center gap-2 mb-0.5">
<span className="text-foreground/80">{icon}</span>
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.

🟡 MEDIUM | ux

Mode icons lose their color coding inside the dropdown options

The trigger button colors selectedMode icons by mode (e.g. ask → purple-500, plan → blue-500) — that's how users learn the mode↔color association at a glance. But ModeOption wraps the icon in <span className="text-foreground/80">{icon}</span>, which overrides those per-mode colors with a neutral foreground tone.

Result: the dropdown strips exactly the visual affordance that the trigger relies on, so scanning the list for the mode you want is noticeably harder than it needs to be.

💡 Suggestion: Drop the text-foreground/80 wrapper (or only apply it when the option is disabled), so the per-mode icon colors carry through from trigger to dropdown.

@github-actions
Copy link
Copy Markdown
Contributor

🎭 Playwright Test Results

❌ Some tests failed

OS Passed Failed Flaky Skipped
🍎 macOS 299 97 3 134
🪟 Windows 303 43 7 134

Summary: 602 passed, 140 failed, 10 flaky, 268 skipped

Failed Tests

🍎 macOS

Show all 97 failures
  • annotator.spec.ts > annotator - capture and submit screenshot
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • astro.spec.ts > astro
    • Error: expect(string).toMatchSnapshot(expected) failed
  • attach_image.spec.ts > attach image - chat
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • attach_image.spec.ts > attach image - chat - upload to codebase
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • attach_image.spec.ts > attach image via drag - chat
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • cancelled_message.spec.ts > cancelled message shows cancelled indicator and is excluded from context
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • chat_history.spec.ts > should open, navigate, and select from history menu
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • chat_image_generation.spec.ts > generate image from chat - full flow
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • chat_input.spec.ts > send button disabled during pending proposal
    • Error: expect(locator).toBeEnabled() failed
  • chat_input.spec.ts > send button disabled during pending proposal - reject
    • Error: expect(locator).toBeEnabled() failed
  • chat_mode.spec.ts > chat mode selector - default build mode
    • Error: expect(string).toMatchSnapshot(expected) failed
  • chat_mode.spec.ts > chat mode selector - ask mode
    • Error: expect(locator).toBeVisible() failed
  • context_compaction.spec.ts > local-agent - context compaction triggers and shows summary
    • Error: expect(locator).toBeVisible() failed
  • context_compaction.spec.ts > local-agent - context compaction can run mid-turn
    • Error: expect(locator).toBeVisible() failed
  • context_manage.spec.ts > manage context - default
    • Error: expect(received).toEqual(expected) // deep equality
  • context_manage.spec.ts > manage context - smart context
    • Error: expect(received).toBe(expected) // Object.is equality
  • context_manage.spec.ts > manage context - smart context - auto-includes only
    • Error: expect(received).toBe(expected) // Object.is equality
  • context_manage.spec.ts > manage context - exclude paths
    • Error: expect(received).toEqual(expected) // deep equality
  • context_manage.spec.ts > manage context - exclude paths with smart context
    • Error: expect(received).toBe(expected) // Object.is equality
  • context_window.spec.ts > context window
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • dyad_tags_parsing.spec.ts > dyad tags handles nested < tags
    • Error: expect(string).toMatchSnapshot(expected) failed
  • engine.spec.ts > send message to engine
    • Error: expect(string).toMatchSnapshot(expected) failed
  • engine.spec.ts > send message to engine - openai gpt-5
    • Error: expect(string).toMatchSnapshot(expected) failed
  • engine.spec.ts > send message to engine - anthropic claude sonnet 4
    • Error: expect(string).toMatchSnapshot(expected) failed
  • engine.spec.ts > smart auto should send message to engine
    • Error: expect(string).toMatchSnapshot(expected) failed
  • free_agent_quota.spec.ts > free agent quota - full flow: mode availability, quota tracking, exceeded banner, switch to build
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • free_agent_quota.spec.ts > free agent quota - quota resets after 24 hours
    • Error: expect(locator).toBeVisible() failed
  • github.spec.ts > github clear integration settings
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • import.spec.ts > import app with AI rules
    • Error: expect(string).toMatchSnapshot(expected) failed
  • local_agent_advanced.spec.ts > local-agent - security review fix
    • Error: expect(locator).toBeVisible() failed
  • local_agent_ask.spec.ts > local-agent ask mode
    • Error: expect(locator).toBeVisible() failed
  • local_agent_basic.spec.ts > local-agent - dump request
    • Error: expect(locator).toBeVisible() failed
  • local_agent_basic.spec.ts > local-agent - read then edit
    • Error: expect(locator).toBeVisible() failed
  • local_agent_basic.spec.ts > local-agent - parallel tool calls
    • Error: expect(locator).toBeVisible() failed
  • local_agent_basic.spec.ts > local-agent - questionnaire flow
    • Error: expect(locator).toBeVisible() failed
  • local_agent_code_search.spec.ts > local-agent - code search
    • Error: expect(locator).toBeVisible() failed
  • local_agent_connection_retry.spec.ts > local-agent - recovers from connection drop
    • Error: expect(locator).toBeVisible() failed
  • local_agent_connection_retry.spec.ts > local-agent - recovers when drop happens after tool-call stream
    • Error: expect(locator).toBeVisible() failed
  • local_agent_consent.spec.ts > local-agent - add_dependency consent: always allow
    • Error: expect(locator).toBeVisible() failed
  • local_agent_consent.spec.ts > local-agent - add_dependency consent: decline
    • Error: expect(locator).toBeVisible() failed
  • local_agent_file_upload.spec.ts > local-agent - upload file to codebase
    • Error: expect(locator).toBeVisible() failed
  • local_agent_generate_image.spec.ts > local-agent - generate image
    • Error: expect(locator).toBeVisible() failed
  • local_agent_list_files.spec.ts > local-agent - list_files
    • Error: expect(locator).toBeVisible() failed
  • local_agent_list_files.spec.ts > local-agent - list_files include_hidden
    • Error: expect(locator).toBeVisible() failed
  • local_agent_persistent_todos.spec.ts > local-agent - persistent todos across turns
    • Error: expect(locator).toBeVisible() failed
  • local_agent_read_logs.spec.ts > local-agent - read logs with filters
    • Error: expect(locator).toBeVisible() failed
  • local_agent_run_type_checks.spec.ts > local-agent - run_type_checks updates problems panel
    • Error: expect(locator).toBeVisible() failed
  • local_agent_step_limit.spec.ts > local-agent - step limit pause
    • Error: expect(locator).toBeVisible() failed
  • local_agent_summarize.spec.ts > local-agent - summarize to new chat works
    • Error: expect(locator).toBeVisible() failed
  • local_agent_todo_followup.spec.ts > local-agent - todo follow-up loop
    • Error: expect(locator).toBeVisible() failed
  • local_agent_web_fetch.spec.ts > local-agent - web fetch
    • Error: expect(locator).toBeVisible() failed
  • mcp.spec.ts > mcp - call calculator
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • mcp.spec.ts > mcp - call calculator via http
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • mention_files.spec.ts > reference file from editor file tree
    • Error: expect(string).toMatchSnapshot(expected) failed
  • partial_response.spec.ts > partial message is resumed
    • Error: No dump path found in messages list
  • pause_queue.spec.ts > pause queue > pause queue prevents dequeuing after current stream completes
    • Error: expect(locator).toContainText(expected) failed
  • pause_queue.spec.ts > pause queue > stopping while paused keeps queue and resume starts sending
    • Error: expect(locator).toContainText(expected) failed
  • pause_queue.spec.ts > pause queue > sending while stopped with paused queue sends immediately and keeps queue
    • Error: expect(locator).toContainText(expected) failed
  • plan_mode.spec.ts > plan mode - accept plan redirects to new chat and saves to disk
    • Error: expect(locator).toBeVisible() failed
  • plan_mode.spec.ts > plan mode - questionnaire flow
    • Error: expect(locator).toBeVisible() failed
  • plan_mode.spec.ts > plan mode - add and review plan annotations
    • Error: expect(locator).toBeVisible() failed
  • plan_mode.spec.ts > plan mode - view plan button opens preview panel when collapsed
    • Error: expect(locator).toBeVisible() failed
  • problems.spec.ts > problems auto-fix - enabled
    • Error: No dump path found in messages list
  • problems.spec.ts > problems auto-fix - gives up after 2 attempts
    • Error: No dump path found in messages list
  • problems.spec.ts > problems auto-fix - complex delete-rename-write
    • Error: No dump path found in messages list
  • problems.spec.ts > problems - fix all
    • Error: expect(locator).toMatchAriaSnapshot(expected) failed
  • queued_message.spec.ts > editing queued message restores attachments and selected components
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • queued_message.spec.ts > canceling queued message edit clears restored components
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • queued_message.spec.ts > queued messages > gets added and sent after stream completes
    • Error: expect(locator).toBeVisible() failed
  • queued_message.spec.ts > queued messages > can be reordered, deleted, and edited
    • Error: expect(locator).toBeVisible() failed
  • queued_message.spec.ts > queued messages > fires queued message while on another page
    • Error: expect(locator).toBeVisible() failed
  • rename_edit.spec.ts > rename then edit works
    • Error: expect(string).toMatchSnapshot(expected) failed
  • security_review.spec.ts > security review
    • Error: expect(string).toMatchSnapshot(expected) failed
  • security_review.spec.ts > security review - edit and use knowledge
    • Error: expect(string).toMatchSnapshot(expected) failed
  • security_review.spec.ts > security review - multi-select and fix issues
    • Error: expect(locator).toMatchAriaSnapshot(expected) failed
  • select_component.spec.ts > select component
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • select_component.spec.ts > select multiple components
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • select_component.spec.ts > deselect component
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • select_component.spec.ts > select component next.js
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • smart_context_deep.spec.ts > smart context deep - read write read
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • socket_firewall.spec.ts > build mode - safe npm package installs through the real socket firewall path
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • socket_firewall.spec.ts > build mode - blocked unsafe npm package shows the real socket verdict and preserves app files
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • supabase_client.spec.ts > supabase client is generated
    • Error: expect(string).toMatchSnapshot(expected) failed
  • supabase_migrations.spec.ts > supabase migrations
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • supabase_migrations.spec.ts > supabase migrations with native git
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • telemetry.spec.ts > telemetry - accept
    • Error: expect(string).toMatchSnapshot(expected) failed
  • telemetry.spec.ts > telemetry - reject
    • Error: expect(string).toMatchSnapshot(expected) failed
  • telemetry.spec.ts > telemetry - later
    • Error: expect(string).toMatchSnapshot(expected) failed
  • thinking_budget.spec.ts > thinking budget
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • turbo_edits_v2.spec.ts > turbo edits v2 - search-replace dump
    • Error: expect(string).toMatchSnapshot(expected) failed
  • turbo_edits_v2.spec.ts > turbo edits v2 - search-replace fallback
    • Error: expect(string).toMatchSnapshot(expected) failed
  • undo.spec.ts > undo
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • undo.spec.ts > undo with native git
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • undo.spec.ts > undo after assistant with no code
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • version_integrity.spec.ts > version integrity (git isomorphic)
    • Error: expect(string).toMatchSnapshot(expected) failed
  • version_integrity.spec.ts > version integrity (git native)
    • Error: expect(string).toMatchSnapshot(expected) failed
  • visual_editing.spec.ts > swap image via URL
    • Error: expect(string).toMatchSnapshot(expected) failed

🪟 Windows

Show all 43 failures
  • astro.spec.ts > astro
    • Error: expect(string).toMatchSnapshot(expected) failed
  • attach_image.spec.ts > attach image - chat
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • attach_image.spec.ts > attach image - chat - upload to codebase
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • attach_image.spec.ts > attach image via drag - chat
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • cancelled_message.spec.ts > cancelled message shows cancelled indicator and is excluded from context
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • chat_history.spec.ts > should open, navigate, and select from history menu
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • chat_image_generation.spec.ts > generate image from chat - full flow
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • chat_input.spec.ts > send button disabled during pending proposal
    • Error: expect(locator).toBeEnabled() failed
  • chat_input.spec.ts > send button disabled during pending proposal - reject
    • Error: expect(locator).toBeEnabled() failed
  • chat_mode.spec.ts > chat mode selector - default build mode
    • Error: expect(string).toMatchSnapshot(expected) failed
  • chat_mode.spec.ts > chat mode selector - ask mode
    • Error: expect(locator).toBeVisible() failed
  • chat_tabs.spec.ts > tabs appear after navigating between chats
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • chat_tabs.spec.ts > closing a tab removes it and selects adjacent tab
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • chat_tabs.spec.ts > right-click context menu: Close other tabs
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • chat_tabs.spec.ts > right-click context menu: Close tabs to the right
    • Error: expect(locator).toBeVisible() failed
  • context_manage.spec.ts > manage context - default
    • Error: expect(received).toEqual(expected) // deep equality
  • context_manage.spec.ts > manage context - smart context
    • Error: expect(received).toBe(expected) // Object.is equality
  • context_manage.spec.ts > manage context - smart context - auto-includes only
    • Error: expect(received).toBe(expected) // Object.is equality
  • context_manage.spec.ts > manage context - exclude paths
    • Error: expect(received).toEqual(expected) // deep equality
  • context_manage.spec.ts > manage context - exclude paths with smart context
    • Error: expect(received).toBe(expected) // Object.is equality
  • edit_code.spec.ts > edit code edits the right file during rapid switches
    • Error: expect(received).toEqual(expected) // deep equality
  • git_collaboration.spec.ts > Git Collaboration > should create, switch, rename, merge, and delete branches
    • Error: expect(locator).toMatchAriaSnapshot(expected) failed
  • github.spec.ts > create and sync to new repo
    • TimeoutError: page.waitForSelector: Timeout 5000ms exceeded.
  • github.spec.ts > create and sync to new repo - custom branch
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • github.spec.ts > create and sync to existing repo
    • Error: expect(locator).toMatchAriaSnapshot(expected) failed
  • github.spec.ts > create and sync to existing repo - custom branch
    • Error: expect(locator).toMatchAriaSnapshot(expected) failed
  • github.spec.ts > github clear integration settings
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • mention_files.spec.ts > reference file from editor file tree
    • Error: expect(string).toMatchSnapshot(expected) failed
  • partial_response.spec.ts > partial message is resumed
    • Error: No dump path found in messages list
  • pause_queue.spec.ts > pause queue > pause queue prevents dequeuing after current stream completes
    • Error: expect(locator).toContainText(expected) failed
  • pause_queue.spec.ts > pause queue > stopping while paused keeps queue and resume starts sending
    • Error: expect(locator).toContainText(expected) failed
  • pause_queue.spec.ts > pause queue > sending while stopped with paused queue sends immediately and keeps queue
    • Error: expect(locator).toContainText(expected) failed
  • problems.spec.ts > problems auto-fix - enabled
    • Error: No dump path found in messages list
  • problems.spec.ts > problems auto-fix - gives up after 2 attempts
    • Error: No dump path found in messages list
  • problems.spec.ts > problems auto-fix - complex delete-rename-write
    • Error: No dump path found in messages list
  • queued_message.spec.ts > queued messages > gets added and sent after stream completes
    • Error: expect(locator).toBeVisible() failed
  • queued_message.spec.ts > queued messages > can be reordered, deleted, and edited
    • Error: expect(locator).toBeVisible() failed
  • queued_message.spec.ts > queued messages > fires queued message while on another page
    • Error: expect(locator).toBeVisible() failed
  • rename_edit.spec.ts > rename then edit works
    • Error: expect(string).toMatchSnapshot(expected) failed
  • security_review.spec.ts > security review - multi-select and fix issues
    • Error: expect(locator).toMatchAriaSnapshot(expected) failed
  • telemetry.spec.ts > telemetry - accept
    • Error: expect(string).toMatchSnapshot(expected) failed
  • telemetry.spec.ts > telemetry - reject
    • Error: expect(string).toMatchSnapshot(expected) failed
  • telemetry.spec.ts > telemetry - later
    • Error: expect(string).toMatchSnapshot(expected) failed

📋 Re-run Failing Tests (macOS)

Copy and paste to re-run all failing spec files locally:

npm run e2e \
  e2e-tests/annotator.spec.ts \
  e2e-tests/astro.spec.ts \
  e2e-tests/attach_image.spec.ts \
  e2e-tests/cancelled_message.spec.ts \
  e2e-tests/chat_history.spec.ts \
  e2e-tests/chat_image_generation.spec.ts \
  e2e-tests/chat_input.spec.ts \
  e2e-tests/chat_mode.spec.ts \
  e2e-tests/context_compaction.spec.ts \
  e2e-tests/context_manage.spec.ts \
  e2e-tests/context_window.spec.ts \
  e2e-tests/dyad_tags_parsing.spec.ts \
  e2e-tests/engine.spec.ts \
  e2e-tests/free_agent_quota.spec.ts \
  e2e-tests/github.spec.ts \
  e2e-tests/import.spec.ts \
  e2e-tests/local_agent_advanced.spec.ts \
  e2e-tests/local_agent_ask.spec.ts \
  e2e-tests/local_agent_basic.spec.ts \
  e2e-tests/local_agent_code_search.spec.ts \
  e2e-tests/local_agent_connection_retry.spec.ts \
  e2e-tests/local_agent_consent.spec.ts \
  e2e-tests/local_agent_file_upload.spec.ts \
  e2e-tests/local_agent_generate_image.spec.ts \
  e2e-tests/local_agent_list_files.spec.ts \
  e2e-tests/local_agent_persistent_todos.spec.ts \
  e2e-tests/local_agent_read_logs.spec.ts \
  e2e-tests/local_agent_run_type_checks.spec.ts \
  e2e-tests/local_agent_step_limit.spec.ts \
  e2e-tests/local_agent_summarize.spec.ts \
  e2e-tests/local_agent_todo_followup.spec.ts \
  e2e-tests/local_agent_web_fetch.spec.ts \
  e2e-tests/mcp.spec.ts \
  e2e-tests/mention_files.spec.ts \
  e2e-tests/partial_response.spec.ts \
  e2e-tests/pause_queue.spec.ts \
  e2e-tests/plan_mode.spec.ts \
  e2e-tests/problems.spec.ts \
  e2e-tests/queued_message.spec.ts \
  e2e-tests/rename_edit.spec.ts \
  e2e-tests/security_review.spec.ts \
  e2e-tests/select_component.spec.ts \
  e2e-tests/smart_context_deep.spec.ts \
  e2e-tests/socket_firewall.spec.ts \
  e2e-tests/supabase_client.spec.ts \
  e2e-tests/supabase_migrations.spec.ts \
  e2e-tests/telemetry.spec.ts \
  e2e-tests/thinking_budget.spec.ts \
  e2e-tests/turbo_edits_v2.spec.ts \
  e2e-tests/undo.spec.ts \
  e2e-tests/version_integrity.spec.ts \
  e2e-tests/visual_editing.spec.ts

⚠️ Flaky Tests

🍎 macOS

  • approve.spec.ts > write to index, approve, check preview (passed after 1 retry)
  • local_agent_consent.spec.ts > local-agent - add_dependency consent: allow once (passed after 2 retries)
  • local_agent_search_replace.spec.ts > local-agent - search_replace edit (passed after 2 retries)

🪟 Windows

  • chat_tabs.spec.ts > clicking a tab switches to that chat (passed after 2 retries)
  • context_limit_banner.spec.ts > context limit banner shows 'running out' when near context limit (passed after 2 retries)
  • github.spec.ts > should connect to GitHub using device flow (passed after 1 retry)
  • per_chat_input.spec.ts > chat input is preserved when switching between chats (passed after 1 retry)
  • per_chat_input.spec.ts > closing a chat tab clears its stored input (passed after 2 retries)
  • per_chat_input.spec.ts > input preserved when switching back and forth multiple times (passed after 1 retry)
  • setup_flow.spec.ts > Setup Flow > node.js install flow (passed after 2 retries)

📊 View full report

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-human:review-issue ai agent flagged an issue that requires human review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug] Chat mode doesn't persist when switching between chats

2 participants