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 <noreply@anthropic.com>
This commit is contained in:
Naiyuan Qing
2026-06-15 11:31:56 +08:00
parent d5494fb20e
commit f1a8c41e08
2 changed files with 30 additions and 1 deletions

View File

@@ -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"]);
});
});

View File

@@ -171,7 +171,15 @@ export const useAgentsViewStore = create<AgentsViewState>()(
// persisted is undefined, which would leak state across workspaces.
merge: (persisted, current) => {
if (!persisted) return { ...current, ...DEFAULTS };
return { ...current, ...(persisted as Partial<AgentsViewState>) };
const p = persisted as Partial<AgentsViewState>;
// 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 ?? {}) },
};
},
},
),