From 8cd63706e245f1450b5c5b31afb28f7fd75e1d2a Mon Sep 17 00:00:00 2001 From: Merge Sim Date: Thu, 3 Sep 2026 15:21:34 -0700 Subject: [PATCH] Pin the absent-vs-empty distinction in the managed-account gate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An empty claudeManagedAccounts array is a real answer: the user has no managed accounts, nothing claims an identity, and the ambient path is legitimate. A readable settings object with no such field is settings we failed to parse — the same unknown as unreadable — so it refuses. The two are one character apart in the code and the difference is invisible without the reasoning, so record it at the branch and pin both sides. The test fails under the obvious "consistency fix" of treating a missing field as empty. --- ...aude-structured-managed-account-support.test.ts | 14 ++++++++++++++ .../claude-structured-managed-account-support.ts | 2 ++ 2 files changed, 16 insertions(+) diff --git a/src/main/native-chat/claude-structured-managed-account-support.test.ts b/src/main/native-chat/claude-structured-managed-account-support.test.ts index 86054b9aab2..64248100429 100644 --- a/src/main/native-chat/claude-structured-managed-account-support.test.ts +++ b/src/main/native-chat/claude-structured-managed-account-support.test.ts @@ -70,6 +70,20 @@ describe('structuredClaudeMatchesActiveManagedAccount', () => { ).toBe(false) }) + /** Absent is not empty. An empty array is a real answer — the user has no managed accounts, so + * nothing claims an identity and the ambient path is legitimate. Settings with no accounts field + * are settings we failed to parse, which is the same epistemic state as unreadable. */ + it('separates an empty account list from an absent one', () => { + expect( + structuredClaudeMatchesActiveManagedAccount(settings({ claudeManagedAccounts: [] })) + ).toBe(true) + expect( + structuredClaudeMatchesActiveManagedAccount({ + activeClaudeManagedAccountId: null + } as unknown as ClaudeManagedAccountGateSettings) + ).toBe(false) + }) + it('fails closed when the settings cannot be read at all', () => { expect(structuredClaudeMatchesActiveManagedAccount(null)).toBe(false) expect(structuredClaudeMatchesActiveManagedAccount(undefined)).toBe(false) diff --git a/src/main/native-chat/claude-structured-managed-account-support.ts b/src/main/native-chat/claude-structured-managed-account-support.ts index cd3afa473ca..46967809641 100644 --- a/src/main/native-chat/claude-structured-managed-account-support.ts +++ b/src/main/native-chat/claude-structured-managed-account-support.ts @@ -27,6 +27,8 @@ export function structuredClaudeMatchesActiveManagedAccount( settings: ClaudeManagedAccountGateSettings | null | undefined ): boolean { const accounts = settings?.claudeManagedAccounts + // Absent is not empty: an empty list is a real "no managed accounts" answer, but a missing field + // is settings we failed to parse, which is the same unknown as unreadable. Do not merge these. if (!settings || !Array.isArray(accounts)) { return false }