From 3115873a6bf0d8e86452a585000cbaf47cb8dfb0 Mon Sep 17 00:00:00 2001 From: Naved Date: Sat, 20 Jun 2026 10:05:57 -0700 Subject: [PATCH 1/2] fix: include auto-close settings in getState and getStateToPostToWebview The autoCloseZooOpenedFiles, autoCloseZooOpenedFilesAfterUserEdited, and autoCloseZooOpenedNewFiles settings were saved to global state but never included in the state posted back to the webview (getStateToPostToWebview) or returned by getState. This caused the checkbox to always revert to checked after saving, and the DiffViewProvider to always use the default value regardless of the user's preference. Also adds a changeset instruction to AGENTS.md. Closes #667 --- AGENTS.md | 1 + src/core/webview/ClineProvider.ts | 9 ++++ .../ClineProvider.taskHistory.spec.ts | 45 +++++++++++++++++++ 3 files changed, 55 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index b93d6a76cc..c32cb8c64b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -3,6 +3,7 @@ This file provides guidance to agents when working with code in this repository. - Settings View Pattern: When working on `SettingsView`, inputs must bind to the local `cachedState`, NOT the live `useExtensionState()`. The `cachedState` acts as a buffer for user edits, isolating them from the `ContextProxy` source-of-truth until the user explicitly clicks "Save". Wiring inputs directly to the live state causes race conditions. +- Changesets: Do NOT create `.changeset` files for each commit or code change. Changesets are managed separately by maintainers and should not be generated by agents during normal development. ## Test Placement Guidance diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 8cd0bdd911..30b1e7f185 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -2278,6 +2278,9 @@ export class ClineProvider openRouterImageApiKey, openRouterImageGenerationSelectedModel, lockApiConfigAcrossModes, + autoCloseZooOpenedFiles, + autoCloseZooOpenedFilesAfterUserEdited, + autoCloseZooOpenedNewFiles, } = await this.getState() let cloudOrganizations: CloudOrganizationMembership[] = [] @@ -2457,6 +2460,9 @@ export class ClineProvider imageGenerationProvider, openRouterImageApiKey, openRouterImageGenerationSelectedModel, + autoCloseZooOpenedFiles: autoCloseZooOpenedFiles ?? true, + autoCloseZooOpenedFilesAfterUserEdited: autoCloseZooOpenedFilesAfterUserEdited ?? false, + autoCloseZooOpenedNewFiles: autoCloseZooOpenedNewFiles ?? false, openAiCodexIsAuthenticated: await (async () => { try { const { openAiCodexOAuthManager } = await import("../../integrations/openai-codex/oauth") @@ -2657,6 +2663,9 @@ export class ClineProvider imageGenerationProvider: stateValues.imageGenerationProvider, openRouterImageApiKey: stateValues.openRouterImageApiKey, openRouterImageGenerationSelectedModel: stateValues.openRouterImageGenerationSelectedModel, + autoCloseZooOpenedFiles: stateValues.autoCloseZooOpenedFiles, + autoCloseZooOpenedFilesAfterUserEdited: stateValues.autoCloseZooOpenedFilesAfterUserEdited, + autoCloseZooOpenedNewFiles: stateValues.autoCloseZooOpenedNewFiles, } } diff --git a/src/core/webview/__tests__/ClineProvider.taskHistory.spec.ts b/src/core/webview/__tests__/ClineProvider.taskHistory.spec.ts index 4a76ab4858..eff25f9082 100644 --- a/src/core/webview/__tests__/ClineProvider.taskHistory.spec.ts +++ b/src/core/webview/__tests__/ClineProvider.taskHistory.spec.ts @@ -685,6 +685,51 @@ describe("ClineProvider Task History Synchronization", () => { }) }) + describe("auto-close settings are included in posted state", () => { + it("getStateToPostToWebview returns saved autoCloseZooOpenedFiles value", async () => { + await provider.resolveWebviewView(mockWebviewView) + + // Simulate the updateSettings handler storing the value. + await provider.contextProxy.setValue("autoCloseZooOpenedFiles", false) + await provider.contextProxy.setValue("autoCloseZooOpenedFilesAfterUserEdited", true) + await provider.contextProxy.setValue("autoCloseZooOpenedNewFiles", true) + + const state = await provider.getStateToPostToWebview() + + // The saved values must be present in the state posted to the webview. + expect(state.autoCloseZooOpenedFiles).toBe(false) + expect(state.autoCloseZooOpenedFilesAfterUserEdited).toBe(true) + expect(state.autoCloseZooOpenedNewFiles).toBe(true) + }) + + it("getStateToPostToWebview defaults autoCloseZooOpenedFiles to true when unset", async () => { + await provider.resolveWebviewView(mockWebviewView) + + // Ensure the settings are not set. + await provider.contextProxy.setValue("autoCloseZooOpenedFiles", undefined) + await provider.contextProxy.setValue("autoCloseZooOpenedFilesAfterUserEdited", undefined) + await provider.contextProxy.setValue("autoCloseZooOpenedNewFiles", undefined) + + const state = await provider.getStateToPostToWebview() + + // Unset values should default to their documented defaults. + expect(state.autoCloseZooOpenedFiles).toBe(true) + expect(state.autoCloseZooOpenedFilesAfterUserEdited).toBe(false) + expect(state.autoCloseZooOpenedNewFiles).toBe(false) + }) + + it("getState returns saved autoCloseZooOpenedFiles value for DiffViewProvider", async () => { + await provider.resolveWebviewView(mockWebviewView) + + await provider.contextProxy.setValue("autoCloseZooOpenedFiles", false) + + const state = await provider.getState() + + // DiffViewProvider reads from getState(); the field must be present. + expect(state.autoCloseZooOpenedFiles).toBe(false) + }) + }) + describe("taskHistory write lock (mutex)", () => { it("serializes concurrent updateTaskHistory calls so no entries are lost", async () => { await provider.resolveWebviewView(mockWebviewView) From b2a7f806f0e960bc1452b5a5c5e715adaab0189a Mon Sep 17 00:00:00 2001 From: Naved Date: Sat, 20 Jun 2026 12:13:44 -0700 Subject: [PATCH 2/2] test: move auto-close settings tests to ClineProvider.spec.ts and assert all three fields Address review comments on PR #668: - Move the auto-close settings test block from ClineProvider.taskHistory.spec.ts to ClineProvider.spec.ts, where existing getState settings coverage (autoCondenseContext, writeDelayMs) already lives. - Expand the getState regression test to assert autoCloseZooOpenedFiles, autoCloseZooOpenedFilesAfterUserEdited, and autoCloseZooOpenedNewFiles so a regression dropping any of the three fields is caught. --- .../webview/__tests__/ClineProvider.spec.ts | 50 +++++++++++++++++++ .../ClineProvider.taskHistory.spec.ts | 45 ----------------- 2 files changed, 50 insertions(+), 45 deletions(-) diff --git a/src/core/webview/__tests__/ClineProvider.spec.ts b/src/core/webview/__tests__/ClineProvider.spec.ts index e1ddd881b8..158238ce7e 100644 --- a/src/core/webview/__tests__/ClineProvider.spec.ts +++ b/src/core/webview/__tests__/ClineProvider.spec.ts @@ -1010,6 +1010,56 @@ describe("ClineProvider", () => { expect(mockPostMessage).toHaveBeenCalled() }) + describe("auto-close settings are included in posted state", () => { + it("getStateToPostToWebview returns saved autoCloseZooOpenedFiles value", async () => { + await provider.resolveWebviewView(mockWebviewView) + + // Simulate the updateSettings handler storing the value. + await provider.contextProxy.setValue("autoCloseZooOpenedFiles", false) + await provider.contextProxy.setValue("autoCloseZooOpenedFilesAfterUserEdited", true) + await provider.contextProxy.setValue("autoCloseZooOpenedNewFiles", true) + + const state = await provider.getStateToPostToWebview() + + // The saved values must be present in the state posted to the webview. + expect(state.autoCloseZooOpenedFiles).toBe(false) + expect(state.autoCloseZooOpenedFilesAfterUserEdited).toBe(true) + expect(state.autoCloseZooOpenedNewFiles).toBe(true) + }) + + it("getStateToPostToWebview defaults autoCloseZooOpenedFiles to true when unset", async () => { + await provider.resolveWebviewView(mockWebviewView) + + // Ensure the settings are not set. + await provider.contextProxy.setValue("autoCloseZooOpenedFiles", undefined) + await provider.contextProxy.setValue("autoCloseZooOpenedFilesAfterUserEdited", undefined) + await provider.contextProxy.setValue("autoCloseZooOpenedNewFiles", undefined) + + const state = await provider.getStateToPostToWebview() + + // Unset values should default to their documented defaults. + expect(state.autoCloseZooOpenedFiles).toBe(true) + expect(state.autoCloseZooOpenedFilesAfterUserEdited).toBe(false) + expect(state.autoCloseZooOpenedNewFiles).toBe(false) + }) + + it("getState returns saved autoCloseZooOpenedFiles value for DiffViewProvider", async () => { + await provider.resolveWebviewView(mockWebviewView) + + await provider.contextProxy.setValue("autoCloseZooOpenedFiles", false) + await provider.contextProxy.setValue("autoCloseZooOpenedFilesAfterUserEdited", true) + await provider.contextProxy.setValue("autoCloseZooOpenedNewFiles", true) + + const state = await provider.getState() + + // DiffViewProvider reads from getState(); all three fields must be present + // so a regression that drops any of them is caught. + expect(state.autoCloseZooOpenedFiles).toBe(false) + expect(state.autoCloseZooOpenedFilesAfterUserEdited).toBe(true) + expect(state.autoCloseZooOpenedNewFiles).toBe(true) + }) + }) + it("loads saved API config when switching modes", async () => { await provider.resolveWebviewView(mockWebviewView) const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as any).mock.calls[0][0] diff --git a/src/core/webview/__tests__/ClineProvider.taskHistory.spec.ts b/src/core/webview/__tests__/ClineProvider.taskHistory.spec.ts index eff25f9082..4a76ab4858 100644 --- a/src/core/webview/__tests__/ClineProvider.taskHistory.spec.ts +++ b/src/core/webview/__tests__/ClineProvider.taskHistory.spec.ts @@ -685,51 +685,6 @@ describe("ClineProvider Task History Synchronization", () => { }) }) - describe("auto-close settings are included in posted state", () => { - it("getStateToPostToWebview returns saved autoCloseZooOpenedFiles value", async () => { - await provider.resolveWebviewView(mockWebviewView) - - // Simulate the updateSettings handler storing the value. - await provider.contextProxy.setValue("autoCloseZooOpenedFiles", false) - await provider.contextProxy.setValue("autoCloseZooOpenedFilesAfterUserEdited", true) - await provider.contextProxy.setValue("autoCloseZooOpenedNewFiles", true) - - const state = await provider.getStateToPostToWebview() - - // The saved values must be present in the state posted to the webview. - expect(state.autoCloseZooOpenedFiles).toBe(false) - expect(state.autoCloseZooOpenedFilesAfterUserEdited).toBe(true) - expect(state.autoCloseZooOpenedNewFiles).toBe(true) - }) - - it("getStateToPostToWebview defaults autoCloseZooOpenedFiles to true when unset", async () => { - await provider.resolveWebviewView(mockWebviewView) - - // Ensure the settings are not set. - await provider.contextProxy.setValue("autoCloseZooOpenedFiles", undefined) - await provider.contextProxy.setValue("autoCloseZooOpenedFilesAfterUserEdited", undefined) - await provider.contextProxy.setValue("autoCloseZooOpenedNewFiles", undefined) - - const state = await provider.getStateToPostToWebview() - - // Unset values should default to their documented defaults. - expect(state.autoCloseZooOpenedFiles).toBe(true) - expect(state.autoCloseZooOpenedFilesAfterUserEdited).toBe(false) - expect(state.autoCloseZooOpenedNewFiles).toBe(false) - }) - - it("getState returns saved autoCloseZooOpenedFiles value for DiffViewProvider", async () => { - await provider.resolveWebviewView(mockWebviewView) - - await provider.contextProxy.setValue("autoCloseZooOpenedFiles", false) - - const state = await provider.getState() - - // DiffViewProvider reads from getState(); the field must be present. - expect(state.autoCloseZooOpenedFiles).toBe(false) - }) - }) - describe("taskHistory write lock (mutex)", () => { it("serializes concurrent updateTaskHistory calls so no entries are lost", async () => { await provider.resolveWebviewView(mockWebviewView)