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