refactor: stop persisting workspace state in the browser

This commit is contained in:
2026-08-14 17:43:10 +02:00
parent 3978008aed
commit fd3b62ce6f
11 changed files with 101 additions and 310 deletions
@@ -6,6 +6,7 @@ import { server } from "../test/msw";
import { FakeEventSource } from "../test/fakeEventSource";
import { useSessionStore } from "../store/sessionStore";
import { AppShell } from "./AppShell";
import { workspacePreferences } from "../workspaces/preferences";
function renderShell() {
const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
@@ -143,8 +144,7 @@ test("model selector shows the three Pi-enabled models and stores the selected p
]);
});
await userEvent.selectOptions(select, "qwen3.6-35b-a3b");
await waitFor(() => expect(JSON.parse(localStorage.getItem("thothii.workspace-registry.v1.preferences")!))
.toEqual({
await waitFor(() => expect(workspacePreferences.load()).toEqual({
workspaceId: "default", provider: "local-qwen", model: "qwen3.6-35b-a3b", thinking: "low",
}));
});
+4 -3
View File
@@ -6,6 +6,7 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { server } from "../test/msw";
import { canonicalWorkspaceFixture, workspaceRevisionFixture, workspaceSummaryFixture } from "../test/workspace-fixtures";
import { NewSessionDialog } from "./NewSessionDialog";
import { workspacePreferences } from "../workspaces/preferences";
function renderDialog() {
const onCreated = vi.fn();
@@ -28,7 +29,7 @@ test("the form has only a question field (no workspace/model/provider/thinking)"
expect(screen.queryByLabelText(/thinking/i)).not.toBeInTheDocument();
});
test("submitting includes browser-local migrated preferences and calls onCreated", async () => {
test("submitting includes ephemeral migrated preferences and calls onCreated", async () => {
localStorage.clear();
let body: unknown = null;
server.use(
@@ -71,9 +72,9 @@ test("first-run direct dialog creation waits for registry policy without a mount
const revision = {
id: "psd-clinical", commit: "a".repeat(40), blob: "b".repeat(40), snapshotPath: "/snapshot",
};
localStorage.setItem("thothii.workspace-registry.v1.preferences", JSON.stringify({
workspacePreferences.save({
workspaceId: "psd-clinical", provider: "deepseek", model: "deepseek-v4-pro", thinking: "medium",
}));
});
server.use(
http.get("/api/workspaces", () => {
summaryRequestStarted = true;
+17 -16
View File
@@ -6,6 +6,7 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { server } from "../test/msw";
import { canonicalWorkspaceFixture, workspaceSummaryFixture } from "../test/workspace-fixtures";
import { useSessionStore } from "../store/sessionStore";
import { workspacePreferences } from "../workspaces/preferences";
import { ComposerFooter, ContextGauge, SteerInput } from "./SteerInput";
beforeEach(() => {
@@ -17,11 +18,11 @@ beforeEach(() => {
);
});
test("new sessions send the browser-selected workspace, model, provider, and thinking", async () => {
test("new sessions send the ephemeral workspace, model, provider, and thinking", async () => {
let body: unknown;
localStorage.setItem("thothii.workspace-registry.v1.preferences", JSON.stringify({
workspacePreferences.save({
workspaceId: "psd-clinical", provider: "zai", model: "glm-5.2", thinking: "high",
}));
});
server.use(
http.get("/api/workspaces", () => HttpResponse.json([{
...workspaceSummaryFixture("psd-clinical", {
@@ -203,9 +204,9 @@ test("footer limits model choices to the selected workspace policy", async () =>
test("switching workspaces replaces an out-of-policy model before session creation", async () => {
let body: unknown;
localStorage.setItem("thothii.workspace-registry.v1.preferences", JSON.stringify({
workspacePreferences.save({
workspaceId: "research", provider: "deepseek", model: "deepseek-v4-pro", thinking: "medium",
}));
});
const revision = (id: string) => ({
id, commit: "a".repeat(40), blob: "b".repeat(40), snapshotPath: "/snapshot",
});
@@ -256,9 +257,9 @@ test("immediate submit waits for a switched workspace policy before creating a s
let releasePolicy!: () => void;
let policyRequestStarted = false;
const policyMayFinish = new Promise<void>((resolve) => { releasePolicy = resolve; });
localStorage.setItem("thothii.workspace-registry.v1.preferences", JSON.stringify({
workspacePreferences.save({
workspaceId: "research", provider: "deepseek", model: "deepseek-v4-pro", thinking: "medium",
}));
});
const revision = (id: string) => ({
id, commit: "a".repeat(40), blob: "b".repeat(40), snapshotPath: "/snapshot",
});
@@ -312,9 +313,9 @@ test("initial restored workspace waits for its delayed policy before creating a
let releasePolicy!: () => void;
let policyRequestStarted = false;
const policyMayFinish = new Promise<void>((resolve) => { releasePolicy = resolve; });
localStorage.setItem("thothii.workspace-registry.v1.preferences", JSON.stringify({
workspacePreferences.save({
workspaceId: "psd-clinical", provider: "deepseek", model: "deepseek-v4-pro", thinking: "medium",
}));
});
const revision = (id: string) => ({
id, commit: "a".repeat(40), blob: "b".repeat(40), snapshotPath: "/snapshot",
});
@@ -359,9 +360,9 @@ test("initial restored workspace waits for its delayed policy before creating a
test("initial submit rejects a workspace summary that omits the canonical revision", async () => {
let body: unknown;
let failure: string | undefined;
localStorage.setItem("thothii.workspace-registry.v1.preferences", JSON.stringify({
workspacePreferences.save({
workspaceId: "broken-workspace", provider: "zai", model: "glm-5.2", thinking: "medium",
}));
});
server.use(
http.get("/api/settings", () => HttpResponse.json({ workspace: "broken-workspace" })),
http.get("/api/workspaces", () => HttpResponse.json([{
@@ -384,7 +385,7 @@ test("initial submit rejects a workspace summary that omits the canonical revisi
await userEvent.type(screen.getByRole("textbox", { name: /new question/i }), "q");
await userEvent.click(screen.getByRole("button", { name: /send/i }));
await waitFor(() => expect(failure).toBe("Could not load selected workspace policy. Please retry."));
await waitFor(() => expect(failure).toBe("Could not load workspace registry. Please retry."));
expect(body).toBeUndefined();
});
@@ -392,9 +393,9 @@ test("failed workspace summaries block creation and report a safe error", async
let body: unknown;
let failure: string | undefined;
let summaryRequestFailed = false;
localStorage.setItem("thothii.workspace-registry.v1.preferences", JSON.stringify({
workspacePreferences.save({
workspaceId: "psd-clinical", provider: "zai", model: "glm-5.2", thinking: "medium",
}));
});
server.use(
http.get("/api/settings", () => HttpResponse.json({ workspace: "psd-clinical" })),
http.get("/api/workspaces", () => {
@@ -432,9 +433,9 @@ test("submit follows a rapid workspace switch instead of waiting for an abandone
let bPolicyRequestStarted = false;
let cPolicyRequestStarted = false;
const cPolicyMayFinish = new Promise<void>((resolve) => { releaseC = resolve; });
localStorage.setItem("thothii.workspace-registry.v1.preferences", JSON.stringify({
workspacePreferences.save({
workspaceId: "research", provider: "deepseek", model: "deepseek-v4-pro", thinking: "medium",
}));
});
const revision = (id: string) => ({
id, commit: "a".repeat(40), blob: "b".repeat(40), snapshotPath: "/snapshot",
});
+1 -1
View File
@@ -156,7 +156,7 @@ export function SteerInput({
/**
* Status strip beneath the composer. Left: the active workspace selector.
* Right: live context usage plus browser-local workspace/model/thinking selectors.
* Right: live context usage plus ephemeral workspace/model/thinking selectors.
*/
export function ComposerFooter() {
const { data: settings } = useQuery({ queryKey: ["settings"], queryFn: getSettings });