feat: add AI request tracing and structured client logging - #1
feat: add AI request tracing and structured client logging#1TrainTravel wants to merge 12 commits into
Conversation
Adds 25 tests across 5 spec files covering all main user flows: home screen, full journal flow (mocked AI), brain dump, section navigation, and language toggle/persistence. All Supabase and edge function calls are intercepted so tests run offline and deterministically. - playwright.config.ts: Chromium, baseURL localhost:8080, auto-starts dev server - e2e/helpers/mocks.ts: shared route mocks and localStorage helpers - e2e/home.spec.ts: 6 rendering tests - e2e/journal-flow.spec.ts: happy path, skip-gratitude, BreatheScreen clock test - e2e/braindump.spec.ts: input, submission, and navigation - e2e/navigation.spec.ts: each section open/back - e2e/language.spec.ts: toggle, persistence after reload, bilingual order - .github/workflows/e2e.yml: runs on push/PR to main, uploads HTML report - docs/e2e-tests.md: permanent summary of all tests and helpers - CLAUDE.md: add Agentic AI Development & Iteration guidelines, update Important Notes to reference new test suite Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Language type extended to 'fr' | 'en' | 'es' - Toggle cycles FR→EN→ES→FR; LanguageToggle shows FR/EN/ES label - t(fr, en, es) and bilingual(fr, en, es) now take 3 required args - isEs added to LanguageContext alongside existing isFr - bilingual() in ES mode renders "ES / EN" (English anchor) - EmotionWord.es and EmotionSuggestion.categoryEs added with full translations - All 17 screens/components updated with Spanish strings - BrainDumpScreen adds PLACEHOLDERS_ES array - ThoughtCard/ClusterCard receive dateLocale string (es-ES support) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- setSpanishLanguage() helper added to e2e/helpers/mocks.ts - 4 new tests in language.spec.ts: - toggle cycles all three languages FR→EN→ES→FR - ES mode shows "Escribir hoy" CTA and "ES" toggle label - bilingual ES-first order shows "Volcado mental / Brain Dump" - ES preference persists after page reload - Updated "toggle back to French from English" test: now clicks 3x since toggle cycle changed from 2-way to 3-way - Total: 29 tests (was 25) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude must always create a feature branch before making code changes and open a PR rather than committing directly to main. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Every AI call (reflection, feedback, chat) now generates a UUID requestId that flows as X-Request-Id header to edge functions, which prefix all log lines with [requestId] and include it in error response bodies. The frontend structured logger (src/lib/logger.ts) emits start/success/error events with latencyMs; error UI surfaces a short Ref: <8-char-id> for easy bug reporting. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR adds end-to-end request correlation for AI calls (client + Supabase Edge Functions) via a requestId, introduces a lightweight structured client logger, and expands the UI to support Spanish alongside new Playwright E2E coverage.
Changes:
- Add structured client logging (
src/lib/logger.ts) and attachX-Request-Idto reflection/feedback/chat requests. - Propagate
requestIdthrough Supabase Edge Function logs/error responses for reflection, french-feedback, and french-chat. - Add Spanish UI language support and introduce Playwright E2E tests + CI workflow.
Reviewed changes
Copilot reviewed 33 out of 34 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| supabase/functions/reflection/index.ts | Allow x-request-id header; include requestId in logs and many error responses |
| supabase/functions/french-feedback/index.ts | Allow x-request-id header; include requestId in logs and many error responses |
| supabase/functions/french-chat/index.ts | Allow x-request-id header; include requestId in logs and many error responses |
| src/types/journal.ts | Add Spanish fields to emotion vocabulary/categories |
| src/lib/logger.ts | New lightweight structured logger with prod suppression for debug/info |
| src/contexts/LanguageContext.tsx | Add es language, update t/bilingual, and language cycling FR→EN→ES |
| src/components/journal/WriteScreen.tsx | Update UI strings for ES; adapt to new t signature |
| src/components/journal/ThoughtGardenScreen.tsx | Add ES strings + date locale handling; update ThoughtCard props |
| src/components/journal/SignUpPrompt.tsx | Add ES strings; update t calls |
| src/components/journal/ReflectionScreen.tsx | Add requestId tracing + structured logs; show short ref in error UI; ES strings |
| src/components/journal/ProgressScreen.tsx | Add ES strings |
| src/components/journal/HomeScreen.tsx | Add ES strings; adjust date locale formatting |
| src/components/journal/GratitudeScreen.tsx | Add ES strings; adapt to new t signature |
| src/components/journal/FeedbackScreen.tsx | Add requestId tracing + structured logs; show short ref in error UI; ES strings |
| src/components/journal/EmotionsScreen.tsx | Render ES emotion/category labels |
| src/components/journal/ClustersScreen.tsx | Add ES strings + date locale; pass localized thought count label |
| src/components/journal/ClusterDetailScreen.tsx | Add ES strings + date locale formatting |
| src/components/journal/ChatScreen.tsx | Attach X-Request-Id to chat calls and add partial structured logging |
| src/components/journal/BreatheScreen.tsx | Add ES strings |
| src/components/journal/BrainDumpScreen.tsx | Add ES placeholders + strings |
| src/components/LanguageToggle.tsx | Display FR/EN/ES label |
| playwright.config.ts | Add Playwright configuration (dev server integration) |
| package.json | Add Playwright scripts and dev dependency |
| package-lock.json | Lockfile updates including Playwright |
| e2e/navigation.spec.ts | New navigation E2E coverage |
| e2e/language.spec.ts | New language toggle + persistence E2E coverage |
| e2e/journal-flow.spec.ts | New “happy path” journal flow E2E coverage + clock control test |
| e2e/home.spec.ts | New home screen E2E coverage |
| e2e/helpers/mocks.ts | Shared Supabase/AI mocks and localStorage helpers for E2E |
| e2e/braindump.spec.ts | New Brain Dump E2E coverage |
| docs/e2e-tests.md | Document how to run E2E tests and what they cover |
| docs/CHANGELOG.md | Add changelog entry for Spanish support |
| CLAUDE.md | Document that Playwright E2E suite + CI workflow exist |
| .github/workflows/e2e.yml | Add CI workflow running Playwright tests on Node 22 |
Comments suppressed due to low confidence (1)
src/components/journal/ChatScreen.tsx:57
startTimeis currently unused and there is noai.request.success(orai.request.error) emitted forstartConversation, so chat request tracing is incomplete. UsestartTimeto log a success event afterstreamResponsecompletes, and log an error event (with latency) in the catch block for symmetry with other AI calls.
const startTime = Date.now();
logger.info('ai.request.start', { component: 'ChatScreen', requestId: sessionRequestId, action: 'startConversation' });
try {
const { data: { session } } = await supabase.auth.getSession();
if (!session?.access_token) throw new Error('Not authenticated');
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const requestId = req.headers.get("x-request-id") ?? "no-id"; | ||
|
|
There was a problem hiding this comment.
Using the literal fallback "no-id" makes tracing ambiguous when the client forgets to send a header (multiple requests will share the same id). Prefer generating a UUID server-side when the header is missing, and use that value consistently in logs/responses.
| console.error(`[no-id] Error in french-feedback function:`, error); | ||
| return new Response(JSON.stringify({ | ||
| error: "Feedback service error", | ||
| code: "INTERNAL_ERROR", | ||
| details: error instanceof Error ? error.message : "Unknown error" | ||
| details: error instanceof Error ? error.message : "Unknown error", | ||
| }), { | ||
| status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" }, | ||
| }); |
There was a problem hiding this comment.
The catch block logs with a hardcoded "[no-id]" and the 500 response body omits requestId, which makes it hard to correlate function crashes with client-side failures. Keep requestId in an outer scope and include it both in the catch log prefix and in the INTERNAL_ERROR JSON response.
| const requestId = req.headers.get("x-request-id") ?? "no-id"; | ||
|
|
There was a problem hiding this comment.
Using a fixed fallback like "no-id" will collapse multiple requests into the same id when the header is missing. Prefer generating a UUID in the edge function if x-request-id is not present, so logs remain uniquely attributable.
There was a problem hiding this comment.
@copilot open a new pull request to apply changes based on this feedback
| console.error(`[no-id] Error in reflection function:`, error); | ||
| return new Response(JSON.stringify({ | ||
| error: "Reflection service error", | ||
| code: "INTERNAL_ERROR", | ||
| details: error instanceof Error ? error.message : "Unknown error" | ||
| details: error instanceof Error ? error.message : "Unknown error", | ||
| }), { |
There was a problem hiding this comment.
The catch block logs with a hardcoded "[no-id]" and the 500 response body omits requestId, which breaks end-to-end request tracing for unexpected failures. Define requestId outside the try/catch and include it in the catch log prefix and in the INTERNAL_ERROR JSON response body.
| console.error(`[no-id] Error in french-chat function:`, error); | ||
| return new Response(JSON.stringify({ | ||
| error: "Chat service error", | ||
| code: "INTERNAL_ERROR", | ||
| details: error instanceof Error ? error.message : "Unknown error" | ||
| details: error instanceof Error ? error.message : "Unknown error", | ||
| }), { | ||
| status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" }, | ||
| }); |
There was a problem hiding this comment.
The catch block hardcodes "[no-id]" and the 500 response body does not include requestId. This breaks correlating unexpected runtime errors with the client request and with other function logs. Keep requestId in an outer scope and include it in both the catch log prefix and the INTERNAL_ERROR JSON response.
| }; | ||
|
|
||
| const promptText = t(prompt.fr, prompt.en); | ||
| const promptText = t(prompt.fr, prompt.en, prompt.en); |
There was a problem hiding this comment.
promptText passes prompt.en as the Spanish argument, which causes ES mode to display the English prompt as both primary and secondary (losing the bilingual/trilingual intent). Either extend BilingualPrompt/prompt data to include an es field and pass it here, or explicitly choose a consistent fallback (e.g., show FR primary + EN secondary even in ES mode).
| const promptText = t(prompt.fr, prompt.en, prompt.en); | |
| const promptText = t(prompt.fr, prompt.en, prompt.fr); |
| }; | ||
|
|
||
| const promptText = t(prompt.fr, prompt.en); | ||
| const promptText = t(prompt.fr, prompt.en, prompt.en); |
There was a problem hiding this comment.
promptText uses prompt.en for the Spanish argument, so ES mode ends up rendering the English prompt as both primary and secondary. Consider adding Spanish prompt strings (extend BilingualPrompt with es) or choose an explicit fallback strategy for ES mode to avoid duplicate English text.
| const promptText = t(prompt.fr, prompt.en, prompt.en); | |
| const promptText = t(prompt.fr, prompt.en, prompt.fr); |
|
@TrainTravel I've opened a new pull request, #2, to work on those changes. Once the pull request is ready, I'll request review from you. |
…-feedback Co-authored-by: TrainTravel <25241752+TrainTravel@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
@TrainTravel I've opened a new pull request, #3, to work on those changes. Once the pull request is ready, I'll request review from you. |
Co-authored-by: TrainTravel <25241752+TrainTravel@users.noreply.github.com>
docs: add missing CHANGELOG entries for AI tracing and Playwright E2E suite
Fix non-unique request IDs when x-request-id header is absent in french-feedback
|
@TrainTravel I've opened a new pull request, #5, to work on those changes. Once the pull request is ready, I'll request review from you. |
Summary
src/lib/logger.ts— lightweight structured logger with[OutputFirst][level]prefix;debug/infosuppressed in productionrequestIdpassed asX-Request-Idheader; error UI showsRef: <8-char-id>for easy bug reportingreflection,french-feedback,french-chat) prefix every log line with[${requestId}]and includerequestIdin error response bodiesTest plan
npm run dev→ open DevTools Console → complete a journal → observeai.request.start/ai.request.successlogs with matchingrequestIdVITE_SUPABASE_URL→ confirmai.request.errorlog appears in console andRef: xxxxxxxxappears in error UI🤖 Generated with Claude Code