Backend: GET /health/dwh (unauthenticated) calls tht db ping with a 5s
timeout. Frontend: checkDwhHealth() races a 5s timer against the fetch;
on failure a non-dismissable Dialog with Retry appears immediately.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Opening an in-progress session that has a live Pi runtime now reconnects to
its pending gate instead of the empty landing screen that read as "stopped".
Cold/completed sessions keep the read-only documents panel with its explicit
Resume, so a mere click never spawns a runtime. Backend GET /sessions now
reports a per-session `active` flag (live runtime bound) to drive this.
Also:
- "New session" now closes any open session detail panel (left box).
- The model-activity separator can be dragged to a full 50/50 split
(was capped at 576px); central-min still guards narrow viewports.
Test fixes uncovered along the way:
- Node 25 ships an experimental global localStorage that shadows jsdom's and
lacks clear(), failing every jsdom test at setup; install a spec-compliant
in-memory Storage (feature-detected, inert on CI/LTS).
- Fix 4 pre-existing session-mgmt tests that used an ambiguous getByText for a
session shown in both nav and header; target the nav item by test id.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adopt the portal's design language so the app embeds seamlessly into its
header/sidebar chrome later:
- Token layer rewritten as raw OKLCH triplets (the previous oklch()-wrapped
vars produced invalid colors; the UI was effectively unstyled). Values mirror
GSD: Manrope + Fraunces fonts, #cb333b red brand, warm off-white surfaces,
pill buttons, brand focus rings. Dark tokens follow .dark or the portal's
[data-bs-theme=dark].
- Layout: no left sidebar, no header (the portal supplies them). Conversation
column flush-left with a slim F1-F8 phase strip; session rail moved to the
RIGHT, mirroring the portal's left sidebar (red section label, red-tint active
item). Editorial empty state.
- Transcript component now actually renders the streamed assistant turns (the
store accumulated them but nothing displayed them). Toaster mounted for errors
(matchMedia polyfill added to the jsdom test setup).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
FakeEventSource.addEventListener was a no-op, so emit() only drove onmessage.
Production relies on addEventListener for the backend's NAMED events
(event: ui_request), so the unit tests could pass while prod silently broke.
- fakeEventSource: store named handlers in a Map<string,Set>; add emitNamed()
that dispatches to them; keep emit() for the unnamed/default onmessage path
- useSessionStream.test: ui_request now driven via emitNamed (production path);
add a separate test for the unnamed text_delta path via plain emit
- f1-loop.test: widget emission switched to emitNamed("ui_request", ...)
- verified: tests FAIL if addEventListener wiring is removed from
useSessionStream (then restored)
- cleanup: fake-pi.mjs drops unused execFileSync import, uses static spawnSync
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a real-browser Playwright e2e of the full F1 disambiguation loop,
driven against the real backend + fake binaries (no VPN, no real Pi, no Python).
- frontend/e2e/fixtures/fake-tht.mjs: stubs tht CLI (session new/list/show)
- frontend/e2e/fixtures/fake-pi.mjs: wraps harness fake-pi-rpc with f1_disambiguation.json
- frontend/playwright.config.ts: two webServer entries (backend:8799, frontend:5199)
- frontend/e2e/f1.spec.ts: open app → create session → wait for SelectWidget → respond
Bug fixes discovered during e2e:
- useSessionStream: add addEventListener for named SSE events (backend sends
'event: ui_request' etc.; onmessage only fires for unnamed 'event: message')
- FakeEventSource: add no-op addEventListener/removeEventListener stubs
- backend SSE route: add CORS headers manually in writeHead() since
reply.raw bypasses the @fastify/cors onSend hook
- backend: install and register @fastify/cors for all non-SSE routes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
TDD: App-renders-ThothII test RED then GREEN. ShadCn v4 (base-nova style)
init + button/checkbox/radio-group/textarea/card/dialog/badge/sonner added.
tailwind.config.ts extended with oklch CSS-variable color mapping required
by ShadCn v4 @apply directives. npm test passes; npm run build clean.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>