From c5e235d8e2834c6f609349820bfa3c5d06879c4a Mon Sep 17 00:00:00 2001 From: Honey Paptan Date: Thu, 5 Nov 2026 01:16:58 +0530 Subject: [PATCH 1/2] fix(web): send /clear without compacting a stale thread first Compact-before-send only exempted /compact, so /clear on an idle Claude thread ran a full compaction of the old context and then discarded it. Fixes #31092 --- apps/web/src/components/ChatView.logic.test.ts | 12 +++++++++--- apps/web/src/components/ChatView.logic.ts | 7 ++++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/apps/web/src/components/ChatView.logic.test.ts b/apps/web/src/components/ChatView.logic.test.ts index 81b6992a59d3b..8550ad46425c2 100644 --- a/apps/web/src/components/ChatView.logic.test.ts +++ b/apps/web/src/components/ChatView.logic.test.ts @@ -1094,16 +1094,22 @@ describe("planComposerTurn", () => { }); }); - it("queues behind a compaction of a stale session, except for /compact itself", () => { + it("queues behind a compaction of a stale session, except for /compact and /clear", () => { const stale = { ...base, resumeCompactionTokens: 120_000 }; - for (const messageText of ["hello", "/compactfoo"]) { + for (const messageText of ["hello", "/compactfoo", "/clearly not a command"]) { expect(planComposerTurn({ ...stale, messageText })).toMatchObject({ compactBeforeSend: true, turnDispatchMode: "queue", shouldQueueBehindActiveRun: true, }); } - for (const messageText of ["/COMPACT", "/compact keep the API notes", "/compact\n tests"]) { + for (const messageText of [ + "/COMPACT", + "/compact keep the API notes", + "/compact\n tests", + "/clear", + "/Clear ", + ]) { expect(planComposerTurn({ ...stale, messageText })).toMatchObject({ compactBeforeSend: false, turnDispatchMode: "auto", diff --git a/apps/web/src/components/ChatView.logic.ts b/apps/web/src/components/ChatView.logic.ts index 4e17fdebf7b52..55d82b89d708c 100644 --- a/apps/web/src/components/ChatView.logic.ts +++ b/apps/web/src/components/ChatView.logic.ts @@ -835,8 +835,9 @@ export function shouldShowPlanFollowUpPrompt(input: { } // A typed `/compact`, with or without instructions, is already the compaction. Queueing a -// bare one ahead of it would run an uninstructed compaction first and charge for two. -const COMPACT_COMMAND_PATTERN = /^\/compact(?:\s|$)/i; +// bare one ahead of it would run an uninstructed compaction first and charge for two. A typed +// `/clear` discards the old context, so compacting it first pays for a summary nobody reads. +const NO_COMPACT_BEFORE_COMMAND_PATTERN = /^\/(?:compact|clear)(?:\s|$)/i; const sessionDismissedBranchMismatchKeys = new Set(); @@ -1425,7 +1426,7 @@ export function planComposerTurn(input: { const compactBeforeSend = input.resumeCompactionTokens !== null && !input.keepFullHistory && - !COMPACT_COMMAND_PATTERN.test(input.messageText); + !NO_COMPACT_BEFORE_COMMAND_PATTERN.test(input.messageText); return { // In worktree mode, require an explicit base branch so we don't silently // fall back to local execution when branch selection is missing. From e1e9b42a6b0d36f3f4517fd065a711f8b930eb6d Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Wed, 11 Nov 2026 03:24:17 +0000 Subject: [PATCH 2/2] test(web): drop redundant /clear casing case AI-assisted; verified with the tests and tools listed above. --- apps/web/src/components/ChatView.logic.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/web/src/components/ChatView.logic.test.ts b/apps/web/src/components/ChatView.logic.test.ts index 31ff8129bdd81..520658182b267 100644 --- a/apps/web/src/components/ChatView.logic.test.ts +++ b/apps/web/src/components/ChatView.logic.test.ts @@ -1108,7 +1108,6 @@ describe("planComposerTurn", () => { "/compact keep the API notes", "/compact\n tests", "/clear", - "/Clear ", ]) { expect(planComposerTurn({ ...stale, messageText })).toMatchObject({ compactBeforeSend: false,