Problem
Save, create and delete in the Library rules section call fetch, then immediately update local state and revalidate. A 403 or 500 leaves the UI showing the edit as applied. The same shape appears in ten other components.
Where
components/library/rules-section.tsx:28-55 (saveFile, createFile, deleteFile).
components/library/context-section.tsx (five fetches, no error handling).
components/panes/output-pane.tsx, components/panes/connections-pane.tsx, components/panes/agent-roster-pane.tsx, components/panes/diff-pane.tsx.
app/(dashboard)/[scope]/assignments/page.tsx, components/agent-editor.tsx, components/app-sidebar.tsx, components/top-bar.tsx, components/active-scope-provider.tsx.
Helpers that already exist: lib/client-fetch.ts has readJson and error-message extraction. The app already has a toast component (check components/ui/ for sonner or toast).
Fix
- Add
mutateJson(url, init) to lib/client-fetch.ts: performs the fetch, throws an Error with the server's message when !res.ok, returns parsed JSON otherwise.
- Replace every bare mutation
fetch in the files above with mutateJson inside try/catch. On error, show a toast with the message and do not update local state.
- Work file by file. One PR per two or three files is fine; link them all here.
Exit criteria
Out of scope
Adding AbortController to the GET fetches in the same files. Separate issue.
https://claude.ai/code/session_01SQ4nS96XYRztd5w9QkubPf
Problem
Save, create and delete in the Library rules section call
fetch, then immediately update local state and revalidate. A 403 or 500 leaves the UI showing the edit as applied. The same shape appears in ten other components.Where
components/library/rules-section.tsx:28-55(saveFile,createFile,deleteFile).components/library/context-section.tsx(five fetches, no error handling).components/panes/output-pane.tsx,components/panes/connections-pane.tsx,components/panes/agent-roster-pane.tsx,components/panes/diff-pane.tsx.app/(dashboard)/[scope]/assignments/page.tsx,components/agent-editor.tsx,components/app-sidebar.tsx,components/top-bar.tsx,components/active-scope-provider.tsx.Helpers that already exist:
lib/client-fetch.tshasreadJsonand error-message extraction. The app already has a toast component (checkcomponents/ui/forsonnerortoast).Fix
mutateJson(url, init)tolib/client-fetch.ts: performs the fetch, throws anErrorwith the server's message when!res.ok, returns parsed JSON otherwise.fetchin the files above withmutateJsoninside try/catch. On error, show a toast with the message and do not update local state.Exit criteria
mutateJsonhas a vitest test inlib/client-fetch.test.tscovering the OK path and the error path with a server message.rg -n 'await fetch\(' components/library components/panes app/\(dashboard\)/\[scope\]/assignmentsreturns no mutation calls (GET fetches inside SWR fetchers are fine).PUT /api/rulesin DevTools, edit a rule, confirm a toast appears and the editor keeps the unsaved content.pnpm lint,pnpm typecheck,pnpm testpass.Out of scope
Adding
AbortControllerto the GET fetches in the same files. Separate issue.https://claude.ai/code/session_01SQ4nS96XYRztd5w9QkubPf