You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maka.apache.org by as...@apache.org on 2026/09/07 23:31:20 UTC

(maka) branch main updated: fix(desktop): exclude archived sessions from startup selection (#4980)

This is an automated email from the ASF dual-hosted git repository.

Astro-Han pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/maka.git


The following commit(s) were added to refs/heads/main by this push:
     new 5df5b7f8a9 fix(desktop): exclude archived sessions from startup selection (#4980)
5df5b7f8a9 is described below

commit 5df5b7f8a9cfbea60b758bef0f8fee5e674acc19
Author: Haoqing Wang <78...@users.noreply.github.com>
AuthorDate: Tue Sep 8 07:31:14 2026 +0800

    fix(desktop): exclude archived sessions from startup selection (#4980)
    
    Archived sessions in the bootstrap catalog could be reopened automatically behind an empty sidebar. Apply archive eligibility at the existing bootstrap-selection owner for initial selection and subsequent snapshots while preserving explicit navigation and new-task reload intent.
    
    No storage migration or protocol change. Focused regression and real Electron archive/reload/new-task checks passed.
    
    Generated-by: OpenAI Codex
---
 apps/desktop/e2e-budget.json                       |  4 +-
 apps/desktop/e2e/new-task-reload.spec.ts           | 52 +++++++++++++++++++
 .../__tests__/bootstrap-selection-lease.test.ts    | 60 ++++++++++++++++++++--
 .../src/renderer/bootstrap-selection-lease.ts      | 16 ++++--
 4 files changed, 122 insertions(+), 10 deletions(-)

diff --git a/apps/desktop/e2e-budget.json b/apps/desktop/e2e-budget.json
index 930837c812..e8583a686d 100644
--- a/apps/desktop/e2e-budget.json
+++ b/apps/desktop/e2e-budget.json
@@ -14,8 +14,8 @@
       "electron": "the saved window is read back from the Host's connection snapshot, not from renderer state"
     },
     "new-task-reload.spec.ts": {
-      "tests": 1,
-      "electron": "renderer reload is the whole contract: an explicit new task must not reopen history"
+      "tests": 2,
+      "electron": "renderer reload must preserve an explicit new task and rebuild archived-only Host history as an empty, usable new-task surface"
     },
     "partial-history-notice.spec.ts": {
       "tests": 1,
diff --git a/apps/desktop/e2e/new-task-reload.spec.ts b/apps/desktop/e2e/new-task-reload.spec.ts
index d6913a49f6..b35056981d 100644
--- a/apps/desktop/e2e/new-task-reload.spec.ts
+++ b/apps/desktop/e2e/new-task-reload.spec.ts
@@ -19,6 +19,58 @@
 
 import { COMPOSER_INPUT, awaitSendReady, ensureSidebarExpanded, expect, test } from './fixtures';
 
+test('archived-only history boots into a usable new task', async ({ window: page }, testInfo) => {
+  await page.setViewportSize({ width: 1280, height: 900 });
+  const composer = page.locator(COMPOSER_INPUT);
+  const reply = page.getByText(/Fake backend received: archived startup regression/);
+  await composer.fill('archived startup regression');
+  await awaitSendReady(page);
+  await composer.press('Enter');
+  await expect(reply).toBeVisible({ timeout: 20_000 });
+
+  // Prove bootstrap can restore this history before archiving it.
+  await page.reload();
+  await expect(reply).toBeVisible();
+  await page.evaluate(async () => {
+    const sessions = await window.maka.sessions.list();
+    for (const session of sessions) await window.maka.sessions.archive(session.id);
+  });
+  await expect.poll(async () =>
+    page.evaluate(async () => (await window.maka.sessions.list()).map(({ isArchived }) => isArchived)),
+  ).toEqual([true]);
+  // A cold window has no explicit new-task reload intent. Keep this test on
+  // automatic bootstrap even if retirement starts recording that intent.
+  await page.evaluate(() => sessionStorage.removeItem('maka-new-task-reload-intent-v1'));
+  await page.reload();
+  await expect(composer).toBeVisible();
+  await ensureSidebarExpanded(page);
+  await expect(page.locator('[data-session-id]')).toHaveCount(0);
+  try {
+    await expect(page.locator('.maka-titlebar-identity__segment--session')).toHaveCount(0);
+    await expect(page.locator('.maka-turn')).toHaveCount(0);
+  } finally {
+    await testInfo.attach('archived-only-startup', {
+      body: await page.screenshot({ animations: 'disabled' }),
+      contentType: 'image/png',
+    });
+  }
+
+  await composer.fill('new task after archived history');
+  await awaitSendReady(page);
+  await composer.press('Enter');
+  await expect(page.getByText(/Fake backend received: new task after archived history/))
+    .toBeVisible({ timeout: 20_000 });
+  await expect.poll(async () =>
+    page.evaluate(async () => {
+      const sessions = await window.maka.sessions.list();
+      return {
+        archived: sessions.filter((session) => session.isArchived).length,
+        active: sessions.filter((session) => !session.isArchived).length,
+      };
+    }),
+  ).toEqual({ archived: 1, active: 1 });
+});
+
 test('an explicit new task survives a renderer reload without reopening history', async ({
   window: page,
 }) => {
diff --git a/apps/desktop/src/main/__tests__/bootstrap-selection-lease.test.ts b/apps/desktop/src/main/__tests__/bootstrap-selection-lease.test.ts
index 3179171fa3..8c64b21180 100644
--- a/apps/desktop/src/main/__tests__/bootstrap-selection-lease.test.ts
+++ b/apps/desktop/src/main/__tests__/bootstrap-selection-lease.test.ts
@@ -30,10 +30,10 @@ import {
   writeNewTaskReloadDraft,
 } from '../../renderer/new-task-reload-intent.js';
 
-type Summary = { id: string; lastMessageAt?: number };
+type Summary = { id: string; lastMessageAt?: number; isArchived: boolean };
 
-function session(id: string, lastMessageAt?: number): Summary {
-  return { id, lastMessageAt };
+function session(id: string, lastMessageAt?: number, isArchived = false): Summary {
+  return { id, lastMessageAt, isArchived };
 }
 
 function harness(activeId?: string) {
@@ -58,6 +58,60 @@ function harness(activeId?: string) {
 }
 
 describe('bootstrap selection lease', () => {
+  it('leaves the new-task surface selected when all history is archived', () => {
+    const state = harness();
+    state.lease.reconcile([session('archived', 2, true), session('older', 1, true)]);
+    assert.equal(state.activeId(), undefined);
+  });
+
+  it('skips archived rows when choosing the first unarchived conversation', () => {
+    const state = harness();
+    state.lease.reconcile([
+      session('archived', 3, true),
+      session('recent', 2),
+      session('older', 1),
+    ]);
+    assert.equal(state.activeId(), 'recent');
+  });
+
+  it('revalidates archive eligibility across bootstrap snapshots', () => {
+    const state = harness();
+    state.lease.reconcile([session('recent', 2), session('older', 1)]);
+    assert.equal(state.activeId(), 'recent');
+    state.lease.reconcile([session('recent', 2, true), session('older', 1)]);
+    assert.equal(state.activeId(), 'older');
+    state.lease.reconcile([session('recent', 2, true), session('older', 1, true)]);
+    assert.equal(state.activeId(), undefined);
+  });
+
+  it('does not reopen older history behind an empty unarchived task', () => {
+    const state = harness();
+    state.lease.reconcile([
+      session('archived', 2, true),
+      session('empty'),
+      session('history', 1),
+    ]);
+    assert.equal(state.activeId(), undefined);
+  });
+
+  it('preserves an eligible bootstrap selection when newer history arrives', () => {
+    const state = harness();
+    state.lease.reconcile([session('selected', 1)]);
+    state.lease.reconcile([session('newer', 2), session('selected', 1)]);
+    assert.equal(state.activeId(), 'selected');
+  });
+
+  it('does not override explicitly opened archived history', () => {
+    const state = harness();
+    state.lease.reconcile([session('active', 1)]);
+    state.select('archived');
+    assert.equal(
+      state.lease.reconcile([session('archived', 2, true), session('active', 1)]),
+      false,
+    );
+    assert.equal(state.activeId(), 'archived');
+  });
+
   it('lets snapshot A and mounted pull B reconcile while bootstrap still owns selection', () => {
     const state = harness();
     assert.equal(state.lease.reconcile([session('a', 1)]), true);
diff --git a/apps/desktop/src/renderer/bootstrap-selection-lease.ts b/apps/desktop/src/renderer/bootstrap-selection-lease.ts
index 3a1cacb766..622eb2e384 100644
--- a/apps/desktop/src/renderer/bootstrap-selection-lease.ts
+++ b/apps/desktop/src/renderer/bootstrap-selection-lease.ts
@@ -17,12 +17,16 @@
  * under the License.
  */
 
-export interface BootstrapSelectionLease<Summary extends { id: string; lastMessageAt?: number }> {
+import type { SessionSummary } from '@maka/core/session';
+
+type BootstrapSession = Pick<SessionSummary, 'id' | 'lastMessageAt' | 'isArchived'>;
+
+export interface BootstrapSelectionLease<Summary extends BootstrapSession> {
   reconcile(sessions: readonly Summary[]): boolean;
   release(): void;
 }
 
-export function createBootstrapSelectionLease<Summary extends { id: string; lastMessageAt?: number }>(options: {
+export function createBootstrapSelectionLease<Summary extends BootstrapSession>(options: {
   readActiveId: () => string | undefined;
   readSelectionRevision: () => number;
   select: (sessionId: string | undefined) => void;
@@ -37,11 +41,13 @@ export function createBootstrapSelectionLease<Summary extends { id: string; last
         return false;
       }
 
+      // The catalog includes archived history; automatic selection must not.
+      const candidates = sessions.filter((session) => !session.isArchived);
       const current = options.readActiveId();
-      const next = current && sessions.some((session) => session.id === current)
+      const next = current && candidates.some((session) => session.id === current)
         ? current
-        : sessions[0]?.lastMessageAt
-          ? sessions[0].id
+        : candidates[0]?.lastMessageAt
+          ? candidates[0].id
           : undefined;
       if (next !== current) options.select(next);
       ownedRevision = options.readSelectionRevision();


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@maka.apache.org
For additional commands, e-mail: commits-help@maka.apache.org