Skip to content

Commit 3e8410d

Browse files
authored
🤖 fix: deterministic project sidebar sort order (#1244)
Sort missing projects lexically in `normalizeOrder()` to ensure deterministic order when projects aren't in the persisted `projectOrder` (e.g., in Storybook where localStorage may be empty). **Root cause:** When projects were missing from the persisted `projectOrder`, `normalizeOrder` would prepend them using `Array.from(projects.keys())` which depends on Map iteration order. This order is determined by insertion order into the Map, which could vary based on how `groupWorkspacesByProject` built it. **Fix:** Sort the missing projects lexically before prepending to ensure deterministic order. --- _Generated with `mux` • Model: `anthropic:claude-opus-4-5` • Thinking: `high`_
1 parent 900af54 commit 3e8410d

File tree

2 files changed

+19
-2
lines changed

2 files changed

+19
-2
lines changed

src/common/utils/projectOrdering.test.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,27 @@ describe("projectOrdering", () => {
6969
expect(result).toEqual(["/a", "/b"]);
7070
});
7171

72-
it("prepends new projects to the front", () => {
72+
it("prepends new projects to the front in lexical order", () => {
7373
const projects = createProjects(["/a", "/b", "/c", "/d"]);
7474
const order = ["/b", "/a"];
7575
const result = normalizeOrder(order, projects);
76+
// /c and /d are missing from order, prepended in lexical order
7677
expect(result).toEqual(["/c", "/d", "/b", "/a"]);
7778
});
7879

80+
it("sorts missing projects lexically for deterministic order", () => {
81+
// Even if Map iteration order is non-lexical, missing projects should be sorted
82+
const projects = new Map<string, ProjectConfig>([
83+
["/z-project", { workspaces: [] }],
84+
["/a-project", { workspaces: [] }],
85+
["/m-project", { workspaces: [] }],
86+
]);
87+
const order: string[] = []; // empty order, all projects are "missing"
88+
const result = normalizeOrder(order, projects);
89+
// Should be lexically sorted regardless of Map insertion order
90+
expect(result).toEqual(["/a-project", "/m-project", "/z-project"]);
91+
});
92+
7993
it("preserves order of existing projects", () => {
8094
const projects = createProjects(["/a", "/b", "/c"]);
8195
const order = ["/c", "/a", "/b"];

src/common/utils/projectOrdering.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,10 @@ export function reorderProjects(
6868
export function normalizeOrder(order: string[], projects: Map<string, ProjectConfig>): string[] {
6969
const present = new Set(projects.keys());
7070
const filtered = order.filter((p) => present.has(p));
71-
const missing = Array.from(projects.keys()).filter((p) => !filtered.includes(p));
71+
// Sort missing projects lexically for deterministic order (avoids flaky UI in Storybook)
72+
const missing = Array.from(projects.keys())
73+
.filter((p) => !filtered.includes(p))
74+
.sort((a, b) => a.localeCompare(b));
7275
return [...missing, ...filtered];
7376
}
7477

0 commit comments

Comments
 (0)