Skip to content

feat(ui): add structured JSON logging for Loki observability - #331

Merged
POWERFULMOVES merged 1 commit into
mainfrom
feat/ui-loki-structured-logging
Dec 18, 2025
Merged

POWERFULMOVES merged 1 commit into
mainfrom
feat/ui-loki-structured-logging

Conversation

@POWERFULMOVES

@POWERFULMOVES POWERFULMOVES commented Dec 18, 2025 •

Copy link
Copy Markdown
Owner

Summary

  • Wires logError() to emit structured JSON for Loki/Promtail ingestion
  • Production: JSON to stdout (Promtail picks up via docker logs)
  • Development: Colorized console output for debugging
  • No new infrastructure - uses existing Loki/Promtail stack

Log Format

{
  "timestamp": "2025-12-18T...",
  "level": "error",
  "message": "Failed to send chat message",
  "error": { "name": "Error", "message": "...", "stack": "..." },
  "component": "chat/send",
  "action": "insert"
}

Changes

  • UPDATE: pmoves/ui/lib/errorUtils.ts
    • Add StructuredLogEntry interface
    • Add emitStructuredLog() helper
    • Update logError() to use structured logging
    • Remove Sentry TODO (using self-hosted Loki instead)

Test Plan

  • Verify JSON output in production mode (NODE_ENV=production)
  • Verify logs appear in Loki via Grafana dashboard
  • Verify development mode shows readable colored output

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Chores
    • Enhanced error logging infrastructure with structured JSON formatting for production environments and improved debugging output for development.

✏️ Tip: You can customize this high-level summary in your review settings.

Wire logError() to emit structured JSON for Loki/Promtail ingestion:

- Add StructuredLogEntry interface for consistent log format
- Add emitStructuredLog() helper for env-aware output
- Production: JSON to stdout (Promtail picks up via docker logs)
- Development: Colorized console output for debugging
- Remove Sentry TODO comment (using self-hosted Loki instead)

Log format includes: timestamp, level, message, error details,
component, action, and full context for searchability in Grafana.

No new infrastructure needed - uses existing Loki/Promtail stack.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@coderabbitai

coderabbitai Bot commented Dec 18, 2025 •

Copy link
Copy Markdown
Contributor

Walkthrough

The changes enhance error logging in errorUtils.ts by introducing structured logging capabilities. A new StructuredLogEntry type and emitStructuredLog helper are added to emit JSON-formatted logs in production (for Loki/Promtail integration) and pretty-printed colored logs in development. The logError function now routes through this structured logging mechanism, while logForDebugging gains conditional behavior based on development mode or an explicit debug flag. Exported API signatures remain unchanged.

Changes

Cohort / File(s) Summary
Error Logging Enhancement
pmoves/ui/lib/errorUtils.ts
Introduces StructuredLogEntry type and emitStructuredLog helper for dual-mode logging (JSON in production, pretty-printed in development). Routes logError through structured logging; enhances logForDebugging with conditional logging based on development mode or NEXT_PUBLIC_DEBUG_LOGGING flag. Retains existing exported API signatures.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

  • Verify emitStructuredLog correctly formats JSON output with timestamp, level, message, error details, and context
  • Validate pretty-print formatting logic for development environments
  • Confirm conditional logging logic for logForDebugging (development mode vs. explicit debug flag)
  • Check that logError properly populates all fields of StructuredLogEntry

Possibly related PRs

Poem

🐰 Logs now structured, clean and bright,
JSON flows in production's light,
Pretty colors aid the dev's might,
Debugging flags keep errors tight! ✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: adding structured JSON logging for Loki observability, which aligns with the changeset's primary objective.
Description check ✅ Passed The description includes a summary of changes and a test plan, but lacks required sections like 'Required Checks' and 'Reviewer Notes' from the template, though it provides substantive content about the implementation.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/ui-loki-structured-logging

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
pmoves/ui/lib/errorUtils.ts (1)

83-95: Consider avoiding duplication of component and action in context.

Lines 92-93 extract component and action to top-level fields, while line 94 spreads the entire context object. This means if context contains component or action properties, they'll appear in both the top-level and nested context field.

If this duplication is intentional for Loki query flexibility, consider adding a comment to clarify. Otherwise, consider excluding these fields from the nested context:

