Skip to content

Commit 9f75d18

Browse files
committed
fix(web): drop dead no-arg listTasks + invalidate overview on runtime-limit WS
Per #143 review (REQUEST_CHANGES), two fixes: 1. Must-fix #1 — bare listTasks() URL /tasks/tasks: The retained no-arg overload did request(`${API_BASE}/tasks`) while API_BASE is already '/tasks' (regression re-introduced when migrating files off an older branch state). After the dashboard migrated to /projects/overview, the no-arg path has no remaining caller, so the overload + bare branch are removed entirely. listTasks(projectId) is the only signature now. 2. Must-fix #2 — WS runtime_limit_updated leaves overview metrics stale: project:runtime_limit_updated fired but only invalidated ['tasks'], skipping the new overview query. Since overview aggregates token/cost fields, the dashboard header + project cards showed stale token/cost after usage updates. Add invalidateProjectTaskOverviews(queryClient) in that branch. Regression test: useWebSocketOverviewInvalidation.test pins that invalidateProjectTaskOverviews invalidates ['projectTaskOverviews'], so a future change that drops/renames the query key surfaces immediately. Verification: tsc clean (web); 675 web tests green; prettier/lint/build green.
1 parent cae0059 commit 9f75d18

3 files changed

Lines changed: 33 additions & 10 deletions

File tree

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { describe, it, expect, vi } from "vitest";
2+
import { QueryClient } from "@tanstack/react-query";
3+
import { invalidateProjectTaskOverviews } from "@/hooks/useProjects";
4+
5+
/**
6+
* Regression for the WS overview-invalidation gap (#143 review, must-fix #2).
7+
*
8+
* `project:runtime_limit_updated` fires when persisted runtime-limit / last
9+
* usage state changes. The overview aggregates token/cost fields, so this
10+
* event must invalidate the `["projectTaskOverviews"]` query — otherwise the
11+
* dashboard header and project cards show stale token/cost after usage
12+
* updates. The useWebSocket handler calls `invalidateProjectTaskOverviews`
13+
* (from useProjects) in that branch; this test pins that helper so a future
14+
* change that drops or renames the query key surfaces immediately.
15+
*/
16+
describe("invalidateProjectTaskOverviews (WS runtime_limit_updated branch)", () => {
17+
it("invalidates the projectTaskOverviews query", () => {
18+
const queryClient = new QueryClient();
19+
const invalidateSpy = vi.spyOn(queryClient, "invalidateQueries");
20+
21+
invalidateProjectTaskOverviews(queryClient);
22+
23+
expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ["projectTaskOverviews"] });
24+
});
25+
});

packages/web/src/hooks/useWebSocket.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,8 @@ export function useWebSocket() {
227227
pendingTaskIds.current.add(data.payload.taskId);
228228
queryClient.invalidateQueries({ queryKey: ["tasks"] });
229229
}
230+
// Overview aggregates token/cost fields, so refresh after usage updates.
231+
invalidateProjectTaskOverviews(queryClient);
230232
return;
231233
}
232234

packages/web/src/lib/api.ts

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -254,16 +254,12 @@ async function request<T>(
254254
return res.json();
255255
}
256256

257-
// Task list fetch. Overloaded: bare listTasks() returns full Task[] (legacy
258-
// dashboard path, until consumers migrate to GET /projects/overview);
259-
// listTasks(projectId) returns lightweight TaskListItem[] (board/list).
260-
function listTasks(): Promise<Task[]>;
261-
function listTasks(projectId: string): Promise<TaskListItem[]>;
262-
function listTasks(projectId?: string): Promise<Task[] | TaskListItem[]> {
263-
if (projectId === undefined) {
264-
console.debug("[api] GET /tasks (bare, legacy)");
265-
return request<Task[]>(`${API_BASE}/tasks`);
266-
}
257+
// Task list fetch. projectId is required: the board/list view is always scoped.
258+
// The dashboard moved to GET /projects/overview (see #139), so the bare no-arg
259+
// listTasks() path has no remaining caller and is removed. The server route
260+
// still answers bare requests for backward compatibility, but the web client
261+
// never calls it.
262+
function listTasks(projectId: string): Promise<TaskListItem[]> {
267263
const qs = `?projectId=${encodeURIComponent(projectId)}`;
268264
console.debug("[api] GET /tasks?projectId=%s", projectId);
269265
return request<TaskListItem[]>(`${API_BASE}${qs}`);

0 commit comments

Comments
 (0)