diff --git a/docs/superpowers/plans/2026-07-31-error-log-bell.md b/docs/superpowers/plans/2026-07-31-error-log-bell.md new file mode 100644 index 0000000..9a0f9c6 --- /dev/null +++ b/docs/superpowers/plans/2026-07-31-error-log-bell.md @@ -0,0 +1,549 @@ +# Error Log Notification Bell Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Add a notification bell to the WebUI header that shows the current user's persisted error logs with an unread badge and a "clear all" action. + +**Architecture:** The backend is already complete and committed: `src/lib/server/errorLogStore.ts` (per-user sqlite log, 200-entry cap), `src/app/api/error-logs/route.ts` (GET/POST/DELETE), `src/lib/errorReporter.ts` (`reportError`/`errorToast`/`subscribeErrorLogged`, wired into 13 components), and `src/lib/server/routeErrors.ts` (BFF errors recorded for the acting user). This plan builds the only missing piece: the header UI. A thin SWR hook polls `/api/error-logs` (30s) and revalidates instantly when `errorReporter` signals a new entry; unread state is a `localStorage` cursor over monotonically increasing log ids; all badge math lives in a pure, node-testable module because vitest runs in `environment: "node"` (no DOM). + +**Tech Stack:** Next.js App Router, React 19, SWR 2, lucide-react icons, shadcn/ui (`@/components/ui/dropdown-menu`, `button`, `scroll-area`), vitest (node env, `.test.ts` only). + +## Global Constraints + +- Repo root for all paths/commands: `/Users/m4/Projects/EvoSci/OriginEvoScientist/EvoScientist-WebUI` +- Test runner: `npm run test` (vitest run, `environment: "node"`, includes only `src/**/*.test.ts` — no component/DOM tests; logic must be extractable to pure TS) +- Lint: `npm run lint`; format: `npm run format:check` +- All UI strings in English (existing UI copy is English: "New chat", "Sign out", "Backend unavailable.") +- Header icon buttons use ` + + +
+ Error log +
+ {loading && logs.length === 0 ? ( +

+ Loading... +

+ ) : logs.length === 0 ? ( +

+ No errors logged. +

+ ) : ( + + + + )} + {logs.length > 0 && ( +
+ +
+ )} +
+ + ); +} +``` + +- [ ] **Step 2: Typecheck and lint** + +Run: `npx tsc --noEmit && npx eslint src/app/components/ErrorLogBell.tsx` +Expected: no errors + +- [ ] **Step 3: Commit** + +```bash +git add src/app/components/ErrorLogBell.tsx +git commit -m "feat(webui): error-log bell with unread badge and clear-all" +``` + +--- + +### Task 4: Mount the bell in the header + +**Files:** +- Modify: `src/app/page.tsx` (header right-side icon group, currently lines 357-359: `` then ``) + +**Interfaces:** +- Consumes: Task 3's `ErrorLogBell` +- Produces: nothing new + +- [ ] **Step 1: Add the import** + +In `src/app/page.tsx`, add with the other component imports (keep the existing import grouping/sorting style): + +```ts +import { ErrorLogBell } from "@/app/components/ErrorLogBell"; +``` + +- [ ] **Step 2: Render the bell** + +In `src/app/page.tsx`, inside the header's right-side container `
`, insert immediately before ``: + +```tsx + +``` + +Render it unconditionally: when WebUI auth is disabled, `requireActor` resolves every request to the built-in `local-admin` user, so the API and the bell work in single-user mode too. + +- [ ] **Step 3: Typecheck, lint, full test suite** + +Run: `npx tsc --noEmit && npm run lint && npm run test` +Expected: no type/lint errors; all existing tests pass (no new tests in this task) + +- [ ] **Step 4: Browser verification** + +Terminal 1 — backend (per project memory, must use the source-tree script so tokens align): + +```bash +/Users/m4/Projects/EvoSci/OriginEvoScientist/EvoScientist/scripts/dev_backend.sh +``` + +Terminal 2 — WebUI: + +```bash +cd /Users/m4/Projects/EvoSci/OriginEvoScientist/EvoScientist-WebUI && npm run dev +``` + +Then in the browser at `http://localhost:4716` (sign in as admin/admin123 if auth is enabled): + +1. Force an error: open devtools console and run `fetch("/api/error-logs", {method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({source:"manual.test",message:"Bell smoke test"})})` +2. Within ~1s the bell shows badge "1" (SWR revalidate via `subscribeErrorLogged` fires only for in-tab `reportError` calls, so this console POST appears on the next 30s poll or focus revalidate — to test the instant path instead, run: `window.dispatchEvent(new Event("focus"))` after focusing, or simply wait for the poll; the badge MUST show "1" within 30s) +3. Click the bell → panel opens, entry "Bell smoke test" visible with source `manual.test`; badge cleared +4. Re-open the panel → "Clear all" → button switches to "Click again to clear all" → click again → list empties, footer disappears +5. Trigger a real UI error (e.g. rename a thread to an existing name or disconnect the backend and send a chat message) → toast appears AND bell badge increments within ~1s (instant path via `errorToast` → `reportError` → `subscribeErrorLogged`) + +- [ ] **Step 5: Commit** + +```bash +git add src/app/page.tsx +git commit -m "feat(webui): mount error-log bell in the header" +``` + +--- + +## Self-Review Notes + +- Spec coverage: the approved design had four parts — (1) server store, (2) collection, (3) BFF API, (4) bell UI. Parts 1-3 are already implemented and committed (`61c2aba`, `8d455a0`, `dd4b63a`, `09d9f49`); this plan covers only part 4. The design's "SWR 30s poll OR instant refresh" is implemented as both. +- Placeholders: none — every code step contains complete code. +- Type consistency: `ErrorLogEntry` shape matches `src/lib/server/errorLogStore.ts`; `useErrorLogs` return value `{ logs, unread, loading, markAllRead, clearAll }` is what Task 3 consumes; Task 1 exports match Task 2's imports verbatim.