From f1a8c41e08560ee7f4c8bac1abb27772ef255dbb Mon Sep 17 00:00:00 2001 From: Naiyuan Qing <145280634+NevilleQingNY@users.noreply.github.com> Date: Mon, 15 Jun 2026 11:31:56 +0800 Subject: [PATCH] fix(agents): backfill new filter dimensions on rehydrate (owners crash) A view payload persisted before the owners filter existed overwrote the default filters wholesale on rehydrate, dropping filters.owners to undefined and crashing the list's filter predicate (.length on undefined). The store merge now deep-merges filters over EMPTY_AGENT_FILTERS so newly-added dimensions always get their default. Regression test added. Co-Authored-By: Claude Fable 5 --- .../core/agents/stores/view-store.test.ts | 21 +++++++++++++++++++ packages/core/agents/stores/view-store.ts | 10 ++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/packages/core/agents/stores/view-store.test.ts b/packages/core/agents/stores/view-store.test.ts index 3f9ce176b..7cb5397da 100644 --- a/packages/core/agents/stores/view-store.test.ts +++ b/packages/core/agents/stores/view-store.test.ts @@ -100,4 +100,25 @@ describe("useAgentsViewStore", () => { expect(useAgentsViewStore.getState().scope).toBe("mine"); expect(localStorage.getItem("multica_agents_view:acme")).not.toBeNull(); }); + + it("backfills new filter dimensions when rehydrating a pre-owners payload", async () => { + // A payload persisted before the `owners` filter existed must not drop + // the key to undefined (the agents list filter predicate reads + // `filters.owners.length` and would crash). + localStorage.setItem( + "multica_agents_view:acme", + JSON.stringify({ + state: { filters: { availability: ["online"], runtimes: [] } }, + version: 0, + }), + ); + + setCurrentWorkspace("acme", "ws_a"); + await flush(); + await flush(); + + const filters = useAgentsViewStore.getState().filters; + expect(filters.owners).toEqual([]); + expect(filters.availability).toEqual(["online"]); + }); }); diff --git a/packages/core/agents/stores/view-store.ts b/packages/core/agents/stores/view-store.ts index e61f8d02a..1cf0b0e5a 100644 --- a/packages/core/agents/stores/view-store.ts +++ b/packages/core/agents/stores/view-store.ts @@ -171,7 +171,15 @@ export const useAgentsViewStore = create()( // persisted is undefined, which would leak state across workspaces. merge: (persisted, current) => { if (!persisted) return { ...current, ...DEFAULTS }; - return { ...current, ...(persisted as Partial) }; + const p = persisted as Partial; + // Deep-merge filters so a payload persisted before a new filter + // dimension existed (e.g. `owners`) still gets that key's default + // instead of dropping it to `undefined` and crashing `.length`. + return { + ...current, + ...p, + filters: { ...EMPTY_AGENT_FILTERS, ...(p.filters ?? {}) }, + }; }, }, ),