🔎 Apply this diff to avoid duplication:
   const entry: StructuredLogEntry = {
     timestamp: new Date().toISOString(),
     level: severity,
     message,
     error: {
       name: errorObj.name,
       message: errorObj.message,
       stack: errorObj.stack,
     },
     component: context?.component,
     action: context?.action,
-    context: context ? { ...context } : undefined,
+    context: context ? Object.fromEntries(
+      Object.entries(context).filter(([key]) => key !== 'component' && key !== 'action')
+    ) : undefined,
   };
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 01d032a and b187b2b.

📒 Files selected for processing (1)
  • pmoves/ui/lib/errorUtils.ts (3 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
pmoves/ui/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (pmoves/AGENTS.md)

UI updates: run make -C pmoves notebook-workbench-smoke ARGS="--thread=<uuid>" to lint the Next.js bundle and validate Supabase connectivity; reference pmoves/docs/UI_NOTEBOOK_WORKBENCH.md

Files:

  • pmoves/ui/lib/errorUtils.ts
🔇 Additional comments (3)
pmoves/ui/lib/errorUtils.ts (3)

15-31: LGTM! Well-structured log entry type.

The StructuredLogEntry interface is well-designed for Loki ingestion with appropriate optional fields and clear typing.


57-69: LGTM! Good addition of debug flag.

The updated comment accurately reflects the function's behavior, and the addition of NEXT_PUBLIC_DEBUG_LOGGING flag provides a useful mechanism for enabling debug logs in production when needed.


1-138: Verify changes with notebook-workbench-smoke test.

As per coding guidelines, please run the following command to lint the Next.js bundle and validate Supabase connectivity:

#!/bin/bash
# Generate a UUID for the test thread
TEST_UUID=$(uuidgen 2>/dev/null || cat /proc/sys/kernel/random/uuid 2>/dev/null || python3 -c "import uuid; print(uuid.uuid4())")

echo "Running notebook-workbench-smoke test with thread: $TEST_UUID"
make -C pmoves notebook-workbench-smoke ARGS="--thread=$TEST_UUID"

This validates that the logging changes don't break the Next.js bundle or runtime behavior.

Comment on lines +38 to +55
function emitStructuredLog(entry: StructuredLogEntry): void {
if (process.env.NODE_ENV === 'production') {
// JSON format for Promtail to parse and send to Loki
console.log(JSON.stringify(entry));
} else {
// Pretty output for development
const levelColors: Record<ErrorSeverity, string> = {
debug: '\x1b[90m', // gray
info: '\x1b[36m', // cyan
warning: '\x1b[33m', // yellow
error: '\x1b[31m', // red
critical: '\x1b[35m' // magenta
};
const reset = '\x1b[0m';
const color = levelColors[entry.level] || '';
console.log(`${color}[${entry.level.toUpperCase()}]${reset} ${entry.message}`, entry);
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

fd errorUtils pmoves/ui -type f

Repository: POWERFULMOVES/PMOVES.AI

Length of output: 237


🏁 Script executed:

cat -n pmoves/ui/lib/errorUtils.ts | head -100

Repository: POWERFULMOVES/PMOVES.AI

Length of output: 3443


🏁 Script executed:

rg -n "StructuredLogEntry" pmoves/ui/lib/errorUtils.ts

Repository: POWERFULMOVES/PMOVES.AI

Length of output: 206


🏁 Script executed:

rg -n "emitStructuredLog" pmoves/ui/ -A 3 -B 3

Repository: POWERFULMOVES/PMOVES.AI

Length of output: 919


🏁 Script executed:

# First, let's find and read the errorUtils file
find pmoves/ui -name "errorUtils.ts" -type f 2>/dev/null | head -1 | xargs cat

Repository: POWERFULMOVES/PMOVES.AI

Length of output: 3859


Add error handling for JSON serialization failures in production logging.

Line 41 should handle serialization errors. The StructuredLogEntry interface accepts context?: Record<string, unknown>, which allows arbitrary objects to be passed. If the context contains circular references or non-serializable values (functions, symbols, DOM nodes), JSON.stringify(entry) will throw a TypeError, breaking production logging.

Implement a try-catch block to gracefully handle serialization failures:

 function emitStructuredLog(entry: StructuredLogEntry): void {
   if (process.env.NODE_ENV === 'production') {
     // JSON format for Promtail to parse and send to Loki
-    console.log(JSON.stringify(entry));
+    try {
+      console.log(JSON.stringify(entry));
+    } catch (error) {
+      // Fallback serialization on failure
+      console.log(JSON.stringify({
+        timestamp: entry.timestamp,
+        level: entry.level,
+        message: entry.message,
+        error: entry.error,
+        serializationError: 'Failed to serialize full entry context'
+      }));
+    }
   } else {
🤖 Prompt for AI Agents
In pmoves/ui/lib/errorUtils.ts around lines 38 to 55, the production path calls
JSON.stringify(entry) directly which can throw if entry.context contains
circular or non-serializable values; wrap the JSON.stringify call in a
try-catch, and on success keep console.log(JSON.stringify(entry)), but on
failure catch the error, console.error a short message including the
serialization error, and output a safe fallback log (for example a minimal JSON
with entry.level, entry.message and a note that serialization failed, plus a
string describing the context type or entry keys). Ensure the catch does not
rethrow so production logging never crashes.

@POWERFULMOVES
POWERFULMOVES merged commit 3cc610a into main Dec 18, 2025
6 checks passed
@POWERFULMOVES
POWERFULMOVES deleted the feat/ui-loki-structured-logging branch December 18, 2025 18:17
POWERFULMOVES pushed a commit that referenced this pull request Dec 18, 2025
- Add IDOR vulnerability details with affected routes (PR #329)
- Expand JWT base64url decoding with RFC 4648 details (PR #330)
- Add structured logging for Loki/Promtail integration (PR #331)
- Add post-refactoring cleanup pattern
- Add Observability checklist section
- Mark completed action items with PR/commit references

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
POWERFULMOVES added a commit that referenced this pull request Jan 2, 2026
Wire logError() to emit structured JSON for Loki/Promtail ingestion:

- Add StructuredLogEntry interface for consistent log format
- Add emitStructuredLog() helper for env-aware output
- Production: JSON to stdout (Promtail picks up via docker logs)
- Development: Colorized console output for debugging
- Remove Sentry TODO comment (using self-hosted Loki instead)

Log format includes: timestamp, level, message, error details,
component, action, and full context for searchability in Grafana.

No new infrastructure needed - uses existing Loki/Promtail stack.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Codex Agent <codex-agent@example.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
POWERFULMOVES added a commit that referenced this pull request Jan 2, 2026
* feat(ui): implement cohesive design system for console (#324)

* feat(ui): implement cohesive design system for console

- Add card-brutal and card-glass component styles
- Implement Cataclysm color palette (cyan, forest, ember, violet, gold)
- Introduce DashboardShell layout component
- Update login page with split branding layout
- Modernize dashboard pages with consistent typography
- Fix JSX conditional rendering in ingestion-queue
- Add CSS variables and utility classes in globals.css
- Extend Tailwind config with design tokens

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(ui): add missing heading-serif class and improve focus accessibility

- Add heading-serif CSS class used in login page
- Add focus:ring-2 focus:ring-cata-cyan/30 to form inputs for WCAG 2.1 AA compliance
- Add focus ring to OAuth provider buttons

Addresses critical issues from PR #324 code review.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Codex Agent <codex-agent@example.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* feat(ui): add error boundaries and error utilities (#325)

* feat(ui): add error boundaries and error utilities

- Add root error boundary (app/error.tsx) for global error handling
- Add dashboard error boundary (app/dashboard/error.tsx)
- Add errorUtils.ts with logError, logForDebugging, Result type
- Provides consistent error handling patterns for the UI

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: address PR review feedback for error boundaries

- Use logError() instead of console.error() for production visibility
- Remove html/body tags from root error boundary (not global-error.tsx)
- Add role="alert" and aria-live for accessibility
- Add aria-hidden="true" to decorative icon
- Add error context (component, digest) to log calls

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Codex Agent <codex-agent@example.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* fix(ui): resolve ESLint set-state-in-effect error in chat page

Move setStatus('connecting') inside async function to avoid
calling setState synchronously in useEffect body.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* test(ui): update tests for new design system

- Add usePathname mock to services-pages Jest test
- Use getAllByRole for multiple service links in Jest
- Use .first() locator in Playwright E2E tests
- Update ingest test to look for "ingest" instead of "ingestion dashboard"

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(ui): accessibility improvements and Tailwind JIT compliance (#327)

* fix(ui): accessibility improvements and Tailwind JIT compliance

- Add skip link for keyboard navigation (WCAG 2.1 AA)
- Add id="main-content" to DashboardShell main element
- Fix dynamic Tailwind class generation in services/page.tsx:
  - Replace `tag tag-${color}` with TAG_CLASSES lookup
  - Replace complex ternary bg class with ICON_BG_CLASSES lookup
  - Extract colorClasses to module-level BORDER_CLASSES constant
- Fix misleading comment in globals.css (heading-serif uses sans-serif font)

Fixes accessibility and design consistency issues from PR #324 review.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: remove focus:outline-none for WCAG 2.4.7 compliance

Skip link now has visible focus outline (focus:outline-2) instead of
hiding it. The cyan background provides primary feedback, but the
outline ensures compliance with WCAG 2.4.7 Focus Visible requirement.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(a11y): improve skip link accessibility compliance

- Add focus:outline-void for visible outline against cyan background
- Add tabIndex={-1} to main-content for proper skip link focus
- Correct WCAG reference: SC 2.4.1 Bypass Blocks is Level A, not AA
- Add cross-reference comment linking skip target to layout.tsx

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Codex Agent <codex-agent@example.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* fix(ui/chat): eliminate silent failures and preserve input on error (#326)

* fix(ui/chat): eliminate silent failures and preserve input on error

- API routes: Return 401 with error message instead of empty array on auth failure
- API routes: Add JWT parsing error logging for debugging
- fetchMessages: Return {data, error} pattern instead of silent empty array
- sendMessage: Check response status and return {ok, error}
- handleSend: Only clear input after successful send (preserve on failure)
- Add error banner UI to show API errors to users
- Add sending state with disabled UI during request

Fixes silent failure issues identified in PR #324 review.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: address PR review feedback for chat error handling

- Use logError() instead of logForDebugging() in JWT catch blocks
- Add explicit JSON parsing error handling with logging
- Add database error logging with sanitized client messages
- Fix field name mismatch: agent_id → agent

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(ui): add defensive error handling to client-side chat code

- Replace `.catch(() => ({}))` with explicit try-catch blocks in
  fetchMessages and sendMessage for proper error logging
- Wrap realtime subscription callbacks (onInsert/onUpdate/onDelete)
  in try-catch to prevent silent failures
- Replace console.error with logForDebugging for consistent logging
- Add component and context metadata to all error logs

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(api): use consistent 401 status for authentication failures

Changed missing owner ID response from 400 to 401 in /api/chat/send
to match /api/chat/messages behavior. Both endpoints now return
401 Unauthorized when authentication is required.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Codex Agent <codex-agent@example.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* refactor(ui): extract ownerFromJwt to shared lib/jwtUtils.ts (#330)

DRY fix - extracts duplicate JWT parsing logic to shared utility:

- Create lib/jwtUtils.ts with proper base64url decoding (RFC 4648)
- Update chat/send/route.ts to use shared ownerFromJwt
- Update chat/messages/route.ts to use shared ownerFromJwt
- Add component parameter for contextual error logging

The shared implementation fixes base64url encoding by replacing
'-' with '+' and '_' with '/' before decoding, and handles
padding correctly per RFC 4648.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Codex Agent <codex-agent@example.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* feat(ui): add structured JSON logging for Loki observability (#331)

Wire logError() to emit structured JSON for Loki/Promtail ingestion:

- Add StructuredLogEntry interface for consistent log format
- Add emitStructuredLog() helper for env-aware output
- Production: JSON to stdout (Promtail picks up via docker logs)
- Development: Colorized console output for debugging
- Remove Sentry TODO comment (using self-hosted Loki instead)

Log format includes: timestamp, level, message, error details,
component, action, and full context for searchability in Grafana.

No new infrastructure needed - uses existing Loki/Promtail stack.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Codex Agent <codex-agent@example.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* fix(security): remove client-provided ownerId bypass in API routes (#329)

* fix(security): remove client-provided ownerId bypass in API routes

CRITICAL SECURITY FIX - Removes authentication bypass vulnerabilities:

- uploads/presign: Remove body.ownerId fallback, require session auth
- uploads/persist: Remove body.ownerId fallback, require session auth
- chat/send: Remove body.ownerId fallback, use JWT only
- chat/messages: Remove query param ownerId fallback, use JWT only

User identity must now come exclusively from authenticated JWT tokens,
preventing IDOR (Insecure Direct Object Reference) attacks where
attackers could access or modify resources by supplying arbitrary
owner IDs in request bodies or query parameters.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* chore: remove unused logForDebugging import

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Codex Agent <codex-agent@example.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* docs: add UI development checklist and patterns from CodeRabbit review

- Add UI Development Checklist section to CLAUDE.md with security,
  privacy, accessibility, and code quality guidelines
- Create ui-patterns.md context doc with error handling, authentication,
  and accessibility patterns from PRs #325, #326, #327
- Document CodeRabbit review learnings for future reference

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: address CodeRabbit review nitpicks from PR #321

- deepresearch/worker.py: Use _env_bool helper for CGP_PUBLISH_ENABLED,
  move datetime import to module level
- supaserch/app.py: Replace nested ternary with status_proj_map dict
- publisher-discord/main.py: Add _safe_format_number helper for defensive
  numeric formatting in CHIT event handlers
- geometry-nats-subjects.md: Add 'text' language identifier to code block

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Codex Agent <codex-agent@example.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
POWERFULMOVES added a commit that referenced this pull request Jan 18, 2026
* feat(ui): implement cohesive design system for console (#324)

* feat(ui): implement cohesive design system for console

- Add card-brutal and card-glass component styles
- Implement Cataclysm color palette (cyan, forest, ember, violet, gold)
- Introduce DashboardShell layout component
- Update login page with split branding layout
- Modernize dashboard pages with consistent typography
- Fix JSX conditional rendering in ingestion-queue
- Add CSS variables and utility classes in globals.css
- Extend Tailwind config with design tokens

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(ui): add missing heading-serif class and improve focus accessibility

- Add heading-serif CSS class used in login page
- Add focus:ring-2 focus:ring-cata-cyan/30 to form inputs for WCAG 2.1 AA compliance
- Add focus ring to OAuth provider buttons

Addresses critical issues from PR #324 code review.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Codex Agent <codex-agent@example.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* feat(ui): add error boundaries and error utilities (#325)

* feat(ui): add error boundaries and error utilities

- Add root error boundary (app/error.tsx) for global error handling
- Add dashboard error boundary (app/dashboard/error.tsx)
- Add errorUtils.ts with logError, logForDebugging, Result type
- Provides consistent error handling patterns for the UI

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: address PR review feedback for error boundaries

- Use logError() instead of console.error() for production visibility
- Remove html/body tags from root error boundary (not global-error.tsx)
- Add role="alert" and aria-live for accessibility
- Add aria-hidden="true" to decorative icon
- Add error context (component, digest) to log calls

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Codex Agent <codex-agent@example.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* fix(ui): resolve ESLint set-state-in-effect error in chat page

Move setStatus('connecting') inside async function to avoid
calling setState synchronously in useEffect body.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* test(ui): update tests for new design system

- Add usePathname mock to services-pages Jest test
- Use getAllByRole for multiple service links in Jest
- Use .first() locator in Playwright E2E tests
- Update ingest test to look for "ingest" instead of "ingestion dashboard"

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(ui): accessibility improvements and Tailwind JIT compliance (#327)

* fix(ui): accessibility improvements and Tailwind JIT compliance

- Add skip link for keyboard navigation (WCAG 2.1 AA)
- Add id="main-content" to DashboardShell main element
- Fix dynamic Tailwind class generation in services/page.tsx:
  - Replace `tag tag-${color}` with TAG_CLASSES lookup
  - Replace complex ternary bg class with ICON_BG_CLASSES lookup
  - Extract colorClasses to module-level BORDER_CLASSES constant
- Fix misleading comment in globals.css (heading-serif uses sans-serif font)

Fixes accessibility and design consistency issues from PR #324 review.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: remove focus:outline-none for WCAG 2.4.7 compliance

Skip link now has visible focus outline (focus:outline-2) instead of
hiding it. The cyan background provides primary feedback, but the
outline ensures compliance with WCAG 2.4.7 Focus Visible requirement.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(a11y): improve skip link accessibility compliance

- Add focus:outline-void for visible outline against cyan background
- Add tabIndex={-1} to main-content for proper skip link focus
- Correct WCAG reference: SC 2.4.1 Bypass Blocks is Level A, not AA
- Add cross-reference comment linking skip target to layout.tsx

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Codex Agent <codex-agent@example.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* fix(ui/chat): eliminate silent failures and preserve input on error (#326)

* fix(ui/chat): eliminate silent failures and preserve input on error

- API routes: Return 401 with error message instead of empty array on auth failure
- API routes: Add JWT parsing error logging for debugging
- fetchMessages: Return {data, error} pattern instead of silent empty array
- sendMessage: Check response status and return {ok, error}
- handleSend: Only clear input after successful send (preserve on failure)
- Add error banner UI to show API errors to users
- Add sending state with disabled UI during request

Fixes silent failure issues identified in PR #324 review.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: address PR review feedback for chat error handling

- Use logError() instead of logForDebugging() in JWT catch blocks
- Add explicit JSON parsing error handling with logging
- Add database error logging with sanitized client messages
- Fix field name mismatch: agent_id → agent

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(ui): add defensive error handling to client-side chat code

- Replace `.catch(() => ({}))` with explicit try-catch blocks in
  fetchMessages and sendMessage for proper error logging
- Wrap realtime subscription callbacks (onInsert/onUpdate/onDelete)
  in try-catch to prevent silent failures
- Replace console.error with logForDebugging for consistent logging
- Add component and context metadata to all error logs

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(api): use consistent 401 status for authentication failures

Changed missing owner ID response from 400 to 401 in /api/chat/send
to match /api/chat/messages behavior. Both endpoints now return
401 Unauthorized when authentication is required.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Codex Agent <codex-agent@example.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* refactor(ui): extract ownerFromJwt to shared lib/jwtUtils.ts (#330)

DRY fix - extracts duplicate JWT parsing logic to shared utility:

- Create lib/jwtUtils.ts with proper base64url decoding (RFC 4648)
- Update chat/send/route.ts to use shared ownerFromJwt
- Update chat/messages/route.ts to use shared ownerFromJwt
- Add component parameter for contextual error logging

The shared implementation fixes base64url encoding by replacing
'-' with '+' and '_' with '/' before decoding, and handles
padding correctly per RFC 4648.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Codex Agent <codex-agent@example.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* feat(ui): add structured JSON logging for Loki observability (#331)

Wire logError() to emit structured JSON for Loki/Promtail ingestion:

- Add StructuredLogEntry interface for consistent log format
- Add emitStructuredLog() helper for env-aware output
- Production: JSON to stdout (Promtail picks up via docker logs)
- Development: Colorized console output for debugging
- Remove Sentry TODO comment (using self-hosted Loki instead)

Log format includes: timestamp, level, message, error details,
component, action, and full context for searchability in Grafana.

No new infrastructure needed - uses existing Loki/Promtail stack.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Codex Agent <codex-agent@example.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* fix(security): remove client-provided ownerId bypass in API routes (#329)

* fix(security): remove client-provided ownerId bypass in API routes

CRITICAL SECURITY FIX - Removes authentication bypass vulnerabilities:

- uploads/presign: Remove body.ownerId fallback, require session auth
- uploads/persist: Remove body.ownerId fallback, require session auth
- chat/send: Remove body.ownerId fallback, use JWT only
- chat/messages: Remove query param ownerId fallback, use JWT only

User identity must now come exclusively from authenticated JWT tokens,
preventing IDOR (Insecure Direct Object Reference) attacks where
attackers could access or modify resources by supplying arbitrary
owner IDs in request bodies or query parameters.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* chore: remove unused logForDebugging import

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Codex Agent <codex-agent@example.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* docs: add UI development checklist and patterns from CodeRabbit review

- Add UI Development Checklist section to CLAUDE.md with security,
  privacy, accessibility, and code quality guidelines
- Create ui-patterns.md context doc with error handling, authentication,
  and accessibility patterns from PRs #325, #326, #327
- Document CodeRabbit review learnings for future reference

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: address CodeRabbit review nitpicks from PR #321

- deepresearch/worker.py: Use _env_bool helper for CGP_PUBLISH_ENABLED,
  move datetime import to module level
- supaserch/app.py: Replace nested ternary with status_proj_map dict
- publisher-discord/main.py: Add _safe_format_number helper for defensive
  numeric formatting in CHIT event handlers
- geometry-nats-subjects.md: Add 'text' language identifier to code block

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Codex Agent <codex-agent@example.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
POWERFULMOVES added a commit that referenced this pull request Jan 18, 2026
Wire logError() to emit structured JSON for Loki/Promtail ingestion:

- Add StructuredLogEntry interface for consistent log format
- Add emitStructuredLog() helper for env-aware output
- Production: JSON to stdout (Promtail picks up via docker logs)
- Development: Colorized console output for debugging
- Remove Sentry TODO comment (using self-hosted Loki instead)

Log format includes: timestamp, level, message, error details,
component, action, and full context for searchability in Grafana.

No new infrastructure needed - uses existing Loki/Promtail stack.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Codex Agent <codex-agent@example.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
POWERFULMOVES pushed a commit that referenced this pull request Jan 18, 2026
- Add IDOR vulnerability details with affected routes (PR #329)
- Expand JWT base64url decoding with RFC 4648 details (PR #330)
- Add structured logging for Loki/Promtail integration (PR #331)
- Add post-refactoring cleanup pattern
- Add Observability checklist section
- Mark completed action items with PR/commit references

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant