From b4e7d024eb234dc3e2cb7937dd78819658f424a2 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Mon, 14 Sep 2026 21:02:45 -0700 Subject: [PATCH] fix(browser): stop reporting an unhydratable profile as a retired choice MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A profile that fails validation for a reason unrelated to identity — a non-UUID id, a mismatched partition — armed both the notice and its degraded flag. Since hydrateFromPersisted skips such entries silently and nothing ever repairs them, the user got "an old browser identity choice could not be inspected" forever, about a profile that never carried one. Key the notice on the presence of userAgentMode instead, and use validation only to decide whether the choice that was found is inspectable. Refusing to hydrate an entry and finding a retired choice are now separate facts. The old case table asserted the defect for null, 42 and 'broken', so it is replaced by two tables stating the new contract rather than adapted to pass. --- ...er-session-persisted-profile-validation.ts | 17 +++-- ...on-user-agent-migration-inspection.test.ts | 67 ++++++++++++++----- 2 files changed, 61 insertions(+), 23 deletions(-) diff --git a/src/main/browser/browser-session-persisted-profile-validation.ts b/src/main/browser/browser-session-persisted-profile-validation.ts index f2845d28379..18a1ff62fb1 100644 --- a/src/main/browser/browser-session-persisted-profile-validation.ts +++ b/src/main/browser/browser-session-persisted-profile-validation.ts @@ -30,17 +30,20 @@ export function inspectRetiredBrowserSessionProfileUserAgentModes( let noticePending = false let degraded = false for (const profile of profiles) { - if (!isValidPersistedBrowserSessionProfile(profile, activeOrcaProfileId)) { - noticePending = true - degraded = true - continue - } - if (!Object.hasOwn(profile, 'userAgentMode')) { + // Refusing to hydrate an entry is not the same as finding a retired choice: hydrateFromPersisted + // already skips it silently, and a notice here would claim an old choice could not be inspected + // for a profile that never carried one. + if (!profile || typeof profile !== 'object' || !Object.hasOwn(profile, 'userAgentMode')) { continue } noticePending = true const mode = Reflect.get(profile, 'userAgentMode') - if (mode !== 'clean' && mode !== 'native') { + // Degraded covers both ways the choice is uninspectable: an unreadable mode, and a mode sitting + // on an entry we refuse to hydrate, where we cannot say which profile it belonged to. + if ( + (mode !== 'clean' && mode !== 'native') || + !isValidPersistedBrowserSessionProfile(profile, activeOrcaProfileId) + ) { degraded = true } } diff --git a/src/main/browser/browser-session-user-agent-migration-inspection.test.ts b/src/main/browser/browser-session-user-agent-migration-inspection.test.ts index 499b341e265..73fc76c446e 100644 --- a/src/main/browser/browser-session-user-agent-migration-inspection.test.ts +++ b/src/main/browser/browser-session-user-agent-migration-inspection.test.ts @@ -3,38 +3,73 @@ import { getOrcaProfileBrowserSessionPartition } from '../../shared/orca-profile import { inspectRetiredBrowserSessionProfileUserAgentModes } from './browser-session-persisted-profile-validation' const ORCA_PROFILE_ID = 'local-default' +const PROFILE_ID = '11111111-1111-4111-8111-111111111111' function profileWithMode(mode: unknown): Record { - const id = '11111111-1111-4111-8111-111111111111' return { - id, + id: PROFILE_ID, scope: 'isolated', - partition: getOrcaProfileBrowserSessionPartition(ORCA_PROFILE_ID, id), + partition: getOrcaProfileBrowserSessionPartition(ORCA_PROFILE_ID, PROFILE_ID), label: 'Existing', source: null, userAgentMode: mode } } +/** Fails `isValidPersistedBrowserSessionProfile` on its id, for reasons unrelated to identity. */ +function unhydratableProfile(extra: Record = {}): Record { + return { + id: 'not-a-uuid', + scope: 'isolated', + partition: 'persist:orca-browser-session-not-a-uuid', + label: 'Unhydratable', + source: null, + ...extra + } +} + describe('retired browser profile identity inspection', () => { it('detects an inspectable old choice without removing its bytes', () => { const profile = profileWithMode('native') - expect( - inspectRetiredBrowserSessionProfileUserAgentModes([profile], ORCA_PROFILE_ID) - ).toEqual({ noticePending: true, degraded: false }) + expect(inspectRetiredBrowserSessionProfileUserAgentModes([profile], ORCA_PROFILE_ID)).toEqual({ + noticePending: true, + degraded: false + }) expect(profile.userAgentMode).toBe('native') }) - it.each([[null], [42], ['broken'], [profileWithMode('unexpected')]])( - 'turns malformed metadata into a degraded notice without throwing', - (entry) => { - expect(() => - inspectRetiredBrowserSessionProfileUserAgentModes([entry], ORCA_PROFILE_ID) - ).not.toThrow() - expect( - inspectRetiredBrowserSessionProfileUserAgentModes([entry], ORCA_PROFILE_ID) - ).toEqual({ noticePending: true, degraded: true }) + // "I refuse to hydrate this" is not "a retired identity choice was found". hydrateFromPersisted + // skips these entries silently, and the notice text claims an old choice could not be inspected — + // which would be a lie about a profile that never carried one, repeated on every launch. + it.each([ + { scenario: 'null', entry: null }, + { scenario: 'a number', entry: 42 }, + { scenario: 'a string', entry: 'broken' }, + { + scenario: 'a profile that fails validation for an unrelated reason', + entry: unhydratableProfile() } - ) + ])('stays silent about $scenario, which carries no identity choice', ({ entry }) => { + expect(inspectRetiredBrowserSessionProfileUserAgentModes([entry], ORCA_PROFILE_ID)).toEqual({ + noticePending: false, + degraded: false + }) + }) + + it.each([ + { scenario: 'an unreadable mode', entry: profileWithMode('unexpected') }, + { + scenario: 'a mode on an entry that cannot be hydrated', + entry: unhydratableProfile({ userAgentMode: 'native' }) + } + ])('turns $scenario into a degraded notice without throwing', ({ entry }) => { + expect(() => + inspectRetiredBrowserSessionProfileUserAgentModes([entry], ORCA_PROFILE_ID) + ).not.toThrow() + expect(inspectRetiredBrowserSessionProfileUserAgentModes([entry], ORCA_PROFILE_ID)).toEqual({ + noticePending: true, + degraded: true + }) + }) })