Skip to content

Conversation

@HardMax71
Copy link
Owner

@HardMax71 HardMax71 commented Dec 25, 2025

Summary by CodeRabbit

  • New Features

    • Modern CodeMirror editor (language/version, resource limits, saved scripts), unified user settings/update flow, execution UI, many admin dashboards/components, desktop notification permission prompt, and richer language/runtime info.
  • Bug Fixes

    • Editor theme default changed to "auto"; auth persistence moved to session scope.
  • Refactor

    • Centralized settings into stores, consolidated APIs, introduced path aliases, and moved SSE/events to typed message models.
  • Documentation

    • Expanded public API schemas (health, rate limits, executions/events, language info).
  • Tests

    • Updated and added tests to reflect new flows and typed SSE/message models.

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

@coderabbitai
Copy link

coderabbitai bot commented Dec 25, 2025

📝 Walkthrough

Walkthrough

Editor theme default changed to "auto". Event and execution types moved from plain strings to enum types. Language/runtime metadata introduced as typed LanguageInfo. SSE/Redis messages and mappers converted to typed Pydantic schemas. Frontend removed the localStorage settings-cache in favor of a stores-based userSettings flow and unified saveUserSettings; many new UI components, admin utilities, and path-alias migrations were added.

Changes

Cohort / File(s) Summary
User settings (domain + frontend)
backend/app/domain/user/settings_models.py, backend/app/schemas_pydantic/user_settings.py, frontend/src/lib/user-settings.ts, frontend/src/stores/userSettings.ts, frontend/src/lib/settings-cache.ts (deleted), frontend/src/lib/__tests__/user-settings.test.ts, frontend/src/lib/__tests__/settings-cache.test.ts (deleted)
Editor theme default "one-dark""auto"; DomainUserSettingsUpdate.to_update_dict removed; Settings history event_type → EventType; new history fields (reason, correlation_id); frontend removed localStorage cache, added userSettings store, setUserSettings/updateSettings, and unified saveUserSettings; tests updated.
Runtime / LanguageInfo typing
backend/app/domain/execution/models.py, backend/app/schemas_pydantic/execution.py, backend/app/runtime_registry.py, backend/app/settings.py, backend/app/domain/execution/__init__.py
Added LanguageInfoDomain/LanguageInfo (versions, file_ext); supported_runtimes type changed from dict[str, list[str]]dict[str, LanguageInfo...]; runtime registry and settings adapted.
SSE / Redis typed messages & mappers
backend/app/schemas_pydantic/sse.py, backend/app/services/sse/redis_bus.py, backend/app/services/sse/sse_service.py, backend/app/infrastructure/mappers/sse_mapper.py, backend/app/db/repositories/sse_repository.py, backend tests
Replaced ad-hoc dict messages with typed Pydantic models (RedisSSEMessage, RedisNotificationMessage, SSEExecutionEventData, SSENotificationEventData); subscriptions now validate models; SSEService emits typed payloads and terminal-event set updated; mappers and tests adjusted.
Event typing & DLQ
backend/app/domain/events/*, backend/app/schemas_pydantic/dlq.py, backend/app/dlq/models.py, backend/app/db/repositories/event_repository.py, backend/app/services/kafka_event_service.py, backend/app/services/event_service.py, backend/app/services/user_settings_service.py
event_type and related parameters shifted from strEventType across domain, schemas, repos and services; repo queries use .value; Kafka publish/signatures updated.
Execution error & resource typing
backend/app/infrastructure/mappers/execution_api_mapper.py, backend/app/schemas_pydantic/execution.py
Execution error type changed to ExecutionErrorType; Pydantic models gained from_attributes/model_validate usage; ResourceLimits reference LanguageInfo.
Frontend editor refactor & CodeMirror
frontend/src/routes/Editor.svelte, frontend/src/components/editor/*, frontend/src/lib/editor/*, frontend/src/lib/editor/index.ts, frontend/src/lib/editor/languages.ts, frontend/src/components/editor/index.ts
Editor split into modular components (CodeMirrorEditor, OutputPanel, LanguageSelect, ResourceLimits, EditorToolbar, ScriptActions, SavedScripts), CodeMirror 6 integration, language extension resolver and createExecutionState execution factory added.
Admin UI & utilities (frontend)
frontend/src/components/admin/**, frontend/src/lib/admin/**, frontend/src/routes/admin/*
Many new admin components (filters, tables, stats cards, modals), utilities (pagination, autoRefresh, constants, sagaStates, rateLimits), barrels and route integrations; routes refactored to use these components.
Notifications, icons & UI migrations
frontend/src/components/NotificationCenter.svelte, frontend/src/routes/Notifications.svelte, frontend/src/stores/notificationStore.ts, many frontend/src/components/*.svelte
Replaced inline SVGs with @lucide/svelte components; icon helpers now return Component types; notification permission flow added; loading derived store exposed; tests adapted.
Auth, interceptors & storage migration
frontend/src/lib/api-interceptors.ts, frontend/src/lib/auth-init.ts, frontend/src/stores/auth.ts, related tests
Centralized error handling and 401 flow; auth persistence moved from localStoragesessionStorage; clearing auth now calls clearUserSettings; test storage and mocks updated.
OpenAPI / frontend API types & SDK
docs/reference/openapi.json, frontend/src/lib/api/types.gen.ts, frontend/src/lib/api/index.ts, frontend/src/lib/api/sdk.gen.ts
Added concrete public schemas (liveness/readiness/shutdown/rate-limit/language info/execution error) and updated TS types/exports; some SDK endpoints reorganized.
Build config, aliases & deps
frontend/package.json, frontend/rollup.config.js, frontend/tsconfig.json, frontend/vitest.config.ts
Added CodeMirror language deps and @rollup/plugin-alias; rollup configured with path aliases and .ts resolution; tsconfig/vitest updated with $lib/$components/$stores/$routes/$utils/$styles aliases.
Tests, mocks & path aliasing
many under frontend/src/lib/__tests__, frontend/src/stores/__tests__, frontend/src/routes/admin/__tests__, frontend/src/routes/admin/__tests__/test-utils.ts
Tests updated to reflect stores/API changes and sessionStorage usage; new test-utils (mockElementAnimate, mockWindowGlobals) added; many imports switched to path aliases.
Misc & import migrations
numerous frontend/src/*, frontend/src/main.ts, backend small changes
Many relative imports replaced with path aliases; backend: replayed events no longer prefixed with "replay." for Kafka; assorted UI refinements (toasts, error normalization, timers).

Sequence Diagram(s)

sequenceDiagram
  participant Browser as Browser UI
  participant Auth as Auth Store
  participant API as Backend API
  participant Store as userSettings Store
  participant Theme as Theme Handler

  Note over Browser,API: User saves settings via unified flow
  Browser->>Auth: subscribe isAuthenticated?
  alt authenticated
    Browser->>API: PUT /api/v1/user/settings (partial UserSettingsUpdate)
    API-->>Browser: 200 OK (updated settings)
    Browser->>Store: setUserSettings(updated)
    Store-->>Theme: if theme present -> setTheme(theme)
  else unauthenticated
    Browser->>Store: setUserSettings(local or null)
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

enhancement

Poem

🐇 I nudged defaults to "auto" and learned each file_ext,
Runtimes list their versions, neatly folded in a text.
The cache hopped off the shelf — a store hopped in its place,
One PUT to save my settings, theme applied with gentle grace.
I munched a carrot, then committed — tiny hops across the base.

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title check ⚠️ Warning The title 'chore: frontend updates' is vague and overly broad, failing to convey the specific scope or primary change of this substantial pull request. Use a more specific, descriptive title that summarizes the main architectural changes, such as: 'refactor: consolidate frontend editor and admin components with modular architecture' or 'feat: modernize frontend with path aliases and component-based UI patterns'.
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ 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 frontend-extra-tests

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 50f79b8 and 04fccc8.

📒 Files selected for processing (1)
  • backend/app/services/event_replay/replay_service.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: E2E Tests
  • GitHub Check: Integration Tests
🔇 Additional comments (1)
backend/app/services/event_replay/replay_service.py (1)

189-191: Parameter rename is correct and all callers are consistent.

The repository interface has been correctly updated: fetch_events now uses replay_filter: ReplayFilter as its parameter name. The change in replay_service.py properly uses the updated keyword argument. All callers are consistent—the test uses a positional argument, while the service uses the named parameter.


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.

@codecov-commenter
Copy link

codecov-commenter commented Dec 25, 2025

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 62.55038% with 1022 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
frontend/src/routes/Editor.svelte 0.00% 179 Missing ⚠️
...ontend/src/components/editor/LanguageSelect.svelte 0.00% 110 Missing ⚠️
frontend/src/components/editor/OutputPanel.svelte 0.00% 91 Missing ⚠️
frontend/src/lib/editor/execution.svelte.ts 0.00% 86 Missing ⚠️
...tend/src/components/editor/CodeMirrorEditor.svelte 0.00% 85 Missing ⚠️
frontend/src/lib/api-interceptors.ts 9.67% 56 Missing ⚠️
frontend/src/lib/admin/autoRefresh.svelte.ts 0.00% 46 Missing ⚠️
frontend/src/lib/admin/pagination.svelte.ts 0.00% 37 Missing ⚠️
frontend/src/components/editor/SavedScripts.svelte 0.00% 34 Missing ⚠️
...ontend/src/components/editor/ResourceLimits.svelte 0.00% 24 Missing ⚠️
... and 47 more
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.
Flag Coverage Δ
backend 67.43% <79.53%> (+0.33%) ⬆️
frontend 56.84% <61.09%> (+10.16%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
backend/app/api/routes/admin/events.py 46.22% <100.00%> (ø)
backend/app/api/routes/dlq.py 80.00% <100.00%> (+0.40%) ⬆️
backend/app/api/routes/events.py 67.52% <ø> (ø)
...p/db/repositories/admin/admin_events_repository.py 39.01% <100.00%> (ø)
backend/app/db/repositories/dlq_repository.py 47.77% <100.00%> (+0.58%) ⬆️
backend/app/dlq/models.py 96.75% <100.00%> (+0.02%) ⬆️
backend/app/domain/events/event_models.py 96.19% <100.00%> (+0.02%) ⬆️
backend/app/domain/events/query_builders.py 78.75% <100.00%> (ø)
backend/app/domain/execution/__init__.py 100.00% <ø> (ø)
backend/app/domain/execution/models.py 98.21% <100.00%> (+0.13%) ⬆️
... and 99 more

... and 4 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/src/routes/Editor.svelte (1)

54-61: Remove redundant local editor settings definition.

This local editorSettings object duplicates DEFAULT_EDITOR_SETTINGS from the store and is immediately overwritten by the store subscription on line 219. This creates maintenance overhead if defaults need to change.

🔎 Recommended refactor

Remove this local definition entirely and rely on the store subscription:

-    // Default editor settings
-    let editorSettings = {
-        theme: 'auto',  // Default to following app theme
-        font_size: 14,
-        tab_size: 4,
-        use_tabs: false,
-        word_wrap: true,
-        show_line_numbers: true,
-    };
+    // Editor settings loaded from store
+    let editorSettings = $state({
+        theme: 'auto',
+        font_size: 14,
+        tab_size: 4,
+        use_tabs: false,
+        word_wrap: true,
+        show_line_numbers: true,
+    });

Or better yet, import and use the DEFAULT_EDITOR_SETTINGS directly from the store if you need an initial value:

+    import { editorSettings as editorSettingsStore, DEFAULT_EDITOR_SETTINGS } from '../stores/userSettings';
     
-    // Default editor settings
-    let editorSettings = {
-        theme: 'auto',  // Default to following app theme
-        font_size: 14,
-        tab_size: 4,
-        use_tabs: false,
-        word_wrap: true,
-        show_line_numbers: true,
-    };
+    let editorSettings = $state(DEFAULT_EDITOR_SETTINGS);

Note: You'd need to export DEFAULT_EDITOR_SETTINGS from the store for the second approach.

🧹 Nitpick comments (5)
backend/app/domain/user/settings_models.py (1)

21-29: Editor theme default aligned with “auto” model-wide

Setting DomainEditorSettings.theme default to "auto" matches the rest of the settings model and the frontend behavior; change looks good. If you ever introduce an EditorTheme enum, this field would be a good candidate to migrate for stronger typing, but that’s not urgent.

frontend/src/stores/__tests__/theme.test.ts (1)

5-7: Mock update matches new user-settings API

Renaming the mock export to saveUserSettings keeps the test aligned with the updated ../lib/user-settings surface. You might later add expectations around when saveUserSettings is (not) called under different auth states, but the current tests remain valid.

frontend/src/stores/theme.ts (1)

26-37: Theme store wiring to unified user settings looks solid

Using a saveUserSettings function slot and calling saveUserSettings({ theme: value }) only when the auth store says the user is logged in keeps theme persistence consistent with the new unified settings API, while still supporting local-only behavior when unauthenticated.

Note that because saveUserSettings itself calls setThemeLocal when a theme is present, authenticated changes will now trigger a second internal set/localStorage write + DOM update for the same value. It’s harmless, but if you ever care about reducing duplicate work, you could special-case the call path from the theme store.

Also applies to: 39-51, 81-87

frontend/src/routes/Settings.svelte (1)

14-15: Settings page correctly integrated with central userSettings store

Updating both loadSettings and saveSettings to call setUserSettings(settings) keeps the new store in sync with the latest backend state, which is exactly what you want for other consumers (editor, theme, etc.). The history mapping change to displayField: item.field plus item.reason?.includes('restore') also hardens the UI against missing reasons without changing semantics.

If you ever revisit error handling, consider whether you want to also clear or flag the global userSettings store when loadSettings fails, so other views don’t keep showing stale data, but that can be deferred.

Also applies to: 95-103, 135-159, 196-201

backend/app/schemas_pydantic/user_settings.py (1)

21-30: Pydantic EditorSettings default matches the “auto” theme model

Changing EditorSettings.theme default to "auto" is consistent with the domain model and OpenAPI schema, so new users get the system-following behavior end-to-end. If you later formalize editor themes as an enum, this field would be a natural place to tighten the type, but it’s fine as-is.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2dd9e1e and b21c402.

📒 Files selected for processing (14)
  • backend/app/domain/user/settings_models.py
  • backend/app/schemas_pydantic/user_settings.py
  • docs/reference/openapi.json
  • frontend/src/lib/__tests__/settings-cache.test.ts
  • frontend/src/lib/__tests__/user-settings.test.ts
  • frontend/src/lib/api/types.gen.ts
  • frontend/src/lib/auth-init.ts
  • frontend/src/lib/settings-cache.ts
  • frontend/src/lib/user-settings.ts
  • frontend/src/routes/Editor.svelte
  • frontend/src/routes/Settings.svelte
  • frontend/src/stores/__tests__/theme.test.ts
  • frontend/src/stores/theme.ts
  • frontend/src/stores/userSettings.ts
💤 Files with no reviewable changes (3)
  • frontend/src/lib/api/types.gen.ts
  • frontend/src/lib/settings-cache.ts
  • frontend/src/lib/tests/settings-cache.test.ts
🧰 Additional context used
🧬 Code graph analysis (7)
frontend/src/stores/userSettings.ts (2)
backend/app/schemas_pydantic/user_settings.py (2)
  • EditorSettings (21-43)
  • UserSettings (46-59)
frontend/src/lib/api/types.gen.ts (2)
  • EditorSettings (463-488)
  • UserSettings (2710-2748)
backend/app/domain/user/settings_models.py (1)
frontend/src/stores/theme.ts (1)
  • theme (39-51)
frontend/src/stores/theme.ts (2)
frontend/src/lib/user-settings.ts (1)
  • saveUserSettings (33-55)
frontend/src/stores/userSettings.ts (1)
  • userSettings (13-13)
frontend/src/lib/auth-init.ts (1)
frontend/src/stores/userSettings.ts (1)
  • clearUserSettings (28-30)
frontend/src/lib/user-settings.ts (6)
frontend/src/stores/userSettings.ts (2)
  • setUserSettings (20-22)
  • updateSettings (24-26)
frontend/src/lib/api/types.gen.ts (2)
  • UserSettingsUpdate (2755-2777)
  • UserSettings (2710-2748)
frontend/src/lib/api/index.ts (3)
  • UserSettingsUpdate (4-4)
  • updateUserSettingsApiV1UserSettingsPut (3-3)
  • UserSettings (4-4)
frontend/src/stores/auth.ts (1)
  • isAuthenticated (43-43)
frontend/src/lib/api/sdk.gen.ts (1)
  • updateUserSettingsApiV1UserSettingsPut (556-563)
frontend/src/stores/theme.ts (1)
  • setThemeLocal (81-87)
frontend/src/lib/__tests__/user-settings.test.ts (2)
frontend/src/lib/user-settings.ts (2)
  • loadUserSettings (12-31)
  • saveUserSettings (33-55)
frontend/src/stores/userSettings.ts (1)
  • editorSettings (15-18)
backend/app/schemas_pydantic/user_settings.py (1)
frontend/src/stores/theme.ts (1)
  • theme (39-51)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Scan Frontend
  • GitHub Check: E2E Tests
  • GitHub Check: Integration Tests
🔇 Additional comments (10)
frontend/src/lib/auth-init.ts (1)

3-3: Clearing user settings on auth reset looks correct

Hooking _clearAuth into clearUserSettings() ensures stale settings aren’t left around after auth is invalidated, which aligns with the new centralized settings store. 👍

Also applies to: 151-160

docs/reference/openapi.json (1)

4989-4995: OpenAPI editor theme default now consistent with implementation

Setting EditorSettings.theme.default to "auto" brings the generated API docs in line with the backend models and frontend behavior. No further changes needed here.

frontend/src/stores/userSettings.ts (2)

1-18: LGTM! Well-structured store setup.

The store architecture is clean:

  • DEFAULT_EDITOR_SETTINGS aligns with backend defaults (theme: 'auto', etc.)
  • The derived store correctly merges defaults with user settings, preserving the right precedence
  • Type safety is maintained with UserSettings and EditorSettings types

20-22: LGTM! Simple and correct helper functions.

Both functions provide a clean API for external modules to interact with the store.

Also applies to: 28-30

frontend/src/routes/Editor.svelte (2)

44-45: LGTM! Correct imports for the new store pattern.

The imports align with the refactored user settings architecture.


217-223: LGTM! Proper store subscription and cleanup.

The subscription correctly:

  • Updates local state from the store
  • Applies settings to the editor when available
  • Unsubscribes on component destroy (line 284)

Also applies to: 284-284

frontend/src/lib/user-settings.ts (2)

1-31: LGTM! Clean integration with the new store.

The function correctly:

  • Fetches settings from the API
  • Updates the centralized store via setUserSettings
  • Applies theme locally when available
  • Handles errors gracefully

33-34: LGTM! Appropriate function signature and auth guard.

The refactored function:

  • Accepts UserSettingsUpdate for partial updates
  • Returns a boolean to indicate success/failure
  • Properly guards against unauthenticated access
frontend/src/lib/__tests__/user-settings.test.ts (2)

1-55: LGTM! Comprehensive mock setup.

The test mocks correctly cover:

  • API calls (getUserSettings, updateUserSettings)
  • Store mutations (setUserSettings, updateSettings)
  • Theme management (setThemeLocal)
  • Authentication state

Reset logic in beforeEach ensures test isolation.


57-228: LGTM! Excellent test coverage.

The test suite thoroughly covers:

  • loadUserSettings: API fetching, store updates, theme application, error handling
  • saveUserSettings: Authentication guards, various setting combinations, store updates, theme application logic, error scenarios

The tests properly verify both success and failure paths, ensuring robust behavior.

Copy link

@coderabbitai coderabbitai bot left a comment

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 (6)
frontend/src/routes/Editor.svelte (1)

58-71: Consider logging fallback to default language.

The function correctly maps languages to CodeMirror extensions. However, when falling back to Python for unrecognized languages, consider logging this to help with debugging.

🔎 Optional: Add logging for default case
 function getLanguageExtension(lang: string) {
     switch (lang) {
         case 'python': return python();
         case 'node': return javascript();
         case 'go': return go();
         case 'ruby': return StreamLanguage.define(ruby);
         case 'bash': return StreamLanguage.define(shell);
-        default: return python();
+        default:
+            console.warn(`Unknown language "${lang}", defaulting to Python`);
+            return python();
     }
 }
docs/reference/openapi.json (5)

1149-1192: Health/readiness/shutdown response schemas look coherent; consider minor timestamp consistency

The new LivenessResponse, ReadinessResponse, and ShutdownStatusResponse types are wired correctly into /api/v1/health/live, /api/v1/health/ready, and SSEHealthResponse.shutdown and the required lists match the defined properties. For consistency with the rest of the spec (where timestamps generally use format: "date-time"), you may want to add that format to LivenessResponse.timestamp (and optionally add a timestamp to ReadinessResponse) so clients can uniformly parse health timestamps.

Also applies to: 6405-6429, 7104-7124, 7891-7947, 8415-8464


3107-3159: DeleteUserResponse wiring is correct; naming is slightly overloaded across delete responses

The DELETE /api/v1/admin/users/{user_id} endpoint now correctly returns DeleteUserResponse, and the schema itself (message + deleted_counts map) is well-formed and marked as required. Note there are several similarly named delete schemas (DeleteResponse, DeleteEventResponse, DeleteNotificationResponse, EventDeleteResponse, DeleteUserResponse), which may be mildly confusing for client authors; not a blocker, but consider converging on a single naming convention the next time you touch these models.

Also applies to: 4953-4974


3257-3348: Rate limit response models are consistent; could reuse existing enums for stronger typing

The new rate-limit-related schemas (RateLimitRuleResponse, UserRateLimitConfigResponse, UserRateLimitsResponse, RateLimitUpdateResponse) are internally consistent and correctly referenced by the admin rate-limit endpoints. One improvement you might consider is having RateLimitRuleResponse.group and RateLimitRuleResponse.algorithm reuse the existing EndpointGroup and RateLimitAlgorithm enums instead of plain string, to keep the response surface as strongly typed as the request/config side.

Also applies to: 6993-7041, 7081-7103, 8785-8883


2618-2632: Admin events export/replay/admin detail endpoints look structurally sound

The export/json, admin event detail, replay, and replay-status endpoints are all using valid, connected schemas (EventDetailResponse, EventReplayRequest/Response/StatusResponse, etc.) and the required fields align with their properties. The schema: {} on the export endpoints is acceptable as “any JSON”, but be aware that generated clients won’t get strong typing for those responses; if you stabilize a concrete shape later, tightening this will help downstream tooling.

Also applies to: 2635-2799


4034-4215: Saga endpoint descriptions expose internal implementation details

The enriched descriptions for the saga endpoints now include Python-style docstrings with Args: and references to “FastAPI request object” and DI services. These are accurate but a bit implementation-centric for public API docs; consider trimming them down to user-facing behavior and moving framework/DI notes back into code-only docstrings.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b21c402 and 082edb6.

⛔ Files ignored due to path filters (1)
  • frontend/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (12)
  • backend/app/domain/execution/__init__.py
  • backend/app/domain/execution/models.py
  • backend/app/runtime_registry.py
  • backend/app/schemas_pydantic/execution.py
  • backend/app/services/execution_service.py
  • backend/app/settings.py
  • docs/reference/openapi.json
  • frontend/package.json
  • frontend/src/lib/api/index.ts
  • frontend/src/lib/api/sdk.gen.ts
  • frontend/src/lib/api/types.gen.ts
  • frontend/src/routes/Editor.svelte
✅ Files skipped from review due to trivial changes (1)
  • backend/app/services/execution_service.py
🧰 Additional context used
🧬 Code graph analysis (4)
backend/app/schemas_pydantic/execution.py (2)
frontend/src/lib/api/index.ts (1)
  • LanguageInfo (4-4)
frontend/src/lib/api/types.gen.ts (1)
  • LanguageInfo (1301-1310)
backend/app/domain/execution/__init__.py (1)
backend/app/domain/execution/models.py (3)
  • LanguageInfoDomain (68-72)
  • ResourceLimitsDomain (76-84)
  • ResourceUsageDomain (43-64)
backend/app/settings.py (1)
backend/app/domain/execution/models.py (1)
  • LanguageInfoDomain (68-72)
backend/app/runtime_registry.py (2)
backend/tests/conftest.py (1)
  • app (130-138)
backend/app/domain/execution/models.py (1)
  • LanguageInfoDomain (68-72)
🪛 GitHub Actions: Ruff Linting
backend/app/schemas_pydantic/execution.py

[error] 82-82: E501 Line too long (125 > 120) detected by ruff check . --config pyproject.toml. Line 82 exceeds the maximum line length.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: E2E Tests
  • GitHub Check: Build Frontend
  • GitHub Check: Integration Tests
🔇 Additional comments (27)
backend/app/settings.py (1)

6-6: LGTM!

The import and type annotation update for SUPPORTED_RUNTIMES correctly aligns with the LanguageInfoDomain type now used in runtime_registry.py. The change maintains type consistency across the codebase.

Also applies to: 41-41

backend/app/domain/execution/__init__.py (1)

10-10: LGTM!

The LanguageInfoDomain is correctly exported from the package, following the established pattern for other domain models. The __all__ list is properly updated.

Also applies to: 18-18

backend/app/schemas_pydantic/execution.py (1)

111-126: LGTM!

The LanguageInfo model and updated ResourceLimits.supported_runtimes type correctly mirror the domain layer changes. The schema cleanly separates API concerns from domain models while maintaining field consistency.

backend/app/domain/execution/models.py (2)

67-73: LGTM!

The LanguageInfoDomain dataclass is well-structured, follows existing patterns in the file, and is correctly positioned before ResourceLimitsDomain which references it.


84-84: LGTM!

The supported_runtimes type update to dict[str, LanguageInfoDomain] is consistent with the new model and aligns with changes across the codebase.

backend/app/runtime_registry.py (2)

3-4: LGTM!

The import of LanguageInfoDomain is correctly placed and follows the existing import structure.


183-186: LGTM!

The SUPPORTED_RUNTIMES construction cleanly maps LANGUAGE_SPECS to LanguageInfoDomain objects using a dict comprehension. This serves as the single source of truth for language runtime information consumed by settings.py.

frontend/package.json (1)

21-25: LGTM!

The CodeMirror language dependencies are correctly added to support Go, JavaScript, and legacy modes (Ruby, Shell) in the editor. Version ranges are appropriate.

frontend/src/routes/Editor.svelte (11)

17-17: LGTM!

The new language imports correctly support multi-language editing with CodeMirror, including Go, JavaScript, and legacy modes for Ruby and Shell.

Also applies to: 37-41


50-51: Settings store integration looks good.

The shift from localStorage-based settings to a reactive editorSettingsStore improves consistency and reactivity across the app.


240-248: LGTM!

The settings store subscription correctly updates editor settings reactively when the store changes.


264-280: LGTM!

The runtime loading logic properly handles errors, validates the current selection against available runtimes, and falls back gracefully to the first available option.


298-306: LGTM!

The language subscription correctly updates CodeMirror's syntax highlighting when the selected language changes.


673-687: LGTM!

The export function correctly determines file extension from runtime info with a sensible fallback, and ensures the exported filename has the proper extension.


689-737: Excellent UX improvement!

The file upload now intelligently detects the language from the file extension and auto-selects the appropriate runtime. Error handling is thorough with informative messages about supported file types.


820-820: LGTM!

The dynamic accept attribute correctly restricts file uploads to supported language extensions.


1046-1095: LGTM!

The runtime selector properly disables when runtimes are unavailable, provides clear visual feedback, and prevents showing empty version menus.


1095-1095: LGTM!

The Run Script button is correctly disabled both during execution and when runtimes are unavailable, preventing invalid execution attempts.


158-160: The concern is not valid. file_ext is a required property in the LanguageInfo type definition (defined as file_ext: string in types.gen.ts and marked as required in the OpenAPI schema), not optional. The type system guarantees that file_ext will always be a string, so the code at lines 158-160 cannot produce .undefined. The derived value safely accesses i.file_ext without risk of undefined values.

Likely an incorrect or invalid review comment.

docs/reference/openapi.json (1)

5007-5042: EditorSettings default and LanguageInfo-based runtimes are aligned with the rest of the spec

Changing EditorSettings.theme’s default to "auto" matches the Theme enum and the default under UserSettings.theme, and ResourceLimits.supported_runtimes now mapping to LanguageInfo (versions + file_ext) gives the frontend a much cleaner contract for runtime metadata. No correctness issues here; this is a good tightening of the model.

Also applies to: 6383-6404, 7715-7733

frontend/src/lib/api/types.gen.ts (7)

432-448: New response types are well-structured.

The addition of DeleteUserResponse, LanguageInfo, LivenessResponse, and ReadinessResponse provides stronger typing for API responses. These types follow consistent patterns with clear field documentation.

Also applies to: 1296-1336, 1787-1805


1712-1750: Minor type asymmetry between request and response models.

RateLimitRuleResponse.group is typed as string (line 1725), while RateLimitRule.group uses the EndpointGroup enum. This is likely intentional for backend flexibility, but consumers should be aware that the response type is more permissive than the input type.

Since this is an auto-generated file, any schema alignment would need to happen in the backend OpenAPI spec.


2261-2261: Improved type safety for shutdown status.

The SseHealthResponse.shutdown field now uses the concrete ShutdownStatusResponse type instead of a generic object, providing better type safety and documentation for shutdown state properties like phase, initiated, complete, and connection counts.

Also applies to: 2565-2607


2826-2881: User rate limit response types are comprehensive.

UserRateLimitConfigResponse and UserRateLimitsResponse provide well-structured types for rate limit configuration and current usage statistics. The nested current_usage map allows for flexible usage data representation.


4661-4774: Admin event endpoint types are well-defined.

The new admin event endpoint types (DeleteEventApiV1AdminEventsEventIdDelete*, GetEventDetailApiV1AdminEventsEventIdGet*, ReplayEventsApiV1AdminEventsReplayPost*, GetReplayStatusApiV1AdminEventsReplaySessionIdStatusGet*) properly use concrete response types and follow the established patterns in the file.


3815-3815: Endpoint response types consistently use new concrete types.

Health endpoints now use LivenessResponse/ReadinessResponse, user deletion uses DeleteUserResponse, and rate limit endpoints use UserRateLimitsResponse/RateLimitUpdateResponse. This improves type safety across the API client.

Also applies to: 3831-3831, 4932-4932, 5082-5082, 5112-5112


2107-2109: Breaking type change in supported_runtimes - verify all consumers are updated.

The type changed from { [key: string]: Array<string> } to { [key: string]: LanguageInfo }. This is a significant structural change where each runtime key now maps to an object containing versions and file_ext instead of just an array of strings.

Ensure all frontend code consuming ResourceLimits.supported_runtimes has been updated to handle the new LanguageInfo structure:

#!/bin/bash
# Search for usages of supported_runtimes in the frontend
rg -n -C5 'supported_runtimes' --type=ts --type=svelte -g '!types.gen.ts'

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
backend/tests/integration/test_user_settings_routes.py (1)

132-132: LGTM! Test correctly updated for new default theme value.

The addition of "auto" to the valid editor theme values correctly aligns with the backend changes that switched the default from "one-dark" to "auto". This prevents test failures and properly validates the expanded set of allowed values.

Note: Line 373 already uses "auto" in test scenarios, providing additional coverage.

Optional: Consider adding explicit default value verification

While the current test validates that "auto" is an acceptable value, you could optionally add an explicit assertion to verify that new users receive "auto" as their default editor theme:

# After line 132, optionally add:
# Verify new default value for editor theme
# (Note: This assumes the backend has been updated to default to "auto")

This would provide more explicit coverage of the default value behavior, though it's not essential since the test already validates the value is within the acceptable range.

backend/app/api/routes/execution.py (1)

61-61: Consider using asdict() for consistency with line 340.

Line 61 uses vars() to convert domain_exec.resource_usage to the API model, while line 340 uses asdict() for the same pattern. Although vars() works here since ResourceUsageDomain contains only primitive fields, using asdict() throughout would be more consistent. Additionally, ResourceUsageDomain defines its own to_dict() method which could be leveraged instead.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 171e161 and d80be86.

📒 Files selected for processing (2)
  • backend/app/api/routes/execution.py
  • backend/tests/integration/test_user_settings_routes.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: E2E Tests
  • GitHub Check: Integration Tests
🔇 Additional comments (1)
backend/app/api/routes/execution.py (1)

1-1: Correct use of asdict() for handling nested dataclass structures.

The change from vars() to asdict() at line 340 is appropriate and necessary. ResourceLimitsDomain contains a nested LanguageInfoDomain in its supported_runtimes field. Using asdict() ensures the nested dataclass is recursively converted to a dictionary, which vars() would not do.

Line 61 correctly uses vars() for ResourceUsageDomain since it contains only primitive fields and no nested dataclasses.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d80be86 and 05c79d9.

📒 Files selected for processing (2)
  • frontend/src/components/NotificationCenter.svelte
  • frontend/src/stores/notificationStore.ts
🧰 Additional context used
🪛 GitHub Actions: Frontend CI
frontend/src/stores/notificationStore.ts

[error] 1-1: Vitest: No "loading" export is defined on the "../../stores/notificationStore" mock. Did you forget to return it from vi.mock? Ensure the mock provides a 'loading' export or use importOriginal to extend the mock.

frontend/src/components/NotificationCenter.svelte

[error] 65-70: Vitest: No "loading" export is defined on the "../../stores/notificationStore" mock. Did you forget to return it from vi.mock? Ensure the mocked module provides a 'loading' export or adjust the mock implementation.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build Frontend
  • GitHub Check: Integration Tests
🔇 Additional comments (1)
frontend/src/components/NotificationCenter.svelte (1)

7-7: LGTM! Clean refactoring to centralized loading state.

The import at line 7 and usage with $loading at line 248 are both correct. The Svelte 5 store subscription syntax properly unwraps the derived store value, and this aligns well with centralizing state management. The local loading state declaration has been properly removed with no lingering references.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 05c79d9 and 9f674c3.

📒 Files selected for processing (1)
  • frontend/src/components/__tests__/NotificationCenter.test.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Scan Frontend
  • GitHub Check: E2E Tests
  • GitHub Check: Integration Tests
🔇 Additional comments (1)
frontend/src/components/__tests__/NotificationCenter.test.ts (1)

89-89: LGTM! Mock export follows established pattern.

The loading getter properly exposes the mockLoading derived store through the mocked module, consistent with how notifications and unreadCount are exported.

Copy link

@coderabbitai coderabbitai bot left a comment

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)
frontend/src/components/NotificationCenter.svelte (1)

209-219: Consider syncing permission state when it changes externally.

The notificationPermission state is only updated when requestNotificationPermission() is called. If the user changes the permission through browser settings or another browser tab, the UI could display a stale state (e.g., the "Enable desktop notifications" button might remain visible even after permission is granted elsewhere).

Consider adding a visibility change or focus listener to refresh the permission state when the user returns to the page.

🔎 Example approach to keep permission state fresh

Add a visibility change listener in the onMount block:

onMount(async () => {
    // ... existing subscription code ...
    
    // Sync permission state when page becomes visible
    const handleVisibilityChange = () => {
        if (!document.hidden && 'Notification' in window) {
            notificationPermission = Notification.permission;
        }
    };
    
    document.addEventListener('visibilitychange', handleVisibilityChange);
    
    return () => {
        // ... existing cleanup ...
        document.removeEventListener('visibilitychange', handleVisibilityChange);
    };
});
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9f674c3 and 03ef734.

📒 Files selected for processing (6)
  • frontend/src/components/NotificationCenter.svelte
  • frontend/src/lib/__tests__/user-settings.test.ts
  • frontend/src/lib/user-settings.ts
  • frontend/src/routes/Settings.svelte
  • frontend/src/stores/__tests__/theme.test.ts
  • frontend/src/stores/theme.ts
🧰 Additional context used
🧬 Code graph analysis (2)
frontend/src/stores/theme.ts (2)
frontend/src/lib/user-settings.ts (1)
  • saveUserSettings (34-50)
frontend/src/stores/userSettings.ts (1)
  • userSettings (13-13)
frontend/src/lib/__tests__/user-settings.test.ts (4)
frontend/src/stores/theme.ts (1)
  • theme (39-48)
frontend/src/components/__tests__/Header.test.ts (1)
  • theme (38-38)
frontend/src/lib/user-settings.ts (2)
  • loadUserSettings (13-32)
  • saveUserSettings (34-50)
frontend/src/stores/userSettings.ts (1)
  • editorSettings (15-18)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: E2E Tests
  • GitHub Check: Build Frontend
  • GitHub Check: Integration Tests
🔇 Additional comments (12)
frontend/src/components/NotificationCenter.svelte (3)

253-263: LGTM! Good implementation of double opt-in pattern.

The permission request UI is well-designed:

  • Only shown when permission is needed (default state)
  • Clear call-to-action with icon and descriptive text
  • Positioned appropriately in the dropdown header
  • Implements explicit opt-in, which is a best practice for notification permissions

This aligns well with the PR objective of "double opt-in for notifications."


267-267: LGTM! Consistent use of centralized loading state.

The template correctly uses $loading to reactively subscribe to the imported loading store, which is consistent with the move to centralized state management.


7-7: The import statement is correct. notificationStore exports loading as a derived store that properly extracts the loading boolean from the store's state.

frontend/src/stores/__tests__/theme.test.ts (1)

6-6: LGTM: Mock updated to match unified API.

The mock correctly reflects the migration from saveThemeSetting to saveUserSettings, maintaining the same Promise-based signature.

frontend/src/stores/theme.ts (2)

26-26: LGTM: Signature updated for unified settings API.

The migration from saveThemeSetting(value: string) to saveUserSettings(partial: { theme?: ThemeValue }) correctly aligns with the new unified user settings flow.

Also applies to: 34-34


72-74: LGTM: Theme toggle now persists to backend.

The addition of saveUserSettings({ theme: next }) ensures theme changes via toggle are persisted when authenticated, with proper null and authentication guards.

frontend/src/routes/Settings.svelte (3)

102-102: LGTM: Store updates correctly integrated.

The setUserSettings(settings) calls appropriately update the reactive store after loading and saving settings, ensuring UI consistency.

Also applies to: 158-158


233-233: LGTM: Theme application unified via setTheme helper.

Replacing direct theme store manipulation with the setTheme helper provides a cleaner, more maintainable API surface for theme changes.

Also applies to: 330-330


197-201: No issues found. The reason field is typed as string | null in the API response, making the optional chaining operator (?.) safe and appropriate. When reason exists, it is guaranteed to be a string, so .includes('restore') will not throw an error.

frontend/src/lib/user-settings.ts (1)

13-32: LGTM: Settings loading correctly implemented.

The function properly loads settings from the API, updates the store via setUserSettings, and applies theme when present. Error handling is appropriate.

frontend/src/lib/__tests__/user-settings.test.ts (2)

1-48: LGTM: Test infrastructure correctly updated.

The mock setup properly reflects the unified API surface with mockUpdateUserSettings replacing separate endpoint mocks, and includes appropriate mocks for setUserSettings and setTheme store functions.


54-228: LGTM: Comprehensive test coverage for unified API.

The tests thoroughly validate:

  • API integration for loading and saving settings
  • Store updates via setUserSettings
  • Conditional theme application via setTheme
  • Authentication guards
  • Error handling for both API and network failures

Particularly good: line 189-197 verifies theme is not applied when only editor settings are saved.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
frontend/src/routes/Settings.svelte (2)

105-105: Consider restoring nullish coalescing for consistency.

Lines 105 and 111 changed from ?? to ||, which alters the fallback behavior. While ?? only defaults on null/undefined, || also defaults on empty strings and other falsy values. For consistency with the other defaults in formData (lines 107-110, 114-120 use ??), consider whether this change was intentional.

🔎 Proposed change for consistency
-            theme: settings.theme || 'auto',
+            theme: settings.theme ?? 'auto',
             notifications: {
                 execution_completed: settings.notifications?.execution_completed ?? true,
                 execution_failed: settings.notifications?.execution_failed ?? true,
                 system_updates: settings.notifications?.system_updates ?? true,
                 security_alerts: settings.notifications?.security_alerts ?? true,
-                channels: settings.notifications?.channels || ['in_app']
+                channels: settings.notifications?.channels ?? ['in_app']
             },

Also applies to: 111-111


197-201: Remove unused isRestore field.

The isRestore field is computed but never used in the template. Consider removing it to reduce unnecessary computation.

🔎 Proposed change to remove unused field
             history = (data?.history || [])
                 .map(item => ({
                     ...item,
-                    displayField: item.field,
-                    isRestore: item.reason?.includes('restore')
+                    displayField: item.field
                 }))
                 .sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp));
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 03ef734 and 053b4b5.

📒 Files selected for processing (2)
  • frontend/src/main.ts
  • frontend/src/routes/Settings.svelte
🧰 Additional context used
🧬 Code graph analysis (1)
frontend/src/main.ts (1)
frontend/src/stores/errorStore.ts (1)
  • appError (26-26)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: E2E Tests
  • GitHub Check: Build Frontend
  • GitHub Check: Integration Tests
🔇 Additional comments (5)
frontend/src/main.ts (1)

18-20: LGTM! Enhanced error handling for unhandled promise rejections.

The changes improve robustness by:

  • Logging rejections at the appropriate error level
  • Normalizing non-Error rejection reasons into Error objects for consistent handling
  • Leveraging centralized error management via the appError store

The type guard and String() conversion safely handle edge cases (null, undefined, primitives, objects).

frontend/src/routes/Settings.svelte (4)

10-10: LGTM!

The new imports align with the unified settings architecture, replacing legacy localStorage-based caching with reactive stores.

Also applies to: 14-14


102-102: LGTM!

Settings are correctly persisted to the store after successful load and save operations, ensuring the reactive store stays synchronized with backend state.

Also applies to: 158-158


232-234: LGTM!

Applying the theme immediately after restore provides good visual feedback. The implementation correctly awaits loadSettings() before applying the theme, avoiding race conditions.


326-332: Verify that immediate theme application without auto-save is the intended UX.

When a user selects a theme from the dropdown, setTheme applies it immediately (line 330) but doesn't save the settings to the backend. If the user doesn't click "Save Settings," the theme change won't persist across sessions. This provides instant visual feedback but could confuse users who expect the change to be permanent.

Consider whether:

  1. This preview behavior is intentional and acceptable
  2. Auto-save should be triggered on theme change
  3. A visual indicator should inform users that changes aren't saved yet

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/src/stores/auth.ts (1)

49-57: Inconsistent user settings cleanup across auth-clearing paths.

The clearAuth function in this file doesn't call clearUserSettings(), but _clearAuth in frontend/src/lib/auth-init.ts (line 150) does. This means user settings are only cleared when auth is cleared through the auth-init path, not through direct calls to logout/clearAuth in this module.

Additionally, the AI summary claims clearUserSettings() is called in this function, but it's not present in the code.

🔎 Verify user settings clearing behavior
#!/bin/bash
# Search for all auth clearing paths to ensure consistent user settings cleanup

echo "=== Searching for clearAuth function definitions ==="
ast-grep --pattern 'function clearAuth() { $$$ }'

echo -e "\n=== Searching for clearUserSettings usage ==="
rg -n 'clearUserSettings\(\)' --type ts

echo -e "\n=== Searching for logout function implementations ==="
ast-grep --pattern 'export async function logout() { $$$ }'
🧹 Nitpick comments (1)
frontend/src/lib/api-interceptors.ts (1)

42-50: Consider adding user settings cleanup to auth failure path.

The clearAuthState function is called when handling 401 errors (line 78), but unlike _clearAuth in auth-init.ts, it doesn't call clearUserSettings(). This creates inconsistent cleanup behavior across different auth-clearing code paths.

🔎 Verify if user settings should be cleared on auth failure
#!/bin/bash
# Check how clearAuthState is used and if it should clear user settings

echo "=== Usage of clearAuthState ==="
rg -n -C 3 'clearAuthState\(\)' --type ts

echo -e "\n=== Compare with other auth clearing functions ==="
rg -n -C 5 'function.*clearAuth' --type ts
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 053b4b5 and d13a257.

📒 Files selected for processing (5)
  • frontend/src/lib/__tests__/auth-init.test.ts
  • frontend/src/lib/api-interceptors.ts
  • frontend/src/lib/auth-init.ts
  • frontend/src/stores/__tests__/auth.test.ts
  • frontend/src/stores/auth.ts
🧰 Additional context used
🧬 Code graph analysis (2)
frontend/src/stores/__tests__/auth.test.ts (1)
frontend/src/stores/auth.ts (1)
  • logout (100-109)
frontend/src/lib/__tests__/auth-init.test.ts (1)
frontend/src/lib/auth-init.ts (1)
  • AuthInitializer (16-167)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: E2E Tests
  • GitHub Check: Build Frontend
  • GitHub Check: Integration Tests
🔇 Additional comments (10)
frontend/src/lib/auth-init.ts (3)

3-3: Good integration of user settings cleanup.

The import and usage of clearUserSettings() ensures user settings are properly cleared when auth is invalidated through this initialization path.


128-137: Simplified auth persistence without timestamp expiration.

The removal of timestamp-based expiration logic from _getPersistedAuth is reasonable since sessionStorage automatically expires when the browser session ends. However, verify that this doesn't create issues with stale auth data within a long-running session.

Note: The _isRecentAuth check (line 139) still validates timestamp for network error handling, which provides some staleness protection.


143-152: LGTM - Consistent auth and settings cleanup.

The _clearAuth function properly clears both authentication state and user settings, then removes the sessionStorage entry. This ensures a clean logout experience.

frontend/src/lib/__tests__/auth-init.test.ts (3)

58-72: Thorough test migration to sessionStorage.

The test setup correctly migrates from localStorage to sessionStorage mocks. The renaming from localStorageData to sessionStorageData improves clarity, and all mock implementations are properly updated.


156-198: Comprehensive coverage of sessionStorage persistence behavior.

The tests properly verify auth state persistence and cleanup with sessionStorage, including setting stores from persisted data and clearing sessionStorage on verification failure.


336-344: Good error handling coverage.

The test correctly validates graceful handling of malformed JSON in sessionStorage, ensuring the auth initialization doesn't crash on corrupted data.

frontend/src/stores/__tests__/auth.test.ts (3)

19-30: Consistent test setup for sessionStorage migration.

The mock implementation correctly simulates sessionStorage behavior, providing good test isolation for the storage migration.


62-80: Test confirms sessionStorage restoration.

The test properly validates that auth state is restored from sessionStorage on module initialization, ensuring the persistence mechanism works correctly within a session.


109-130: sessionStorage persistence and cleanup verified.

The tests confirm that auth state is both persisted to sessionStorage on login and properly cleared on logout. This ensures the full lifecycle of session-scoped auth storage is working correctly.

Note on UX implications: The migration from localStorage to sessionStorage means users will need to re-authenticate when:

  • Opening the application in a new tab
  • Closing and reopening the browser
  • The browser session expires

This is more secure but may impact user experience compared to the previous localStorage behavior.

Also applies to: 186-193

frontend/src/stores/auth.ts (1)

22-25: Clarify that sessionStorage improves session data hygiene but doesn't directly mitigate CSRF vulnerability.

The migration from localStorage to sessionStorage is a reasonable session-scoped data practice—sessionStorage clears on tab close, reducing stale session data persistence. However, this change does not directly address CSRF vulnerability. Both sessionStorage and localStorage are equally exposed to XSS since they are readable by JavaScript.

The actual CSRF protection in this codebase relies on the backend's double-submit cookie validation pattern: the server validates that the X-CSRF-Token header matches the csrf_token cookie (using constant-time comparison in app/core/security.py), combined with SameSite=strict on the access token cookie. The storage location of the client-side token copy is irrelevant to CSRF defense—only the server-side validation matters.

If the goal was to address CSRF vulnerability, sessionStorage is an appropriate choice since it limits token lifetime to the active session, but document that CSRF protection itself depends on server-side validation, not storage mechanism.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (4)
frontend/src/components/ToastContainer.svelte (1)

22-46: Consider using immutable updates instead of direct mutation.

The pattern of mutating toast.progress and toast.timerStarted directly (lines 27, 35, 45) followed by toasts.update(n => n) works but is a code smell. It relies on the store holding mutable references.

🔎 Suggested immutable alternative
-        toast.progress = progress;
-        toasts.update(n => n);
+        toasts.update(n => n.map(t =>
+            t.id === toast.id ? { ...t, progress } : t
+        ));

This ensures explicit store updates without relying on reference mutation. Not blocking, as the current approach functions correctly.

frontend/src/routes/Settings.svelte (1)

197-203: History mapping simplified, but sort comparison may have TypeScript issues.

The simplified displayField and isRestore derivation is cleaner. However, the sort comparison new Date(b.timestamp) - new Date(a.timestamp) works at runtime (Date subtraction coerces to numbers) but may trigger TypeScript strictness warnings in some configurations.

🔎 Optional: Explicit numeric comparison for type safety
             .sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp));
+            .sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
frontend/src/routes/Editor.svelte (1)

527-529: Empty catch block silently swallows errors.

The empty catch block after attempting to fetch the result will silently fail. Consider logging the error for debugging purposes.

🔎 Add error logging in catch block
                 } catch {
-                    // Keep the apiError
+                    // Keep the original apiError - secondary fetch also failed
+                    console.warn('Fallback result fetch also failed');
                 }
frontend/src/components/NotificationCenter.svelte (1)

204-214: Consider tracking external permission changes.

The permission state is initialized correctly and SSR-safe. However, if the user changes notification permission in browser settings (without using the in-app button), the UI won't reflect that change. Consider adding error handling and optionally listening for permission changes.

🔎 Optional improvements

Add error handling to requestNotificationPermission:

 async function requestNotificationPermission(): Promise<void> {
     if (!('Notification' in window)) return;
+    try {
         const result = await Notification.requestPermission();
         notificationPermission = result;
+    } catch (err) {
+        console.error('Failed to request notification permission:', err);
+    }
 }

Optionally, sync permission state when the window regains focus:

onMount(() => {
    const handleVisibilityChange = () => {
        if (document.visibilityState === 'visible' && 'Notification' in window) {
            notificationPermission = Notification.permission;
        }
    };
    document.addEventListener('visibilitychange', handleVisibilityChange);
    return () => document.removeEventListener('visibilitychange', handleVisibilityChange);
});
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d13a257 and db44f2b.

📒 Files selected for processing (12)
  • frontend/src/components/ErrorDisplay.svelte
  • frontend/src/components/Footer.svelte
  • frontend/src/components/Header.svelte
  • frontend/src/components/NotificationCenter.svelte
  • frontend/src/components/ToastContainer.svelte
  • frontend/src/components/__tests__/Header.test.ts
  • frontend/src/components/__tests__/NotificationCenter.test.ts
  • frontend/src/routes/Editor.svelte
  • frontend/src/routes/Home.svelte
  • frontend/src/routes/Notifications.svelte
  • frontend/src/routes/Settings.svelte
  • frontend/src/routes/admin/AdminLayout.svelte
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/src/components/tests/NotificationCenter.test.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: E2E Tests
  • GitHub Check: Build Frontend
  • GitHub Check: Integration Tests
🔇 Additional comments (27)
frontend/src/components/Footer.svelte (2)

2-2: LGTM! Clean migration to Lucide icons.

Using @lucide/svelte is an excellent choice for standardizing icons across the codebase. The library is well-maintained and provides consistent, accessible icon components.


54-64: Excellent implementation with proper accessibility and security.

The icon component usage demonstrates best practices:

  • sr-only spans ensure screen reader accessibility
  • rel="noopener noreferrer" prevents security vulnerabilities with external links
  • Icon choice is intuitive (Send for Telegram, Github for GitHub)
  • Consistent sizing and styling across both icons
frontend/src/components/ToastContainer.svelte (4)

1-5: LGTM! Clean migration to Lucide icons.

The import of semantic icon components (CircleCheck, CircleX, AlertTriangle, Info, X) from @lucide/svelte properly replaces the inline SVG approach, improving maintainability and consistency with the PR objective of using an icons library.


55-59: Good cleanup implementation.

The onDestroy hook properly unsubscribes from the store and clears all active timers, preventing memory leaks.


61-107: Well-structured styling helpers.

The helper functions provide clean separation of styling logic with consistent patterns across all toast types, proper dark mode support, and sensible defaults.


121-124: Correct usage of Lucide icon components.

The conditional icon rendering properly maps each toast type to its semantic icon (CircleCheck for success, CircleX for error, AlertTriangle for warning, Info for info), and the close button appropriately uses the X icon with consistent sizing.

Also applies to: 136-136

frontend/src/routes/admin/AdminLayout.svelte (1)

9-9: LGTM! Clean icon component migration.

The ShieldCheck import and usage follow the Lucide Svelte conventions correctly. The class prop for sizing and theming is appropriate.

Also applies to: 82-82

frontend/src/components/ErrorDisplay.svelte (1)

2-3: LGTM! Appropriate icon migration for error display.

AlertTriangle is semantically appropriate for an error display component. The styling matches the error theme context.

Also applies to: 38-38

frontend/src/routes/Home.svelte (2)

4-12: LGTM! Well-structured icon component migration.

The use of Component type for the features array and <svelte:component> for dynamic rendering is the correct Svelte 5 pattern. The explicit typing { icon: Component; title: string; content: string }[] improves type safety.


63-63: LGTM! Dynamic icon rendering.

Using <svelte:component this={feature.icon} class="h-6 w-6" /> correctly renders the Lucide icon components with proper sizing.

frontend/src/components/Header.svelte (3)

8-8: LGTM! Comprehensive icon imports.

All required Lucide icons are properly imported for the various header states (theme toggle, user menu, mobile menu, auth actions).


98-104: LGTM! Theme toggle icons.

The conditional rendering for Sun/Moon/MonitorCog based on theme state is correct and follows the expected behavior.


192-196: LGTM! Mobile menu toggle icons.

The X/Menu icon swap based on isMenuActive state correctly indicates menu open/closed state.

frontend/src/routes/Settings.svelte (3)

10-16: LGTM! Updated imports for settings and theme management.

The imports correctly bring in setTheme for immediate theme application and setUserSettings for reactive store updates, along with the ChevronDown icon.


103-106: Good integration: Settings sync to reactive store.

Calling setUserSettings(settings) after loading ensures the reactive store stays synchronized with the fetched settings. The theme fallback to 'auto' is appropriate.


233-235: LGTM! Theme restoration applies immediately.

Calling setTheme(settings.theme) after restore ensures the UI theme updates immediately without requiring a page refresh.

frontend/src/routes/Editor.svelte (5)

59-71: LGTM! Language extension mapping for multi-language support.

The getLanguageExtension function correctly maps runtime languages to CodeMirror language extensions. Using StreamLanguage.define() for legacy modes (ruby, shell) is the correct approach for CodeMirror 6.


159-163: Good use of derived state for runtime availability.

The derived acceptedFileExts and runtimesAvailable values reactively update based on supportedRuntimes, providing clean UI state management.


283-290: LGTM! Dynamic syntax highlighting on language change.

The subscription to selectedLang that reconfigures the languageCompartment ensures syntax highlighting updates when users switch languages.


672-708: Dynamic file extension detection for multi-language upload.

The file upload handler correctly builds an extension-to-language map from supportedRuntimes and auto-selects the appropriate language and version. Good UX improvement.


1091-1093: Run button correctly disabled when runtimes unavailable.

Disabling execution when !runtimesAvailable prevents users from attempting to run code when the backend configuration isn't loaded.

frontend/src/routes/Notifications.svelte (3)

2-2: LGTM! Icon imports for notification UI.

The Component type import and Lucide icons are correctly added for the notification rendering refactor.

Also applies to: 11-11


114-120: Good semantic icon selection based on notification tags.

The getNotificationIcon function now returns appropriate icon components:

  • AlertCircle for failures/errors/security alerts
  • Clock for timeouts/warnings
  • CircleCheck for success states
  • Info as a sensible default

The return type change to Component enables dynamic rendering with <svelte:component>.


166-168: LGTM! Dynamic icon rendering in notification list.

The empty state uses a centered Bell icon appropriately, and the notification items use <svelte:component this={getNotificationIcon(notification.tags)}> for dynamic icon selection.

Also applies to: 192-192

frontend/src/components/NotificationCenter.svelte (3)

2-2: LGTM: Icon library migration.

The imports correctly add the Component type for type safety and Lucide icon components to replace hardcoded SVGs. The loading store import provides better state management integration.

Also applies to: 7-7, 9-9


19-25: LGTM: Type-safe icon mapping.

The refactored function correctly returns Lucide component types based on notification tags. The Set-based tag checking and semantic icon mappings (error→AlertCircle, warning→AlertTriangle, success→CircleCheck) are appropriate.


223-223: LGTM: Clean component-based icon rendering.

The template changes correctly use Lucide components:

  • Bell icon in the notification button (line 223)
  • Conditional permission request button with good UX (lines 248-256)
  • Store-derived loading state for consistency (line 260)
  • Dynamic icon rendering via <svelte:component> (line 305)

Also applies to: 248-256, 260-260, 305-305

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (7)
frontend/src/lib/api-interceptors.ts (1)

117-125: Verify fallthrough behavior for unhandled status codes.

The changes correctly suppress toasts for auth endpoints. However, consider the fallthrough for status codes not explicitly handled (e.g., 400, 404, 405):

  • If !response && isAuthEndpoint: skips network error toast, falls through to line 122 where it's also silenced ✓
  • If response exists with unhandled status (e.g., 404) on non-auth endpoint: shows generic toast ✓
  • If response exists with unhandled status on auth endpoint: returns silently ✓

The logic appears correct. For clarity, consider adding a brief comment noting which status codes fall through to the generic handler (4xx not in {401, 403, 422, 429}).

🔎 Optional: Add comment for clarity on fallthrough cases
         return error;
     }

+    // Fallthrough: handles remaining 4xx errors (400, 404, etc.) not caught above
     // Don't toast for auth-related silent failures
     if (!isAuthEndpoint) {
         addToast(getErrorMessage(error, 'An error occurred'), 'error');
     }
frontend/src/lib/editor/languages.ts (1)

17-19: Consider logging unknown language requests.

The silent fallback to Python when a language is not found could make debugging difficult if unsupported languages are requested.

🔎 Add a console warning for unsupported languages
 export function getLanguageExtension(lang: string): LanguageSupport | Extension {
+    if (!languageExtensions[lang]) {
+        console.warn(`Language '${lang}' not supported, falling back to Python`);
+    }
     return languageExtensions[lang]?.() ?? python();
 }
frontend/src/components/editor/SavedScripts.svelte (1)

45-64: Consider extracting fallback language constants.

The hardcoded fallback values 'python' and '3.11' appear multiple times (lines 49, 53). Extracting these as named constants would improve maintainability.

🔎 Extract constants
 <script lang="ts">
     import { slide } from 'svelte/transition';
     import { List, Trash2 } from '@lucide/svelte';
+
+    const DEFAULT_LANGUAGE = 'python';
+    const DEFAULT_VERSION = '3.11';

     interface SavedScript {
         // ...
     }

Then use DEFAULT_LANGUAGE and DEFAULT_VERSION instead of the string literals.

frontend/src/lib/editor/execution.svelte.ts (1)

35-82: Consider adding timeout and cleanup safeguards for EventSource.

The EventSource implementation lacks explicit timeout handling and may not properly clean up in all scenarios:

  1. No timeout for the EventSource connection - if events stop arriving, the promise hangs indefinitely
  2. If the component unmounts during execution, the EventSource may remain open
  3. Multiple fallback calls could race (e.g., if onerror fires while handling a failure event)
🔎 Recommended improvements
// Add timeout handling
const EXECUTION_TIMEOUT = 300000; // 5 minutes
const timeout = setTimeout(() => {
    eventSource.close();
    reject(new Error('Execution timeout'));
}, EXECUTION_TIMEOUT);

// Ensure single fallback call
let fallbackCalled = false;
const fetchFallback = async () => {
    if (fallbackCalled) return;
    fallbackCalled = true;
    // ... existing fallback logic
};

// Clear timeout on completion
eventSource.onmessage = async (event) => {
    try {
        // ... existing logic
        if (eventType === 'result_stored') {
            clearTimeout(timeout);
            eventSource.close();
            resolve(eventData.result);
        }
    } catch (err) {
        clearTimeout(timeout);
        // ... error handling
    }
};

Also consider exposing an abort() method from the returned state to allow external cleanup.

frontend/src/components/editor/ResourceLimits.svelte (1)

13-18: Consider a more semantically appropriate icon.

MessageSquare doesn't convey "resource limits" well. Consider using an icon like Gauge, Activity, or Settings2 from Lucide to better represent system resource constraints.

frontend/src/components/editor/CodeMirrorEditor.svelte (1)

69-76: Batch multiple editor dispatches into a single transaction for better performance.

Dispatching 5 separate transactions causes 5 DOM updates. CodeMirror supports batching effects in a single dispatch.

🔎 Proposed fix
 function applySettings() {
     if (!view) return;
-    view.dispatch({ effects: themeCompartment.reconfigure(getThemeExtension()) });
-    view.dispatch({ effects: fontSizeCompartment.reconfigure(EditorView.theme({ ".cm-content": { fontSize: `${settings.font_size ?? 14}px` } })) });
-    view.dispatch({ effects: tabSizeCompartment.reconfigure(EditorState.tabSize.of(settings.tab_size ?? 4)) });
-    view.dispatch({ effects: lineNumbersCompartment.reconfigure(settings.show_line_numbers ? lineNumbers() : []) });
-    view.dispatch({ effects: lineWrappingCompartment.reconfigure(settings.word_wrap ? EditorView.lineWrapping : []) });
+    view.dispatch({
+        effects: [
+            themeCompartment.reconfigure(getThemeExtension()),
+            fontSizeCompartment.reconfigure(EditorView.theme({ ".cm-content": { fontSize: `${settings.font_size ?? 14}px` } })),
+            tabSizeCompartment.reconfigure(EditorState.tabSize.of(settings.tab_size ?? 4)),
+            lineNumbersCompartment.reconfigure(settings.show_line_numbers ? lineNumbers() : []),
+            lineWrappingCompartment.reconfigure(settings.word_wrap ? EditorView.lineWrapping : [])
+        ]
+    });
 }
frontend/src/components/editor/OutputPanel.svelte (1)

144-147: CPU time calculation assumes 100Hz jiffies.

The formula cpu_time_jiffies * 10 assumes the kernel reports jiffies at 100Hz (10ms per jiffy). This is common on Linux but not universal (some systems use 250Hz or 1000Hz). Consider documenting this assumption or making it configurable.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between db44f2b and 35d22bf.

📒 Files selected for processing (15)
  • frontend/rollup.config.js
  • frontend/src/components/editor/CodeMirrorEditor.svelte
  • frontend/src/components/editor/EditorToolbar.svelte
  • frontend/src/components/editor/LanguageSelect.svelte
  • frontend/src/components/editor/OutputPanel.svelte
  • frontend/src/components/editor/ResourceLimits.svelte
  • frontend/src/components/editor/SavedScripts.svelte
  • frontend/src/components/editor/ScriptActions.svelte
  • frontend/src/components/editor/index.ts
  • frontend/src/lib/api-interceptors.ts
  • frontend/src/lib/editor/execution.svelte.ts
  • frontend/src/lib/editor/index.ts
  • frontend/src/lib/editor/languages.ts
  • frontend/src/routes/Editor.svelte
  • frontend/src/styles/pages.css
🧰 Additional context used
🧬 Code graph analysis (2)
frontend/src/lib/api-interceptors.ts (6)
frontend/src/lib/editor/execution.svelte.ts (1)
  • error (106-106)
frontend/src/lib/auth-init.ts (1)
  • isAuthenticated (154-160)
frontend/src/components/__tests__/Header.test.ts (1)
  • isAuthenticated (31-31)
frontend/src/components/__tests__/NotificationCenter.test.ts (1)
  • isAuthenticated (81-81)
frontend/src/stores/toastStore.ts (1)
  • addToast (16-25)
frontend/src/lib/__mocks__/api-interceptors.ts (1)
  • getErrorMessage (7-7)
frontend/src/lib/editor/execution.svelte.ts (1)
frontend/src/lib/api-interceptors.ts (1)
  • getErrorMessage (18-36)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Scan Frontend
  • GitHub Check: E2E Tests
  • GitHub Check: Integration Tests
🔇 Additional comments (25)
frontend/rollup.config.js (2)

187-187: LGTM! TypeScript extension support added.

Adding .ts to the resolver extensions properly aligns with the project's TypeScript configuration and the .ts entry point. This ensures TypeScript module imports are resolved correctly.


147-148: Implementation is correct. Both @codemirror/lang-javascript and @codemirror/lang-go are properly declared in package.json and actively imported in frontend/src/lib/editor/languages.ts. The manual chunk declarations in rollup.config.js are justified and appropriate.

frontend/src/lib/api-interceptors.ts (2)

49-49: Consistent with session-scoped auth handling.

The change from localStorage to sessionStorage aligns with the broader auth state persistence shift mentioned in the context (auth-init.ts, auth.ts). Session-scoped storage is appropriate for auth state as it clears when the browser session ends.


73-91: Improved 401 handling with proper session-awareness.

The logic correctly differentiates between:

  1. Auth endpoints (return early, let them handle their own messaging)
  2. Previously authenticated users (show toast + redirect)
  3. Unauthenticated users or concurrent 401s (silent clear)

The isHandling401 flag with 1-second cooldown in finally prevents toast spam during rapid 401 responses.

One minor observation: if wasAuthenticated is false, calling clearAuthState() on line 89 is harmless but redundant since there's nothing to clear. Consider guarding it, though the current approach is safer for edge cases.

frontend/src/styles/pages.css (1)

23-23: LGTM! Consistent bottom padding improvement.

The addition of 1rem bottom padding across all breakpoints provides better spacing for the editor layout and aligns well with the editor refactoring effort.

Also applies to: 28-28, 87-87, 113-113, 119-119

frontend/src/lib/editor/languages.ts (1)

1-15: LGTM! Clean language extension mapping.

The lazy evaluation pattern using functions in the map is efficient and well-structured for dynamic language support.

frontend/src/components/editor/EditorToolbar.svelte (2)

1-11: LGTM! Well-typed component props.

The Props interface and $props() usage follow Svelte 5 best practices correctly.


13-26: LGTM! Good accessibility and responsive design.

The toolbar includes proper accessibility labels and responsive text visibility. The use of currentTarget.value in the input handler is correct.

frontend/src/components/editor/LanguageSelect.svelte (2)

1-19: LGTM! Well-structured component state.

The reactive state management and derived availability check are implemented correctly following Svelte 5 patterns.


21-31: LGTM! Proper disabled state handling.

The button correctly handles the unavailable state with appropriate styling and cursor changes.

frontend/src/components/editor/SavedScripts.svelte (2)

1-27: LGTM! Clean component structure and refresh pattern.

The toggle function correctly refreshes the script list when expanding the panel, ensuring up-to-date data.


29-39: LGTM! Excellent accessibility features.

The toggle button includes proper ARIA attributes and dynamic labels for screen readers.

frontend/src/lib/editor/execution.svelte.ts (3)

1-19: LGTM! Clean state management pattern.

The factory function and reset logic follow Svelte 5 patterns correctly with proper state encapsulation.


21-33: LGTM! Proper initialization and error handling.

The execution start correctly resets state and handles API errors appropriately.


84-113: LGTM! Robust error recovery pattern.

The error handling attempts to recover results even after failures and properly resets state in the finally block. The getter-based API maintains reactivity correctly.

frontend/src/components/editor/ScriptActions.svelte (1)

1-37: LGTM! Clean action buttons with proper authentication check.

The component is well-structured with appropriate conditional rendering for the Save button based on authentication state. All buttons include accessibility-friendly titles.

frontend/src/lib/editor/index.ts (1)

1-2: LGTM! Standard barrel export pattern.

The index file properly exposes the editor utilities for external consumption.

frontend/src/components/editor/ResourceLimits.svelte (1)

1-9: LGTM - Clean component setup with Svelte 5 runes.

The component correctly uses $props() and $state() for reactive state management. The imports are appropriately scoped.

frontend/src/components/editor/CodeMirrorEditor.svelte (2)

114-124: LGTM - Public API methods are well-designed.

The setContent and getView methods provide clean escape hatches for parent components to interact with the editor imperatively when needed.


109-112: The void settings pattern correctly tracks nested property changes in this context.

The editorSettings store creates a new object instance on every change via object spread syntax ({...DEFAULT_EDITOR_SETTINGS, ...$userSettings?.editor}), not mutating the existing object. When any nested property changes, the entire object reference changes, which Svelte 5's effect system will detect. The void settings dependency will correctly trigger applySettings() re-runs when any property is modified. Destructuring is unnecessary.

Likely an incorrect or invalid review comment.

frontend/src/components/editor/OutputPanel.svelte (2)

16-29: LGTM - ANSI conversion with proper sanitization.

Good security practice using DOMPurify with a restricted allowlist of tags and attributes before rendering ANSI-converted HTML.


58-170: Well-structured output panel with comprehensive state handling.

The component cleanly handles idle, executing, error, and result states with appropriate visual feedback. The copy-to-clipboard functionality with toast notifications enhances UX.

frontend/src/components/editor/index.ts (1)

1-7: LGTM - Clean barrel exports for editor components.

This consolidates editor component imports nicely, enabling consumers to use import { CodeMirrorEditor, OutputPanel, ... } from '../components/editor'.

frontend/src/routes/Editor.svelte (2)

101-143: Well-structured initialization with proper cleanup.

The onMount correctly sets up subscriptions and stores unsubscribe functions, and onDestroy properly cleans them up. The auth state handling and runtime configuration loading are logically organized.


233-263: Robust file upload handling with language detection.

The file upload logic correctly builds a reverse mapping from file extensions to languages, validates against supported runtimes, and provides clear error feedback for unsupported file types.

- keyboard navi support in editor
- smaller fixes
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
frontend/src/routes/Editor.svelte (1)

189-199: Add error feedback for non-404 update failures (past issue still present).

The error handling still silently fails when updateSavedScriptApiV1ScriptsScriptIdPut returns a non-404 error. Users receive no feedback for failures like network errors, 500 errors, or authentication issues.

🔎 Proposed fix
 if (id) {
     const { error, response } = await updateSavedScriptApiV1ScriptsScriptIdPut({ path: { script_id: id }, body });
     if (error) {
         if (response?.status === 404) {
             currentScriptId.set(null);
             const data = unwrap(await createSavedScriptApiV1ScriptsPost({ body }));
             currentScriptId.set(data.id);
             addToast('Script saved successfully.', 'success');
+        } else {
+            addToast('Failed to update script. Please try again.', 'error');
         }
         return;
     }
     addToast('Script updated successfully.', 'success');
🧹 Nitpick comments (3)
frontend/src/components/__tests__/NotificationCenter.test.ts (1)

526-580: Desktop notification permission tests are comprehensive.

The new test suite thoroughly covers the notification permission flow:

  • Enable button visibility based on permission state
  • Permission request invocation
  • Browser notification display on SSE message

The tests are well-structured and cover the critical paths.

Optional: Consider refactoring the browser notification test for clarity

The test at lines 558-579 redefines the global Notification in the middle of execution. For improved clarity and maintainability, consider extracting this test into a separate describe block with its own beforeEach to set up the Notification mock:

+  describe('browser notifications on SSE', () => {
+    let mockNotificationConstructor: ReturnType<typeof vi.fn>;
+
+    beforeEach(() => {
+      mockNotificationConstructor = vi.fn();
+      (globalThis as unknown as { Notification: unknown }).Notification = class {
+        constructor(title: string, options?: NotificationOptions) {
+          mockNotificationConstructor(title, options);
+        }
+        static get permission() { return 'granted'; }
+        static requestPermission = mockRequestPermission;
+      };
+    });
+
+    it('shows browser notification when SSE notification received and permission granted', async () => {
+      mockNotificationPermission = 'granted';
+      const instance = await setupSSE();
+      instance.simulateMessage({ notification_id: 'n1', subject: 'Test Title', body: 'Test Body' });
+      await waitFor(() => {
+        expect(mockNotificationConstructor).toHaveBeenCalledWith('Test Title', expect.objectContaining({ body: 'Test Body' }));
+      });
+    });
+  });

This isolates the global mock setup and makes the test's intent clearer.

frontend/src/components/editor/OutputPanel.svelte (2)

50-56: Acknowledge partial fix from past review; consider explicit 'idle' case for completeness.

The 'starting' phase is now handled explicitly (line 51), which addresses part of the previous concern. However, the 'idle' case still falls through to "Executing...". While this fallback is never reached in practice because line 64 filters out phase !== 'idle', adding an explicit case would improve code clarity and maintainability:

 const phaseLabel = $derived.by(() => {
+    if (phase === 'idle') return 'Ready';
     if (phase === 'starting') return 'Starting...';
     if (phase === 'queued') return 'Queued...';
     if (phase === 'scheduled') return 'Scheduled...';
     if (phase === 'running') return 'Running...';
     return 'Executing...';
 });

98-115: Optional: Remove redundant fallback operators.

Lines 102 and 122 use result.stdout || '' and result.stderr || '' respectively, but these are already inside {#if result.stdout} and {#if result.stderr} blocks. The fallback || '' is redundant since the condition guarantees the value is truthy:

🔎 Proposed simplification
 {#if result.stdout}
     <div class="output-section">
         <h4 class="text-xs font-medium text-fg-muted dark:text-dark-fg-muted mb-1 uppercase tracking-wider">Output:</h4>
         <div class="relative">
-            <pre class="output-pre custom-scrollbar">{@html sanitize(ansiConverter.toHtml(result.stdout || ''))}</pre>
+            <pre class="output-pre custom-scrollbar">{@html sanitize(ansiConverter.toHtml(result.stdout))}</pre>

Apply the same pattern to line 122 for result.stderr.

Also applies to: 117-136

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 35d22bf and 71055a4.

📒 Files selected for processing (4)
  • frontend/src/components/__tests__/NotificationCenter.test.ts
  • frontend/src/components/editor/LanguageSelect.svelte
  • frontend/src/components/editor/OutputPanel.svelte
  • frontend/src/routes/Editor.svelte
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Scan Frontend
  • GitHub Check: E2E Tests
  • GitHub Check: Integration Tests
🔇 Additional comments (11)
frontend/src/components/__tests__/NotificationCenter.test.ts (4)

51-51: Loading state mock infrastructure is now in place.

The mockLoading derived store and its export are correctly implemented. However, the past review comment remains relevant: despite this infrastructure now being available, there are still no test cases that verify the loading UI behavior (e.g., spinner visibility when loading is true/false).

Based on the past review comment, consider adding a test case that:

  1. Sets mocks.mockNotificationsState with loading: true
  2. Renders the component
  3. Asserts the spinner element is visible
  4. Sets loading: false
  5. Asserts the spinner is removed

Also applies to: 89-89


127-134: LGTM: Clean Notification mock implementation.

The configurable Notification mock with permission state tracking is well-structured. Resetting the permission state and mock function in beforeEach ensures test isolation.

Also applies to: 272-273


210-219: LGTM: Icon test data updated for lucide icons.

The test cases correctly map notification tags to their corresponding lucide icon classes. The coverage is comprehensive and aligns with the component's migration to lucide icons.


362-370: LGTM: Icon tests correctly verify lucide icon classes.

The test implementation properly verifies that the SVG element has the expected lucide icon class in its classList. The approach is suitable for testing class-based icon rendering.

frontend/src/components/editor/OutputPanel.svelte (1)

138-164: LGTM! Resource usage calculations are correct.

The resource usage display correctly handles:

  • CPU time: Converts jiffies to milliseconds (× 10) with proper handling of zero values
  • Memory: Converts KB to MiB (÷ 1024)
  • Wall time: Displays seconds with appropriate precision
frontend/src/components/editor/LanguageSelect.svelte (2)

35-85: Excellent keyboard navigation implementation! Past accessibility concern resolved.

The keyboard navigation implementation comprehensively addresses the accessibility barrier flagged in the previous review:

  • ✅ Arrow key navigation between languages and versions
  • ✅ Enter/Space to open submenus and select items
  • ✅ Escape to close menus (submenu first, then main menu)
  • ✅ Focus management with roving tabindex pattern
  • ✅ Visual focus indicators via conditional styling

The state management with focusedLangIndex and focusedVersionIndex cleanly tracks focus position, and the handlers properly distinguish between main menu and submenu contexts.


88-158: LGTM! Proper ARIA implementation with correct role hierarchy.

The template correctly implements accessible menu patterns:

  • Proper ARIA roles (menu, menuitem, menuitemradio)
  • Appropriate ARIA properties (aria-haspopup, aria-expanded, aria-checked)
  • Roving tabindex pattern for keyboard navigation (lines 117, 137)
  • Focus and selection states properly reflected in styling
  • Mouse and keyboard interactions work harmoniously together
frontend/src/routes/Editor.svelte (4)

41-69: LGTM! Persistent store implementation resolves past memory leak concern.

The new implementation correctly addresses the previous memory leak issue by wrapping set and update methods to write to localStorage, rather than creating a subscription. This pattern:

  • Eliminates the need for cleanup (no subscription to unsubscribe)
  • Is more efficient (only writes on mutations, not on every reactive update)
  • Properly handles SSR (returns plain writable when localStorage unavailable)
  • Includes error handling for malformed JSON

314-323: LGTM! Reactive store access now correct (past issue resolved).

Line 314 now correctly uses $script.trim() instead of get(script), ensuring the condition re-evaluates reactively when the script content changes. The empty state UI with the example loading prompt is a nice touch.


244-274: LGTM! Comprehensive file upload with runtime-aware validation.

The file upload implementation correctly:

  • Dynamically builds extension-to-language mapping from supportedRuntimes
  • Validates uploaded file extensions against supported runtimes
  • Auto-detects and sets language/version based on file extension
  • Provides clear error feedback listing accepted extensions
  • Clears input value to allow re-uploading the same file
  • Updates editor content via both store and ref

97-101: LGTM! Proper subscription cleanup prevents memory leaks.

The subscription management correctly:

  • Declares unsubscribe handlers at component scope (lines 97-101)
  • Stores unsubscribe functions from all subscriptions in onMount
  • Cleans up all subscriptions in onDestroy with optional chaining for safety
  • Handles edge cases where cleanup might be called before initialization completes

Also applies to: 156-161

Copy link

@coderabbitai coderabbitai bot left a comment

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 (4)
frontend/src/lib/api-interceptors.ts (4)

25-30: Type assertion could lead to incorrect typing.

The function assumes detail is always string | ValidationError[], but the actual property could be any type. If the API returns an unexpected detail format (e.g., a number or nested object), the return type would be misleading.

🔎 Suggested improvement for safer type handling
 function extractDetail(err: unknown): string | ValidationError[] | null {
     if (typeof err === 'object' && err !== null && 'detail' in err) {
-        return (err as { detail: string | ValidationError[] }).detail;
+        const detail = (err as { detail: unknown }).detail;
+        if (typeof detail === 'string') return detail;
+        if (Array.isArray(detail)) return detail as ValidationError[];
     }
     return null;
 }

50-52: Duplicate logic and same potential runtime error.

This function duplicates the mapping logic from getErrorMessage (line 38), and shares the same vulnerability to empty/missing loc arrays. Consider consolidating to a single helper.

🔎 Suggested consolidation
+function formatSingleValidationError(e: ValidationError): string {
+    const field = e.loc?.length ? e.loc[e.loc.length - 1] : 'field';
+    return `${field}: ${e.msg}`;
+}
+
 function formatValidationErrors(detail: ValidationError[]): string {
-    return detail.map((e) => `${e.loc[e.loc.length - 1]}: ${e.msg}`).join('\n');
+    return detail.map(formatSingleValidationError).join('\n');
 }

Then reuse formatSingleValidationError in getErrorMessage as well.


100-106: Consider validating array elements before treating as ValidationError[].

When checking Array.isArray(detail), the code assumes all elements conform to ValidationError. If the API returns a malformed array, formatValidationErrors could fail.

🔎 Suggested defensive check
     if (status === 422) {
         const detail = extractDetail(error);
-        if (Array.isArray(detail) && detail.length > 0) {
+        if (Array.isArray(detail) && detail.length > 0 && detail.every(e => e?.loc && e?.msg)) {
             addToast(`Validation error:\n${formatValidationErrors(detail)}`, 'error');
             return true;
         }
     }

148-155: Type assertion masks potential undefined return.

If result.data is undefined (no error but also no data), unwrap returns undefined cast as T, which could cause downstream type errors. Consider throwing or providing a clearer contract.

🔎 Suggested improvement
 export function unwrap<T>(result: { data?: T; error?: unknown }): T {
     if (result.error) throw result.error;
+    if (result.data === undefined) throw new Error('No data returned');
     return result.data as T;
 }

Alternatively, change the return type to T | undefined if undefined is a valid outcome.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 71055a4 and 62a357a.

📒 Files selected for processing (1)
  • frontend/src/lib/api-interceptors.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Scan Frontend
  • GitHub Check: E2E Tests
  • GitHub Check: Integration Tests
🔇 Additional comments (1)
frontend/src/lib/api-interceptors.ts (1)

64-81: LGTM!

The 401 handling logic properly debounces repeated failures, preserves the redirect path for post-login navigation, and gracefully handles edge cases. The isHandling401 guard prevents cascading toasts during concurrent failures.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (21)
frontend/src/lib/admin/__tests__/constants.test.ts (2)

32-34: Expand dark mode variant testing to all colors.

The dark mode variant test only validates the green color. For comprehensive coverage, verify that all color entries in STATS_BG_COLORS include dark mode variants.

🔎 Proposed test expansion
     it('includes dark mode variants', () => {
       expect(STATS_BG_COLORS.green).toContain('dark:bg-green');
+      expect(STATS_BG_COLORS.red).toContain('dark:bg-red');
+      expect(STATS_BG_COLORS.yellow).toContain('dark:bg-yellow');
+      expect(STATS_BG_COLORS.blue).toContain('dark:bg-blue');
+      expect(STATS_BG_COLORS.purple).toContain('dark:bg-purple');
+      expect(STATS_BG_COLORS.orange).toContain('dark:bg-orange');
+      expect(STATS_BG_COLORS.neutral).toContain('dark:bg-neutral');
     });

45-47: Expand dark mode variant testing to all text colors.

Similar to the background colors, the dark mode variant test only validates the green text color. Consider testing all color entries for consistency.

🔎 Proposed test expansion
     it('includes dark mode variants', () => {
       expect(STATS_TEXT_COLORS.green).toContain('dark:text-green');
+      expect(STATS_TEXT_COLORS.red).toContain('dark:text-red');
+      expect(STATS_TEXT_COLORS.yellow).toContain('dark:text-yellow');
+      expect(STATS_TEXT_COLORS.blue).toContain('dark:text-blue');
+      expect(STATS_TEXT_COLORS.purple).toContain('dark:text-purple');
+      expect(STATS_TEXT_COLORS.orange).toContain('dark:text-orange');
+      expect(STATS_TEXT_COLORS.neutral).toContain('dark:text-neutral');
     });
frontend/src/components/admin/StatusBadge.svelte (1)

16-20: Tighten the type for sizeClasses.

The sizeClasses mapping uses Record<string, string>, but size is constrained to 'sm' | 'md' | 'lg'. Using a more precise type improves type safety and prevents runtime errors.

🔎 Proposed type refinement
-    const sizeClasses: Record<string, string> = {
+    const sizeClasses: Record<'sm' | 'md' | 'lg', string> = {
         sm: 'text-xs px-1.5 py-0.5',
         md: 'text-sm px-2 py-1',
         lg: 'text-base px-3 py-1.5'
     };
frontend/src/components/admin/events/ReplayProgressBanner.svelte (1)

1-112: Well-structured progress banner with comprehensive error handling.

The component effectively displays replay progress with detailed sections for failures and execution results. The conditional rendering properly guards against null/undefined values, and the visual hierarchy is clear.

Consider adding ARIA attributes to the progress bar (lines 35-40) for improved accessibility, such as role="progressbar", aria-valuenow, aria-valuemin, and aria-valuemax.

frontend/src/components/admin/sagas/SagaStatsCards.svelte (1)

11-13: Consider memoizing counts for better performance.

The getCount function is invoked for each state during render, causing repeated array filtering. For small datasets this is fine, but if saga counts grow, consider pre-computing counts with a derived state.

🔎 Optional optimization using $derived
+    const countsByState = $derived(
+        sagas.reduce((acc, s) => {
+            acc[s.state] = (acc[s.state] || 0) + 1;
+            return acc;
+        }, {} as Record<string, number>)
+    );
+
     function getCount(state: string): number {
-        return sagas.filter(s => s.state === state).length;
+        return countsByState[state] || 0;
     }
frontend/src/components/admin/sagas/SagaFilters.svelte (1)

25-32: Consider debouncing the search inputs to reduce API call frequency.

The oninput={onSearch} on both search inputs triggers the search callback on every keystroke, which may cause excessive API calls and degrade performance. Consider implementing a debounce mechanism (e.g., 300-500ms delay) to wait until the user has finished typing before triggering the search.

💡 Example debounce implementation

You could create a debounced search handler in the script section:

+import { debounce } from '../../../lib/utils';
+
 interface Props {
     searchQuery: string;
     stateFilter: string;
     executionIdFilter: string;
     onSearch: () => void;
     onClear: () => void;
 }

 let {
     searchQuery = $bindable(''),
     stateFilter = $bindable(''),
     executionIdFilter = $bindable(''),
     onSearch,
     onClear
 }: Props = $props();
+
+const debouncedSearch = debounce(onSearch, 300);

Then use oninput={debouncedSearch} instead of oninput={onSearch} for the text inputs.

Also applies to: 45-52

frontend/src/components/admin/users/DeleteUserModal.svelte (1)

26-30: Potential redundancy with $bindable directive.

The handleCascadeChange function manually updates cascadeDelete (line 28) and then calls onCascadeChange. However, since cascadeDelete uses the $bindable directive, the binding should automatically sync the prop when the checkbox changes. The manual assignment might be redundant or could potentially interfere with the binding mechanism.

Consider simplifying to just call the optional callback if you need to notify the parent, or rely solely on the reactive binding without the manual handler.

💡 Simplified approach

If you only need to notify the parent of changes, you could use a reactive statement:

-    function handleCascadeChange(e: Event): void {
-        const target = e.target as HTMLInputElement;
-        cascadeDelete = target.checked;
-        onCascadeChange?.(cascadeDelete);
-    }
+    $effect(() => {
+        onCascadeChange?.(cascadeDelete);
+    });

And update the checkbox:

     <input
         type="checkbox"
-        checked={cascadeDelete}
-        onchange={handleCascadeChange}
+        bind:checked={cascadeDelete}
         class="rounded border-border-default dark:border-dark-border-default"
     />

Alternatively, if the parent doesn't need notifications and just reads the bound value, you can remove onCascadeChange entirely and use bind:checked={cascadeDelete}.

frontend/src/components/admin/users/UsersTable.svelte (1)

94-114: Consider adding aria-label to icon-only action buttons for screen reader accessibility.

The desktop action buttons rely only on title for context. Screen readers may not announce title attributes, so adding aria-label would improve accessibility.

🔎 Suggested improvement
 <button
     onclick={() => onEdit(user)}
     class="text-green-600 hover:text-green-800 dark:text-green-400"
     title="Edit User"
+    aria-label="Edit User"
 >
     <Pencil class="w-5 h-5" />
 </button>
 <button
     onclick={() => onRateLimits(user)}
     class="text-blue-600 hover:text-blue-800 dark:text-blue-400"
     title="Manage Rate Limits"
+    aria-label="Manage Rate Limits"
 >
     <Clock class="w-5 h-5" />
 </button>
 <button
     onclick={() => onDelete(user)}
     class="text-red-600 hover:text-red-800 dark:text-red-400"
     title="Delete User"
+    aria-label="Delete User"
 >
     <Trash2 class="w-5 h-5" />
 </button>
frontend/src/lib/admin/autoRefresh.svelte.ts (2)

88-93: Simplify the $effect condition for clarity.

The condition if (enabled || rate) is confusing: rate is always truthy (default 5), so this effectively always runs. Since start() already guards with if (enabled), you can simplify by just referencing both reactive variables without the conditional.

🔎 Suggested improvement
 $effect(() => {
-    if (enabled || rate) {
-        start();
-    }
+    // Track both enabled and rate for reactivity
+    void enabled;
+    void rate;
+    start();
     return () => stop();
 });

60-65: Consider documenting async callback behavior.

If onRefresh is async and exceeds the interval duration, calls may overlap. The consumer should manage this (e.g., with a loading flag that skips refresh if already in progress). Consider documenting this behavior in the JSDoc.

frontend/src/components/admin/FilterPanel.svelte (1)

2-2: Remove unused icon imports.

X and ChevronDown are imported but not used in the template.

🔎 Suggested fix
-import { X, ChevronDown, Filter } from '@lucide/svelte';
+import { Filter } from '@lucide/svelte';
frontend/src/components/admin/users/UserFormModal.svelte (1)

46-57: Consider adding required attribute to mandatory fields.

The username field is marked with a visual indicator (*) but lacks the HTML5 required attribute, which would provide native browser validation and improve accessibility.

🔎 Suggested improvement
 <input
     id="user-form-username"
     type="text"
     bind:value={form.username}
     class="form-input-standard"
     placeholder="johndoe"
     disabled={saving}
+    required
     autocomplete="username"
     autocorrect="off"
     autocapitalize="off"
     spellcheck="false"
 />

Similarly for the password field when creating a new user (not editing).

frontend/src/components/admin/events/EventsTable.svelte (1)

42-48: Inconsistent timestamp formatting between desktop and mobile views.

Desktop view uses inline toLocaleDateString()/toLocaleTimeString() (lines 44-48), while mobile view uses formatTimestamp() (line 143). This could result in different display formats. Consider using formatTimestamp consistently, or split it into date/time helpers.

🔎 Suggested improvement for consistency
 <td class="px-3 py-2 text-sm text-fg-default dark:text-dark-fg-default">
     <div class="text-xs text-fg-muted dark:text-dark-fg-muted">
-        {new Date(event.timestamp).toLocaleDateString()}
+        {formatTimestamp(event.timestamp, 'date')}
     </div>
     <div class="text-sm">
-        {new Date(event.timestamp).toLocaleTimeString()}
+        {formatTimestamp(event.timestamp, 'time')}
     </div>
 </td>

This assumes extending formatTimestamp to accept an optional format parameter, or creating separate helpers.

frontend/src/components/admin/ActionButtons.svelte (1)

20-27: Consider adding fallback for unknown color values.

If action.color is set to an unrecognized value (not in colorClasses), the lookup returns undefined. Adding a fallback ensures consistent styling.

🔎 Suggested improvement
-const colorClasses: Record<string, string> = {
+const colorClasses: Record<string, string> = {
     primary: 'text-primary hover:text-primary-dark dark:hover:text-primary-light',
     success: 'text-green-600 hover:text-green-800 dark:text-green-400',
     danger: 'text-red-600 hover:text-red-800 dark:text-red-400',
     warning: 'text-yellow-600 hover:text-yellow-800 dark:text-yellow-400',
     info: 'text-blue-600 hover:text-blue-800 dark:text-blue-400',
     default: 'text-fg-muted hover:text-fg-default dark:hover:text-dark-fg-default'
 };
+
+function getColorClass(color?: string): string {
+    return colorClasses[color || 'default'] || colorClasses.default;
+}

Then use getColorClass(action.color) instead of the direct lookup.

frontend/src/components/admin/users/RateLimitsModal.svelte (1)

237-244: Clarify the usage value display logic.

The fallback chain usageData.count || usageData.tokens_remaining || 0 may unintentionally show tokens_remaining when count is 0 (a valid count). Consider using nullish coalescing for more precise handling.

🔎 Proposed fix
                                 <span class="text-sm font-semibold text-fg-default dark:text-dark-fg-default">
-                                    {usageData.count || usageData.tokens_remaining || 0}
+                                    {usageData.count ?? usageData.tokens_remaining ?? 0}
                                 </span>
frontend/src/components/admin/events/EventDetailsModal.svelte (1)

59-67: Consider constraining max-height for large JSON payloads.

The <pre> elements have overflow-auto which is good, but without a max-height constraint, extremely large metadata or payloads could make the modal unusable. Consider adding a height limit.

🔎 Proposed fix
-            <pre class="bg-neutral-100 dark:bg-neutral-800 p-3 rounded overflow-auto text-sm font-mono text-fg-default dark:text-dark-fg-default">{JSON.stringify(event.event.metadata, null, 2)}</pre>
+            <pre class="bg-neutral-100 dark:bg-neutral-800 p-3 rounded overflow-auto max-h-48 text-sm font-mono text-fg-default dark:text-dark-fg-default">{JSON.stringify(event.event.metadata, null, 2)}</pre>
-            <pre class="bg-neutral-100 dark:bg-neutral-800 p-3 rounded overflow-auto text-sm font-mono text-fg-default dark:text-dark-fg-default">{JSON.stringify(event.event.payload, null, 2)}</pre>
+            <pre class="bg-neutral-100 dark:bg-neutral-800 p-3 rounded overflow-auto max-h-64 text-sm font-mono text-fg-default dark:text-dark-fg-default">{JSON.stringify(event.event.payload, null, 2)}</pre>
frontend/src/lib/admin/events/eventTypes.ts (1)

44-57: Simplify getEventTypeLabel - redundant conditional.

Lines 52-55 reconstruct the same string that was passed in. If eventType is "execution.started", splitting and rejoining produces the identical string.

🔎 Proposed fix
 export function getEventTypeLabel(eventType: string): string {
     // For execution.requested, show icon only (with tooltip)
     if (eventType === 'execution.requested') {
         return '';
     }
-
-    // For all other events, show full name
-    const parts = eventType.split('.');
-    if (parts.length === 2) {
-        return `${parts[0]}.${parts[1]}`;
-    }
     return eventType;
 }
frontend/src/lib/admin/sagas/sagaStates.ts (1)

79-83: Replace magic numbers with derived constants.

The hardcoded 5 and 3 for total steps reduce maintainability. If EXECUTION_SAGA_STEPS is modified, this function won't automatically update.

🔎 Proposed fix
+const DEFAULT_SAGA_STEPS = 3;
+
 export function getSagaProgressPercentage(completedSteps: string[], sagaName: string): number {
     if (!completedSteps?.length) return 0;
-    const totalSteps = sagaName === 'execution_saga' ? 5 : 3;
+    const totalSteps = sagaName === 'execution_saga' 
+        ? EXECUTION_SAGA_STEPS.length 
+        : DEFAULT_SAGA_STEPS;
     return Math.min(100, (completedSteps.length / totalSteps) * 100);
 }
frontend/src/lib/admin/pagination.svelte.ts (1)

17-27: Consider exposing pageSizeOptions in the returned state.

pageSizeOptions is defined in PaginationOptions and merged into opts, but it's never exposed in the returned PaginationState. Consumers rendering page-size selectors would benefit from access to this array.

🔎 Proposed fix to PaginationState interface and return object
 export interface PaginationState {
     currentPage: number;
     pageSize: number;
     totalItems: number;
     readonly totalPages: number;
     readonly skip: number;
+    readonly pageSizeOptions: number[];
     handlePageChange: (page: number, onLoad?: () => void) => void;
     handlePageSizeChange: (size: number, onLoad?: () => void) => void;
     reset: () => void;
 }

And in the return statement:

     return {
         // ... existing properties
         get skip() { return skip; },
+        get pageSizeOptions() { return opts.pageSizeOptions; },
         handlePageChange,
         // ...
     };
frontend/src/routes/admin/AdminSagas.svelte (1)

132-137: Simplify auto-refresh effect dependency tracking.

The condition autoRefresh || refreshRate is always truthy when autoRefresh is true. In Svelte 5 $effect, simply reading the variables establishes reactivity. The boolean expression could be misleading.

🔎 Proposed fix
     // Re-setup auto-refresh when settings change
     $effect(() => {
-        if (autoRefresh || refreshRate) {
-            setupAutoRefresh();
-        }
+        // Track both values to trigger effect on changes
+        void autoRefresh;
+        void refreshRate;
+        setupAutoRefresh();
     });
frontend/src/routes/admin/AdminEvents.svelte (1)

346-395: Consider using the Pagination component for consistency.

This section implements pagination UI inline with custom buttons and page size selection. However, AdminUsers.svelte (lines 274-283) uses a reusable Pagination component that provides the same functionality. Using the shared component would improve consistency and reduce code duplication.

💡 Suggested refactor to use Pagination component

Replace the inline pagination UI with:

-                            <div class="flex items-center gap-4">
-                                <div class="flex items-center gap-2">
-                                    <label for="events-page-size" class="text-sm text-fg-muted dark:text-dark-fg-muted">Show:</label>
-                                    <select
-                                        id="events-page-size"
-                                        bind:value={pageSize}
-                                        onchange={() => { currentPage = 1; loadEvents(); }}
-                                        class="px-3 py-1.5 pr-8 rounded-lg border border-neutral-300 dark:border-neutral-600 bg-surface-overlay dark:bg-dark-surface-overlay text-neutral-900 dark:text-neutral-100 text-sm focus:outline-hidden focus:ring-2 focus:ring-blue-500 focus:border-blue-500 appearance-none cursor-pointer"
-                                        style="background-image: url('data:image/svg+xml;utf8,<svg xmlns=%22http://www.w3.org/2000/svg%22 fill=%22none%22 viewBox=%220 0 20 20%22><path stroke=%22%236b7280%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22 stroke-width=%221.5%22 d=%22M6 8l4 4 4-4%22/></svg>'); background-repeat: no-repeat; background-position: right 0.5rem center; background-size: 16px;"
-                                    >
-                                        <option value={10}>10</option>
-                                        <option value={25}>25</option>
-                                        <option value={50}>50</option>
-                                        <option value={100}>100</option>
-                                    </select>
-                                    <span class="text-sm text-fg-muted dark:text-dark-fg-muted">per page</span>
-                                </div>
-
-                                {#if totalPages > 1}
-                                    <div class="flex items-center gap-1">
-                                        <button onclick={() => { currentPage = 1; loadEvents(); }} disabled={currentPage === 1} class="pagination-button" title="First page">
-                                            <ChevronsLeft size={16} />
-                                        </button>
-                                        <button onclick={() => { currentPage--; loadEvents(); }} disabled={currentPage === 1} class="pagination-button" title="Previous page">
-                                            <ChevronLeft size={16} />
-                                        </button>
-                                        <div class="pagination-text">
-                                            <span class="font-medium">{currentPage}</span>
-                                            <span class="text-fg-muted dark:text-dark-fg-muted mx-1">/</span>
-                                            <span class="font-medium">{totalPages}</span>
-                                        </div>
-                                        <button onclick={() => { currentPage++; loadEvents(); }} disabled={currentPage === totalPages} class="pagination-button" title="Next page">
-                                            <ChevronRight size={16} />
-                                        </button>
-                                        <button onclick={() => { currentPage = totalPages; loadEvents(); }} disabled={currentPage === totalPages} class="pagination-button" title="Last page">
-                                            <ChevronsRight size={16} />
-                                        </button>
-                                    </div>
-                                {/if}
-                            </div>
-
-                            <div class="text-xs sm:text-sm text-fg-muted dark:text-dark-fg-muted">
-                                Showing {(currentPage - 1) * pageSize + 1} to {Math.min(currentPage * pageSize, totalEvents)} of {totalEvents} events
-                            </div>
+                        <Pagination
+                            {currentPage}
+                            {totalPages}
+                            totalItems={totalEvents}
+                            {pageSize}
+                            onPageChange={(page) => { currentPage = page; loadEvents(); }}
+                            onPageSizeChange={(size) => { pageSize = size; currentPage = 1; loadEvents(); }}
+                            pageSizeOptions={[10, 25, 50, 100]}
+                            itemName="events"
+                        />

Don't forget to add the import at the top if not already present:

+    import Pagination from '../../components/Pagination.svelte';
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 62a357a and f64db08.

📒 Files selected for processing (43)
  • frontend/src/components/admin/ActionButtons.svelte
  • frontend/src/components/admin/AutoRefreshControl.svelte
  • frontend/src/components/admin/FilterPanel.svelte
  • frontend/src/components/admin/StatsCard.svelte
  • frontend/src/components/admin/StatusBadge.svelte
  • frontend/src/components/admin/events/EventDetailsModal.svelte
  • frontend/src/components/admin/events/EventFilters.svelte
  • frontend/src/components/admin/events/EventStatsCards.svelte
  • frontend/src/components/admin/events/EventsTable.svelte
  • frontend/src/components/admin/events/ReplayPreviewModal.svelte
  • frontend/src/components/admin/events/ReplayProgressBanner.svelte
  • frontend/src/components/admin/events/UserOverviewModal.svelte
  • frontend/src/components/admin/events/index.ts
  • frontend/src/components/admin/index.ts
  • frontend/src/components/admin/sagas/SagaDetailsModal.svelte
  • frontend/src/components/admin/sagas/SagaFilters.svelte
  • frontend/src/components/admin/sagas/SagaStatsCards.svelte
  • frontend/src/components/admin/sagas/SagasTable.svelte
  • frontend/src/components/admin/sagas/index.ts
  • frontend/src/components/admin/users/DeleteUserModal.svelte
  • frontend/src/components/admin/users/RateLimitsModal.svelte
  • frontend/src/components/admin/users/UserFilters.svelte
  • frontend/src/components/admin/users/UserFormModal.svelte
  • frontend/src/components/admin/users/UsersTable.svelte
  • frontend/src/components/admin/users/index.ts
  • frontend/src/lib/admin/__tests__/constants.test.ts
  • frontend/src/lib/admin/autoRefresh.svelte.ts
  • frontend/src/lib/admin/constants.ts
  • frontend/src/lib/admin/events/__tests__/eventTypes.test.ts
  • frontend/src/lib/admin/events/eventTypes.ts
  • frontend/src/lib/admin/events/index.ts
  • frontend/src/lib/admin/index.ts
  • frontend/src/lib/admin/pagination.svelte.ts
  • frontend/src/lib/admin/sagas/__tests__/sagaStates.test.ts
  • frontend/src/lib/admin/sagas/index.ts
  • frontend/src/lib/admin/sagas/sagaStates.ts
  • frontend/src/lib/admin/users/__tests__/rateLimits.test.ts
  • frontend/src/lib/admin/users/index.ts
  • frontend/src/lib/admin/users/rateLimits.ts
  • frontend/src/routes/admin/AdminEvents.svelte
  • frontend/src/routes/admin/AdminSagas.svelte
  • frontend/src/routes/admin/AdminUsers.svelte
  • frontend/src/routes/admin/__tests__/AdminSagas.test.ts
🧰 Additional context used
🧬 Code graph analysis (2)
frontend/src/lib/admin/__tests__/constants.test.ts (1)
frontend/src/lib/admin/constants.ts (5)
  • STATUS_COLORS (6-12)
  • STATS_BG_COLORS (15-23)
  • STATS_TEXT_COLORS (26-34)
  • ROLE_COLORS (37-40)
  • ACTIVE_STATUS_COLORS (43-47)
frontend/src/lib/admin/users/__tests__/rateLimits.test.ts (1)
frontend/src/lib/admin/users/rateLimits.ts (7)
  • GROUP_COLORS (7-15)
  • getGroupColor (17-19)
  • ENDPOINT_GROUP_PATTERNS (22-29)
  • detectGroupFromEndpoint (31-37)
  • getDefaultRules (44-53)
  • getDefaultRulesWithMultiplier (55-62)
  • createEmptyRule (65-76)
🪛 Gitleaks (8.30.0)
frontend/src/lib/admin/users/rateLimits.ts

[high] 13-13: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Scan Frontend
  • GitHub Check: E2E Tests
  • GitHub Check: Integration Tests
🔇 Additional comments (39)
frontend/src/components/admin/sagas/SagaDetailsModal.svelte (1)

1-195: Well-architected saga details modal.

The component provides comprehensive saga information with proper null handling and conditional rendering. The execution steps visualization (lines 88-136) is particularly well-done, with clear visual states and proper fallback handling.

frontend/src/lib/admin/constants.ts (1)

1-51: Clean and well-organized constants file.

The color mappings are consistent, well-documented, and properly typed with const assertions. The type aliases provide useful type safety for consumers.

Note: The static analysis warning about a "Generic API Key" on line 13 is a false positive—it's detecting the string 'api' in the CSS class name 'bg-neutral-100 text-neutral-800 dark:bg-neutral-700 dark:text-neutral-200', not an actual API key.

frontend/src/lib/admin/users/rateLimits.ts (1)

1-76: Well-structured rate limit utilities.

The module provides clear group mappings, pattern detection, and default rule generation. The use of regex patterns for endpoint grouping is pragmatic and maintainable.

Note: The static analysis warning about a "Generic API Key" on line 13 is a false positive—it's flagging the string 'api' in the CSS class definition, not an actual credential.

frontend/src/lib/admin/events/index.ts (1)

1-1: LGTM!

Standard barrel export pattern for consolidating event type exports.

frontend/src/lib/admin/sagas/index.ts (1)

1-1: LGTM!

Standard barrel export pattern for consolidating saga state exports.

frontend/src/lib/admin/users/index.ts (1)

1-1: LGTM!

Clean barrel export that properly exposes the rate limits utilities from the users module.

frontend/src/components/admin/sagas/SagaStatsCards.svelte (1)

16-36: LGTM!

Clean responsive grid layout with proper theme-aware styling. The dynamic icon rendering pattern is idiomatic Svelte 5.

frontend/src/components/admin/sagas/index.ts (1)

1-4: LGTM!

Clean barrel export providing a unified entry point for saga admin components.

frontend/src/components/admin/events/EventFilters.svelte (1)

27-157: LGTM!

Well-structured filter form with proper accessibility (label associations), responsive grid layout, and theme-aware styling. The $bindable pattern correctly enables two-way binding with the parent component.

frontend/src/components/admin/index.ts (1)

1-6: LGTM!

Clean barrel export for shared admin components with a helpful comment indicating the module's purpose.

frontend/src/components/admin/events/EventStatsCards.svelte (1)

12-46: LGTM!

Clean stats card grid with proper null safety and theme-aware styling. The conditional color for error rate provides good visual feedback.

One minor note: inside the {#if stats} block, the optional chaining (stats?.) is technically redundant since stats is guaranteed to be truthy, but it's harmless and adds defensive safety.

frontend/src/components/admin/users/index.ts (1)

1-5: LGTM!

Clean barrel export providing a complete set of user management admin components.

frontend/src/lib/admin/index.ts (1)

1-9: LGTM!

Well-organized central barrel with clear separation between shared utilities and domain-specific exports. Verification confirms no export name collisions across the re-exported modules. The wildcard re-exports provide a clean single entry point for the admin module with no risk of naming conflicts.

frontend/src/lib/admin/sagas/__tests__/sagaStates.test.ts (1)

1-105: Excellent test coverage for saga state utilities.

The test suite thoroughly validates the saga state utilities with comprehensive coverage of:

  • State configuration completeness and structure
  • Label mappings and fallback behavior
  • Step definitions and compensation logic
  • Progress calculation including edge cases (empty, null/undefined, capping at 100%)

This provides good confidence in the reliability of the saga state management logic.

frontend/src/routes/admin/__tests__/AdminSagas.test.ts (1)

1-495: Comprehensive test suite with excellent coverage.

This test suite provides thorough validation of the AdminSagas component functionality including:

  • Loading states and data rendering
  • Filter interactions (state, search, execution ID)
  • Auto-refresh behavior with fake timers
  • Modal interactions and content display
  • Pagination controls
  • Edge cases and error scenarios

The use of helper functions (createMockSaga, renderWithSagas) and proper setup/teardown demonstrates good testing practices.

frontend/src/lib/admin/events/__tests__/eventTypes.test.ts (1)

1-246: Thorough test coverage for event type utilities.

The test suite comprehensively validates event type utilities with good coverage of:

  • Event type constants and categories
  • Color mapping logic including dark mode variants
  • Label formatting for different event type patterns
  • Filter creation, detection, counting, and summary generation
  • Edge cases for singular/plural handling and empty states
frontend/src/lib/admin/users/__tests__/rateLimits.test.ts (1)

1-190: Comprehensive test coverage for rate limit utilities.

The test suite provides excellent validation of rate limit utilities with thorough coverage of:

  • Group color mappings including dark mode variants
  • Endpoint pattern detection across various endpoint types
  • Default rate limit rule generation and structure validation
  • Multiplier application with edge cases (default, falsy, fractional values)
  • Empty rule creation

The edge case testing (lines 154-168) for multiplier handling is particularly valuable for ensuring robust behavior.

frontend/src/components/admin/StatsCard.svelte (1)

1-44: LGTM!

Clean and well-structured presentational component. The Props interface is properly typed, conditional rendering is handled correctly, and the responsive design with compact mode support is a nice touch.

frontend/src/components/admin/AutoRefreshControl.svelte (1)

1-88: LGTM!

Well-designed auto-refresh control component. The use of $bindable() for two-way binding with optional callback notifications is a clean pattern. The responsive layout and loading state handling are properly implemented.

frontend/src/components/admin/FilterPanel.svelte (1)

35-82: LGTM!

The toggle button interaction pattern with conditional styling and the optional action buttons (Clear All, Apply) provide good flexibility. The use of {@render children()} for content projection is correct Svelte 5 syntax.

frontend/src/components/admin/users/UserFormModal.svelte (1)

38-122: LGTM on overall form structure.

The modal form is well-organized with clear field groupings, appropriate autocomplete attributes for security, and proper loading state handling. The conditional UI for edit vs. create mode is cleanly implemented.

frontend/src/components/admin/events/EventsTable.svelte (1)

34-41: Good accessibility pattern for clickable rows.

The table rows correctly implement keyboard navigation with role="button", tabindex="0", aria-label, and onkeydown handler for Enter key. The stopPropagation() on action buttons prevents double navigation.

frontend/src/components/admin/ActionButtons.svelte (1)

30-74: LGTM!

Clean implementation of a reusable action buttons component with three variants. The use of {@const IconComponent = action.icon} for dynamic component rendering is correct Svelte 5 syntax, and the disabled state handling with both attribute and visual styling is thorough.

frontend/src/components/admin/users/RateLimitsModal.svelte (1)

1-57: LGTM! Well-structured modal component.

The component is cleanly organized with proper TypeScript interfaces, reactive derivations, and mutation handlers. The use of $bindable for config enables two-way binding from the parent while keeping the component self-contained.

frontend/src/components/admin/users/UserFilters.svelte (1)

1-112: LGTM! Clean and accessible filter component.

The component demonstrates good practices:

  • Proper label-input associations for accessibility
  • Responsive layout using Tailwind utilities
  • Two-way binding with $bindable for parent-child state sync
  • Collapsible advanced section with visual rotation indicator
frontend/src/components/admin/events/EventDetailsModal.svelte (1)

92-102: LGTM! Footer actions are well-implemented.

The snippet-based footer with conditional replay invocation and clear action handlers is clean.

frontend/src/lib/admin/events/eventTypes.ts (1)

84-122: LGTM! Filter utilities are well-implemented.

The hasActiveFilters, getActiveFilterCount, and getActiveFilterSummary functions have consistent logic and handle all filter fields appropriately. The time range consolidation in the summary is a nice UX touch.

frontend/src/components/admin/events/ReplayPreviewModal.svelte (1)

1-89: LGTM! Well-designed replay preview component.

The component has a clear structure with:

  • Proper null checks before actions
  • Good UX with dry-run indicator and warning about potential duplicates
  • Correct pluralization logic
  • Clear visual hierarchy for event details
frontend/src/lib/admin/sagas/sagaStates.ts (1)

14-51: LGTM! Comprehensive state configuration.

The SAGA_STATES map provides a clean, centralized configuration for all saga states with consistent structure. The icon assignments are semantically appropriate (e.g., XCircle for failed, Clock for timeout).

frontend/src/lib/admin/pagination.svelte.ts (1)

36-76: LGTM! Clean reactive pagination factory.

The factory pattern with Svelte 5 runes ($state, $derived) and explicit getters/setters provides a well-encapsulated pagination state. The reset() function and callback-based page handlers offer good flexibility for consumers.

frontend/src/routes/admin/AdminSagas.svelte (1)

1-210: Good refactor to modular component architecture.

The migration from a monolithic implementation to component-based UI (SagaStatsCards, SagaFilters, SagasTable, SagaDetailsModal) improves maintainability and testability. The lifecycle management with onMount/onDestroy and auto-refresh logic is correctly implemented.

frontend/src/routes/admin/AdminUsers.svelte (3)

19-26: Excellent componentization!

The refactor from inline modal implementations to modular components (UserFilters, UsersTable, UserFormModal, DeleteUserModal, RateLimitsModal) significantly improves maintainability and separation of concerns.


67-78: Well-structured derived state for filter tracking.

The hasFiltersActive derived value cleanly checks all filter conditions to determine whether any filters are applied, which is useful for UI feedback.


95-121: Comprehensive filter implementation.

The filterUsers function correctly handles all filter types including search, role, status, and advanced filters (bypass rate limit, custom limits, global multiplier).

frontend/src/components/admin/events/index.ts (1)

1-7: Clean barrel export pattern.

This centralized export file simplifies imports for consuming modules and provides a clear public API for the admin events components.

frontend/src/routes/admin/AdminEvents.svelte (4)

21-42: Well-structured imports for modular components.

The refactor introduces clean separation with dedicated components (EventStatsCards, EventFilters, EventsTable, and various modals) and helper functions from lib/admin/events.


92-110: Correct filter handling with date conversion.

The loadEvents function properly converts date strings to ISO format for the API call and uses skip/limit for pagination.


140-171: Well-implemented replay functionality.

The replay feature correctly handles both dry-run preview and actual replay modes, with proper status polling and progress tracking for scheduled replays.


256-260: Nice use of active filter count badge.

The filter button includes a visual badge showing the count of active filters using the getActiveFilterCount helper, providing clear feedback to users.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
frontend/src/routes/admin/AdminSagas.svelte (1)

45-74: Client-side filtering pagination inconsistency persists despite partial mitigation.

The pagination issue flagged in the previous review remains: totalItems reflects the server's pre-filter count (line 57), while sagas is reduced by client-side filtering (lines 61-72). Although the UI now shows a "filtered locally" message (lines 193-196), users still see pagination controls based on the server total rather than the filtered count. For example, if the server returns 50 sagas but filtering leaves only 3 visible, pagination shows "Page 1 of 5" rather than "Page 1 of 1."

The previous review recommended either:

  1. Moving executionIdFilter and searchQuery to server-side query parameters
  2. Updating totalItems after filtering (breaking server pagination)
  3. Documenting this as "local search within current page"

The current implementation partially adopts option 3 with the added message, but the pagination controls remain inconsistent.

🧹 Nitpick comments (3)
frontend/src/routes/admin/AdminSagas.svelte (3)

85-94: Consider pagination for execution-specific saga loading.

When loading sagas for a specific execution, no limit or offset parameters are applied. If an execution generates many sagas, this could load a large result set into memory and impact performance. Consider applying the same pagination approach used in loadSagas or documenting the expected maximum saga count per execution.

🔎 Suggested approach: Apply pagination to execution saga queries
 async function loadExecutionSagas(executionId: string): Promise<void> {
     loading = true;
     const data = unwrapOr(await getExecutionSagasApiV1SagasExecutionExecutionIdGet({
-        path: { execution_id: executionId }
+        path: { execution_id: executionId },
+        query: {
+            limit: pageSize,
+            offset: (currentPage - 1) * pageSize
+        }
     }), null);
     loading = false;
     sagas = data?.sagas || [];
     totalItems = data?.total || 0;
     executionIdFilter = executionId;
 }

136-141: Consider clarifying the auto-refresh effect condition.

The condition if (autoRefresh || refreshRate) triggers the effect when either value changes, but setupAutoRefresh only acts when autoRefresh is true. While functionally safe, this can be confusing—changing refreshRate while autoRefresh is false will trigger the effect unnecessarily.

Consider making the dependency explicit:

🔎 Suggested clarification
 // Re-setup auto-refresh when settings change
 $effect(() => {
-    if (autoRefresh || refreshRate) {
-        setupAutoRefresh();
-    }
+    autoRefresh; // track dependency
+    refreshRate; // track dependency
+    setupAutoRefresh();
 });

Or alternatively, track only when autoRefresh is enabled:

 $effect(() => {
-    if (autoRefresh || refreshRate) {
+    if (autoRefresh) {
+        refreshRate; // track rate changes too
         setupAutoRefresh();
     }
 });

61-62: Case-sensitivity inconsistency between filter types.

The executionIdFilter uses case-sensitive matching (.includes(executionIdFilter)), while searchQuery applies case-insensitive matching to the same execution_id field (line 69: .toLowerCase().includes(q)). This inconsistency may confuse users when the same execution ID matches differently depending on which filter is used.

Consider normalizing both to case-insensitive matching for consistent behavior:

🔎 Suggested consistency fix
 // Client-side filtering for execution ID and search (filters within current page only)
 if (executionIdFilter) {
-    result = result.filter(s => s.execution_id.includes(executionIdFilter));
+    result = result.filter(s => 
+        s.execution_id.toLowerCase().includes(executionIdFilter.toLowerCase())
+    );
 }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f64db08 and 438e409.

📒 Files selected for processing (11)
  • frontend/src/components/admin/events/EventFilters.svelte
  • frontend/src/components/admin/users/DeleteUserModal.svelte
  • frontend/src/lib/admin/events/__tests__/eventTypes.test.ts
  • frontend/src/lib/admin/users/__tests__/rateLimits.test.ts
  • frontend/src/lib/admin/users/rateLimits.ts
  • frontend/src/routes/admin/AdminSagas.svelte
  • frontend/src/routes/admin/AdminUsers.svelte
  • frontend/src/routes/admin/__tests__/AdminEvents.test.ts
  • frontend/src/routes/admin/__tests__/AdminSagas.test.ts
  • frontend/src/routes/admin/__tests__/AdminUsers.test.ts
  • frontend/src/routes/admin/__tests__/test-utils.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/src/components/admin/events/EventFilters.svelte
🧰 Additional context used
🧬 Code graph analysis (5)
frontend/src/routes/admin/__tests__/AdminEvents.test.ts (2)
frontend/src/lib/admin/events/eventTypes.ts (1)
  • EVENT_TYPES (6-17)
frontend/src/routes/admin/__tests__/test-utils.ts (2)
  • mockElementAnimate (34-36)
  • mockWindowGlobals (41-44)
frontend/src/routes/admin/__tests__/AdminSagas.test.ts (2)
frontend/src/lib/admin/sagas/sagaStates.ts (1)
  • SAGA_STATES (14-51)
frontend/src/routes/admin/__tests__/test-utils.ts (1)
  • mockElementAnimate (34-36)
frontend/src/lib/admin/events/__tests__/eventTypes.test.ts (1)
frontend/src/lib/admin/events/eventTypes.ts (7)
  • EventFilters (60-69)
  • EVENT_TYPES (6-17)
  • getEventTypeColor (22-42)
  • getEventTypeLabel (45-57)
  • hasActiveFilters (84-95)
  • getActiveFilterCount (97-108)
  • getActiveFilterSummary (110-122)
frontend/src/routes/admin/__tests__/AdminUsers.test.ts (1)
frontend/src/routes/admin/__tests__/test-utils.ts (1)
  • mockElementAnimate (34-36)
frontend/src/lib/admin/users/__tests__/rateLimits.test.ts (1)
frontend/src/lib/admin/users/rateLimits.ts (7)
  • getDefaultRules (44-53)
  • GROUP_COLORS (7-15)
  • getGroupColor (17-19)
  • ENDPOINT_GROUP_PATTERNS (22-29)
  • detectGroupFromEndpoint (31-37)
  • getDefaultRulesWithMultiplier (55-63)
  • createEmptyRule (66-77)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Scan Frontend
  • GitHub Check: E2E Tests
  • GitHub Check: Integration Tests
🔇 Additional comments (16)
frontend/src/lib/admin/events/__tests__/eventTypes.test.ts (8)

1-14: LGTM! Clean test setup.

The imports are comprehensive, and the withFilter helper function provides a concise way to create test fixtures with partial overrides. This improves test readability throughout the suite.


16-26: LGTM! Comprehensive constant validation.

The test ensures all expected event types are present in the EVENT_TYPES constant. Using forEach with individual assertions provides complete feedback if multiple events are missing.


28-46: LGTM! Excellent parameterized color testing.

The test suite comprehensively validates all color mappings, including the unknown event fallback. Checking for both the color class and dark mode variant ensures proper styling across themes.


48-58: LGTM! Label generation well tested.

The tests cover the special case for execution.requested (empty label for icon-only display), standard two-part events, and edge cases like single words and deeply nested event names.


60-77: LGTM! Default filter creation properly tested.

The tests validate both the structure of default filters and that each call returns a new object instance. The identity check on Line 75 is important to ensure callers can safely mutate their filter objects without affecting other consumers.


79-96: LGTM! Comprehensive active filter detection.

The test suite validates detection of active filters across all 8 fields, plus the empty case. The parameterized approach ensures consistent coverage.


98-110: LGTM! Filter count calculation validated.

The tests cover the key scenarios: no filters, multiple filters, and all filters active. This provides sufficient confidence in the counting logic without exhaustive permutation testing.


112-136: LGTM! Excellent summary generation testing.

The test suite is thorough, covering empty filters, individual labels with proper singular/plural handling for event types (lines 118-119), time range coalescing for start/end times (lines 122-123), and comprehensive multi-filter scenarios. This ensures the summary UI will display correctly across all use cases.

frontend/src/routes/admin/__tests__/AdminSagas.test.ts (1)

1-476: LGTM! Comprehensive test coverage for AdminSagas.

The test file is well-structured with proper mock setup, test isolation, and comprehensive coverage across loading, rendering, filters, pagination, and modal interactions. The use of hoisted mocks, fake timers, and proper cleanup demonstrates good testing practices.

frontend/src/components/admin/users/DeleteUserModal.svelte (1)

1-69: LGTM! Safe default for cascade delete.

The component now correctly defaults cascadeDelete to false, requiring explicit user opt-in for the destructive cascade operation. The conditional warning and proper loading states enhance the user experience.

frontend/src/routes/admin/__tests__/AdminEvents.test.ts (1)

1-892: LGTM! Good refactor to centralized test utilities.

The migration to use mockElementAnimate() and mockWindowGlobals() from test-utils improves maintainability by eliminating duplicate mock setup code. The cleanup with vi.unstubAllGlobals() ensures proper test isolation.

frontend/src/routes/admin/__tests__/test-utils.ts (1)

1-44: LGTM! Well-structured test utilities.

The centralized test utilities provide proper mocks for DOM animations and window globals. The animateMock comprehensively covers the Web Animations API, and the use of vi.stubGlobal() is the correct approach for mocking window methods.

frontend/src/routes/admin/__tests__/AdminUsers.test.ts (1)

394-500: LGTM! Tests verify safe cascade delete behavior.

The new tests properly validate that cascade delete defaults to false (safe default) and that the warning message appears only when cascade is explicitly enabled. This ensures the dangerous operation requires user opt-in.

frontend/src/lib/admin/users/__tests__/rateLimits.test.ts (1)

1-131: LGTM! Comprehensive test coverage for rate limits.

The test suite thoroughly validates the rate limit utilities including group detection, default rules, multiplier calculations, and edge cases. The tests for non-positive multipliers (lines 104-109) correctly verify that 0 and negative values default to 1.0, matching the implementation's intent.

frontend/src/lib/admin/users/rateLimits.ts (1)

55-63: LGTM! Intentional handling of non-positive multipliers.

The implementation explicitly treats 0 and negative multipliers as invalid (line 58: multiplier > 0 ? multiplier : 1.0), which is a reasonable design choice. The inline comment (line 57) clearly documents this behavior, and the test suite verifies it. This prevents edge cases like 0 effective requests that would block all access.

frontend/src/routes/admin/AdminUsers.svelte (1)

1-325: LGTM! Clean refactor to modular components.

The component has been successfully refactored to use dedicated modal components (DeleteUserModal, RateLimitsModal, UserFormModal) and specialized UI components (UserFilters, UsersTable). This improves maintainability and separation of concerns. The cascade delete integration (lines 158-170, 291-299) correctly manages state and passes it to the modal component.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
frontend/src/lib/api-interceptors.ts (1)

50-52: Same unsafe array access in formatValidationErrors.

This function has the same vulnerability as getErrorMessage. Apply the same defensive fix here.

🔎 Suggested defensive fix
 function formatValidationErrors(detail: ValidationError[]): string {
-    return detail.map((e) => `${e.loc[e.loc.length - 1]}: ${e.msg}`).join('\n');
+    return detail.map((e) => {
+        const field = Array.isArray(e.loc) && e.loc.length ? e.loc[e.loc.length - 1] : 'field';
+        return `${field}: ${e.msg}`;
+    }).join('\n');
 }
frontend/src/routes/admin/AdminUsers.svelte (1)

136-156: Unhandled error from unwrap in saveUser.

At line 153, unwrap(result) is called after setting savingUser = false. If the API returns an error, unwrap will throw, but there's no try-catch block. This will cause an unhandled exception.

🔎 Proposed fix
     async function saveUser(): Promise<void> {
         if (!userForm.username) { addToast('Username is required', 'error'); return; }
         if (!editingUser && !userForm.password) { addToast('Password is required', 'error'); return; }
         savingUser = true;
-        let result;
-        if (editingUser) {
-            const updateData: Record<string, string | boolean | null> = {
-                username: userForm.username, email: userForm.email || null, role: userForm.role, is_active: userForm.is_active
-            };
-            if (userForm.password) updateData.password = userForm.password;
-            result = await updateUserApiV1AdminUsersUserIdPut({ path: { user_id: editingUser.user_id }, body: updateData });
-        } else {
-            result = await createUserApiV1AdminUsersPost({
-                body: { username: userForm.username, email: userForm.email || null, password: userForm.password, role: userForm.role, is_active: userForm.is_active }
-            });
+        try {
+            let result;
+            if (editingUser) {
+                const updateData: Record<string, string | boolean | null> = {
+                    username: userForm.username, email: userForm.email || null, role: userForm.role, is_active: userForm.is_active
+                };
+                if (userForm.password) updateData.password = userForm.password;
+                result = await updateUserApiV1AdminUsersUserIdPut({ path: { user_id: editingUser.user_id }, body: updateData });
+            } else {
+                result = await createUserApiV1AdminUsersPost({
+                    body: { username: userForm.username, email: userForm.email || null, password: userForm.password, role: userForm.role, is_active: userForm.is_active }
+                });
+            }
+            unwrap(result);
+            showUserModal = false;
+            await loadUsers();
+            addToast(editingUser ? 'User updated successfully.' : 'User created successfully.', 'success');
+        } catch (err) {
+            addToast('Failed to save user. Please try again.', 'error');
+        } finally {
+            savingUser = false;
         }
-        savingUser = false;
-        unwrap(result);
-        showUserModal = false;
-        await loadUsers();
     }
frontend/src/routes/admin/AdminEvents.svelte (1)

92-110: Potential runtime error on invalid date input.

If filters.start_time or filters.end_time contains an invalid date string, new Date(invalidString).toISOString() will throw a RangeError: Invalid time value. Consider adding validation or wrapping in try-catch.

🔎 Suggested defensive fix
             body: {
                 filters: {
                     ...filters,
-                    start_time: filters.start_time ? new Date(filters.start_time).toISOString() : null,
-                    end_time: filters.end_time ? new Date(filters.end_time).toISOString() : null
+                    start_time: filters.start_time && !isNaN(Date.parse(filters.start_time)) 
+                        ? new Date(filters.start_time).toISOString() 
+                        : null,
+                    end_time: filters.end_time && !isNaN(Date.parse(filters.end_time)) 
+                        ? new Date(filters.end_time).toISOString() 
+                        : null
                 },
♻️ Duplicate comments (9)
frontend/src/components/__tests__/Header.test.ts (1)

118-129: Verify that Header.svelte now includes the icon-specific CSS classes.

The test expects SVG elements to have icon-specific classes (lucide-sun, lucide-moon, lucide-monitor-cog), but a previous review flagged that Header.svelte only passes class="w-5 h-5" to Lucide icon components without these icon-specific classes. Since @lucide/svelte components don't automatically add type-based classes, they must be explicitly passed as props (e.g., <Sun class="w-5 h-5 lucide-sun" />).

Confirm that Header.svelte has been updated to include these classes in the icon component props; otherwise, this test will fail.

Run the following script to verify the current Header.svelte implementation:

#!/bin/bash
# Description: Check if Header.svelte includes icon-specific classes on Lucide icon components

# Search for Lucide icon component usage in Header.svelte around the theme toggle button
rg -n -A2 -B2 'lucide-(sun|moon|monitor-cog)' frontend/src/components/Header.svelte

# If no matches, check how the theme toggle icons are currently implemented
echo "---"
echo "Current icon component usage in theme toggle:"
rg -n -A5 -B5 'Sun|Moon.*class=' frontend/src/components/Header.svelte || echo "No matches found with icon-specific classes"
frontend/src/lib/api-interceptors.ts (1)

37-39: Unresolved: Potential runtime error if loc array is empty or undefined.

The previous review identified that accessing e.loc[e.loc.length - 1] without validation can throw if the API returns a malformed ValidationError with an empty or missing loc array. This issue remains unaddressed.

🔎 Suggested defensive fix
     if (Array.isArray(detail) && detail.length > 0) {
-        return detail.map((e) => `${e.loc[e.loc.length - 1]}: ${e.msg}`).join(', ');
+        return detail.map((e) => {
+            const field = Array.isArray(e.loc) && e.loc.length ? e.loc[e.loc.length - 1] : 'field';
+            return `${field}: ${e.msg}`;
+        }).join(', ');
     }
frontend/src/lib/admin/users/rateLimits.ts (1)

55-63: Multiplier fallback behavior differs from past review suggestion.

The past review suggested using nullish coalescing (??) to allow multiplier=0 as a valid input. The current implementation explicitly rejects 0 and negative values, falling back to 1.0.

If disabling rate limits via multiplier=0 is a valid use case (e.g., for testing or special accounts), the current logic would prevent this. If the intent is that 0 should never be valid, please document this constraint.

frontend/src/components/NotificationCenter.svelte (1)

204-214: Permission handling looks correct, but tests are still needed.

The SSR-safe permission initialization and requestNotificationPermission function are implemented correctly. The existing past review comment about missing test coverage for this flow remains valid.

frontend/src/components/__tests__/NotificationCenter.test.ts (1)

51-51: Loading store mock is exposed but loading UI test is still missing.

The mockLoading derived store is correctly implemented. However, the past review comment about adding a test to verify the loading spinner displays when $loading is true remains unaddressed.

frontend/src/lib/user-settings.ts (2)

38-43: Type mismatch persists: API response may contain nulls.

The unwrap() returns UserSettings from the API, but updateUserSettingsApiV1UserSettingsPut actually returns a response where fields can be null (matching UserSettingsUpdate). When passing directly to setUserSettings(data), null values may violate the store's type contract.

This was flagged in a past review. Consider filtering out null values before updating the store.


22-26: Same potential issue in loadUserSettings.

The API response from getUserSettingsApiV1UserSettingsGet is passed directly to setUserSettings(data). If the API returns null fields, this could cause type inconsistencies in the store.

frontend/src/routes/admin/AdminSagas.svelte (1)

60-73: Client-side filtering still causes pagination inconsistency.

The past review comment remains partially valid: while UI messaging was added, the core issue persists. When client-side filters are active, totalPages is still calculated from server's totalItems, but the displayed items are filtered locally. Navigating to page 2 could show 0 results if all matching items were on page 1.

Consider documenting this as intentional "search within current page" behavior or moving filters server-side for accurate pagination.

frontend/src/routes/Editor.svelte (1)

189-206: Silent failure persists for non-404 update errors.

When updateSavedScriptApiV1ScriptsScriptIdPut returns an error that's not a 404, the function returns early at line 198 without user feedback. This was flagged in a past review.

🔎 Proposed fix
     if (id) {
         const { error, response } = await updateSavedScriptApiV1ScriptsScriptIdPut({ path: { script_id: id }, body });
         if (error) {
             if (response?.status === 404) {
                 currentScriptId.set(null);
                 const data = unwrap(await createSavedScriptApiV1ScriptsPost({ body }));
                 currentScriptId.set(data.id);
                 addToast('Script saved successfully.', 'success');
+            } else {
+                addToast('Failed to update script. Please try again.', 'error');
             }
             return;
         }
🧹 Nitpick comments (9)
frontend/src/lib/admin/events/__tests__/eventTypes.test.ts (1)

42-44: Consider exact color string validation for stricter regression detection.

The current substring checks (.toContain()) are reasonable and flexible, but exact string assertions would catch mismatches between light and dark color variants (e.g., if someone accidentally used text-green-600 dark:text-red-400).

💡 Example: exact string matching
-const color = getEventTypeColor(eventType);
-expect(color).toContain(expectedColor);
-expect(color).toContain('dark:');
+expect(getEventTypeColor(eventType)).toBe('text-green-600 dark:text-green-400');

This would require updating the test data structure to include full expected strings rather than just the color prefix, but it provides stronger guarantees.

frontend/src/routes/admin/__tests__/AdminEvents.test.ts (1)

27-30: Consider using the canonical EVENT_TYPES from the source module.

The local EVENT_TYPES array contains only 6 event types, while the source module (frontend/src/lib/admin/events/eventTypes.ts) defines 11 event types. While this may be intentional for test brevity, using the canonical list ensures tests remain in sync with production code.

🔎 Proposed fix
-const EVENT_TYPES = [
-  'execution.requested', 'execution.started', 'execution.completed',
-  'execution.failed', 'pod.created', 'pod.running'
-];
+import { EVENT_TYPES } from '$lib/admin/events/eventTypes';
frontend/src/components/admin/events/EventDetailsModal.svelte (1)

92-102: Consider simplifying the footer snippet.

The footer snippet pattern is good, but the guard on line 94 (event && onReplay(...)) could benefit from a brief comment explaining why it's needed despite the parent {#if event} block—the snippet may execute independently of that conditional.

frontend/src/routes/Settings.svelte (1)

81-87: Add type annotation to event handler parameter.

The event parameter in handleClickOutside lacks a type annotation, which could cause TypeScript warnings depending on compiler strictness.

🔎 Proposed fix
-        const handleClickOutside = (event) => {
+        const handleClickOutside = (event: MouseEvent) => {
             const target = event.target;
frontend/src/components/editor/LanguageSelect.svelte (1)

110-113: Minor: Mouse leave may interfere with keyboard navigation.

When a user navigates with the keyboard and then moves the mouse out of the menu, onmouseleave resets hoveredLang and focusedVersionIndex, potentially disrupting keyboard-focused state. Consider preserving keyboard focus state unless a new mouse interaction occurs.

🔎 Possible approach

Track whether the last interaction was keyboard or mouse, and only reset on mouseleave if the last interaction was mouse-based:

let lastInteractionType = $state<'mouse' | 'keyboard'>('mouse');

Then conditionally reset in onmouseleave only when lastInteractionType === 'mouse'.

frontend/src/components/__tests__/NotificationCenter.test.ts (1)

362-370: Icon class assertion may fail for missing icons.

The selector container.querySelector(\[aria-label*="${subject}"] svg`)may return null if the notification button isn't found, causingsvg?.classList.contains(iconClass)to evaluateundefined?.contains()which returnsundefined, not false`.

🔎 Consider adding an explicit existence check
      await waitFor(() => {
        const svg = container.querySelector(`[aria-label*="${subject}"] svg`);
+       expect(svg).toBeInTheDocument();
        expect(svg?.classList.contains(iconClass)).toBe(true);
      });
frontend/src/lib/editor/execution.svelte.ts (2)

35-82: EventSource is not cleaned up if the component unmounts during execution.

The EventSource created inside the Promise has no external cleanup mechanism. If the calling component unmounts while execution is in progress, the stream will remain open and the state machine may attempt to update destroyed state.

Consider returning an abort mechanism from execute() or storing the EventSource reference for cleanup.


93-96: Consider adding a comment to clarify the empty catch block.

The empty catch on line 96 is intentional (to preserve the original error), but this intent isn't obvious. A brief comment would improve clarity.

🔎 Proposed improvement
                    if (data) {
                        result = data;
                        error = null;
                    }
-               } catch { /* keep error */ }
+               } catch {
+                   // Ignore - preserve the original error message
+               }
frontend/src/routes/admin/AdminEvents.svelte (1)

402-428: Consider simplifying redundant modal open props.

The open prop values are redundant with their surrounding {#if} conditions. For example, on line 405 open={!!selectedEvent} is always true inside {#if selectedEvent}. This works but adds unnecessary complexity.

🔎 Simplified modal declarations
 {#if selectedEvent}
     <EventDetailsModal
         event={selectedEvent}
-        open={!!selectedEvent}
+        open={true}
         onClose={() => selectedEvent = null}
         onReplay={handleReplayFromModal}
         onViewRelated={loadEventDetail}
     />
 {/if}

 {#if showReplayPreview && replayPreview}
     <ReplayPreviewModal
         preview={replayPreview}
-        open={showReplayPreview && !!replayPreview}
+        open={true}
         onClose={() => { showReplayPreview = false; replayPreview = null; }}
         onConfirm={handleReplayConfirm}
     />
 {/if}

Alternatively, if the modal components handle their own visibility via the open prop, you could remove the {#if} wrappers entirely and rely solely on open.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 438e409 and d223c36.

⛔ Files ignored due to path filters (1)
  • frontend/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (84)
  • frontend/package.json
  • frontend/rollup.config.js
  • frontend/src/App.svelte
  • frontend/src/components/Header.svelte
  • frontend/src/components/NotificationCenter.svelte
  • frontend/src/components/ProtectedRoute.svelte
  • frontend/src/components/ToastContainer.svelte
  • frontend/src/components/__tests__/ErrorDisplay.test.ts
  • frontend/src/components/__tests__/Footer.test.ts
  • frontend/src/components/__tests__/Header.test.ts
  • frontend/src/components/__tests__/NotificationCenter.test.ts
  • frontend/src/components/__tests__/ProtectedRoute.test.ts
  • frontend/src/components/__tests__/Spinner.test.ts
  • frontend/src/components/__tests__/ToastContainer.test.ts
  • frontend/src/components/admin/AutoRefreshControl.svelte
  • frontend/src/components/admin/events/EventDetailsModal.svelte
  • frontend/src/components/admin/events/EventFilters.svelte
  • frontend/src/components/admin/events/EventStatsCards.svelte
  • frontend/src/components/admin/events/EventsTable.svelte
  • frontend/src/components/admin/events/ReplayPreviewModal.svelte
  • frontend/src/components/admin/events/ReplayProgressBanner.svelte
  • frontend/src/components/admin/events/UserOverviewModal.svelte
  • frontend/src/components/admin/events/index.ts
  • frontend/src/components/admin/index.ts
  • frontend/src/components/admin/sagas/SagaDetailsModal.svelte
  • frontend/src/components/admin/sagas/SagaFilters.svelte
  • frontend/src/components/admin/sagas/SagaStatsCards.svelte
  • frontend/src/components/admin/sagas/SagasTable.svelte
  • frontend/src/components/admin/sagas/index.ts
  • frontend/src/components/admin/users/DeleteUserModal.svelte
  • frontend/src/components/admin/users/RateLimitsModal.svelte
  • frontend/src/components/admin/users/UserFormModal.svelte
  • frontend/src/components/admin/users/UsersTable.svelte
  • frontend/src/components/admin/users/index.ts
  • frontend/src/components/editor/CodeMirrorEditor.svelte
  • frontend/src/components/editor/LanguageSelect.svelte
  • frontend/src/components/editor/OutputPanel.svelte
  • frontend/src/components/editor/ResourceLimits.svelte
  • frontend/src/components/editor/index.ts
  • frontend/src/lib/__mocks__/api-interceptors.ts
  • frontend/src/lib/__tests__/auth-init.test.ts
  • frontend/src/lib/__tests__/user-settings.test.ts
  • frontend/src/lib/admin/__tests__/constants.test.ts
  • frontend/src/lib/admin/events/__tests__/eventTypes.test.ts
  • frontend/src/lib/admin/events/index.ts
  • frontend/src/lib/admin/index.ts
  • frontend/src/lib/admin/sagas/__tests__/sagaStates.test.ts
  • frontend/src/lib/admin/sagas/index.ts
  • frontend/src/lib/admin/sagas/sagaStates.ts
  • frontend/src/lib/admin/users/__tests__/rateLimits.test.ts
  • frontend/src/lib/admin/users/index.ts
  • frontend/src/lib/admin/users/rateLimits.ts
  • frontend/src/lib/api-interceptors.ts
  • frontend/src/lib/auth-init.ts
  • frontend/src/lib/editor/execution.svelte.ts
  • frontend/src/lib/editor/index.ts
  • frontend/src/lib/user-settings.ts
  • frontend/src/main.ts
  • frontend/src/routes/Editor.svelte
  • frontend/src/routes/Home.svelte
  • frontend/src/routes/Login.svelte
  • frontend/src/routes/Notifications.svelte
  • frontend/src/routes/Register.svelte
  • frontend/src/routes/Settings.svelte
  • frontend/src/routes/admin/AdminEvents.svelte
  • frontend/src/routes/admin/AdminLayout.svelte
  • frontend/src/routes/admin/AdminSagas.svelte
  • frontend/src/routes/admin/AdminSettings.svelte
  • frontend/src/routes/admin/AdminUsers.svelte
  • frontend/src/routes/admin/__tests__/AdminEvents.test.ts
  • frontend/src/routes/admin/__tests__/AdminSagas.test.ts
  • frontend/src/routes/admin/__tests__/AdminUsers.test.ts
  • frontend/src/stores/__tests__/auth.test.ts
  • frontend/src/stores/__tests__/errorStore.test.ts
  • frontend/src/stores/__tests__/notificationStore.test.ts
  • frontend/src/stores/__tests__/theme.test.ts
  • frontend/src/stores/__tests__/toastStore.test.ts
  • frontend/src/stores/auth.ts
  • frontend/src/stores/notificationStore.ts
  • frontend/src/stores/theme.ts
  • frontend/src/stores/userSettings.ts
  • frontend/src/utils/__tests__/meta.test.ts
  • frontend/tsconfig.json
  • frontend/vitest.config.ts
✅ Files skipped from review due to trivial changes (2)
  • frontend/src/components/tests/ProtectedRoute.test.ts
  • frontend/src/App.svelte
🚧 Files skipped from review as they are similar to previous changes (28)
  • frontend/src/components/admin/sagas/index.ts
  • frontend/src/lib/admin/index.ts
  • frontend/src/lib/admin/sagas/index.ts
  • frontend/src/stores/userSettings.ts
  • frontend/src/lib/admin/events/index.ts
  • frontend/src/components/admin/users/UsersTable.svelte
  • frontend/src/components/ToastContainer.svelte
  • frontend/src/components/admin/index.ts
  • frontend/src/components/editor/CodeMirrorEditor.svelte
  • frontend/src/components/admin/events/EventsTable.svelte
  • frontend/src/components/admin/users/UserFormModal.svelte
  • frontend/src/components/editor/index.ts
  • frontend/src/components/Header.svelte
  • frontend/src/routes/Home.svelte
  • frontend/rollup.config.js
  • frontend/src/components/admin/events/ReplayPreviewModal.svelte
  • frontend/src/lib/admin/sagas/sagaStates.ts
  • frontend/src/lib/editor/index.ts
  • frontend/src/lib/auth-init.ts
  • frontend/src/components/admin/events/index.ts
  • frontend/src/main.ts
  • frontend/src/components/admin/users/RateLimitsModal.svelte
  • frontend/src/stores/theme.ts
  • frontend/src/components/editor/ResourceLimits.svelte
  • frontend/src/stores/auth.ts
  • frontend/package.json
  • frontend/src/stores/notificationStore.ts
  • frontend/src/lib/admin/users/index.ts
🧰 Additional context used
🧬 Code graph analysis (13)
frontend/src/lib/api-interceptors.ts (4)
frontend/src/stores/toastStore.ts (2)
  • ToastType (3-3)
  • addToast (16-25)
frontend/src/lib/__mocks__/api-interceptors.ts (1)
  • getErrorMessage (7-7)
frontend/src/lib/auth-init.ts (1)
  • isAuthenticated (154-160)
frontend/src/stores/auth.ts (1)
  • isAuthenticated (38-38)
frontend/src/routes/admin/__tests__/AdminEvents.test.ts (2)
frontend/src/lib/admin/events/eventTypes.ts (1)
  • EVENT_TYPES (6-17)
frontend/src/routes/admin/__tests__/test-utils.ts (2)
  • mockElementAnimate (34-36)
  • mockWindowGlobals (41-44)
frontend/src/lib/admin/__tests__/constants.test.ts (1)
frontend/src/lib/admin/constants.ts (5)
  • STATUS_COLORS (6-12)
  • STATS_BG_COLORS (15-23)
  • STATS_TEXT_COLORS (26-34)
  • ROLE_COLORS (37-40)
  • ACTIVE_STATUS_COLORS (43-47)
frontend/src/routes/admin/__tests__/AdminUsers.test.ts (1)
frontend/src/routes/admin/__tests__/test-utils.ts (1)
  • mockElementAnimate (34-36)
frontend/src/lib/admin/sagas/__tests__/sagaStates.test.ts (1)
frontend/src/lib/admin/sagas/sagaStates.ts (4)
  • SAGA_STATES (14-51)
  • getSagaStateInfo (60-62)
  • EXECUTION_SAGA_STEPS (71-77)
  • getSagaProgressPercentage (79-83)
frontend/src/lib/admin/events/__tests__/eventTypes.test.ts (1)
frontend/src/lib/admin/events/eventTypes.ts (8)
  • EventFilters (60-69)
  • createDefaultEventFilters (71-82)
  • EVENT_TYPES (6-17)
  • getEventTypeColor (22-42)
  • getEventTypeLabel (45-57)
  • hasActiveFilters (84-95)
  • getActiveFilterCount (97-108)
  • getActiveFilterSummary (110-122)
frontend/src/lib/user-settings.ts (7)
frontend/src/stores/userSettings.ts (1)
  • setUserSettings (20-22)
frontend/src/stores/theme.ts (1)
  • setTheme (77-79)
frontend/src/lib/api/index.ts (2)
  • UserSettingsUpdate (4-4)
  • updateUserSettingsApiV1UserSettingsPut (3-3)
frontend/src/lib/api/types.gen.ts (1)
  • UserSettingsUpdate (2989-3011)
frontend/src/stores/auth.ts (1)
  • isAuthenticated (38-38)
frontend/src/lib/api-interceptors.ts (1)
  • unwrap (148-151)
frontend/src/lib/api/sdk.gen.ts (1)
  • updateUserSettingsApiV1UserSettingsPut (556-563)
frontend/src/stores/__tests__/notificationStore.test.ts (2)
frontend/src/components/__tests__/NotificationCenter.test.ts (1)
  • notificationStore (86-86)
frontend/src/stores/notificationStore.ts (1)
  • notificationStore (99-99)
frontend/src/stores/__tests__/theme.test.ts (2)
frontend/src/components/__tests__/Header.test.ts (2)
  • theme (38-38)
  • toggleTheme (39-39)
frontend/src/stores/theme.ts (2)
  • theme (39-48)
  • toggleTheme (68-75)
frontend/src/lib/__tests__/user-settings.test.ts (3)
frontend/src/stores/theme.ts (1)
  • theme (39-48)
frontend/src/lib/user-settings.ts (2)
  • loadUserSettings (13-32)
  • saveUserSettings (34-50)
frontend/src/stores/userSettings.ts (1)
  • editorSettings (15-18)
frontend/src/routes/admin/__tests__/AdminSagas.test.ts (2)
frontend/src/lib/admin/sagas/sagaStates.ts (1)
  • SAGA_STATES (14-51)
frontend/src/routes/admin/__tests__/test-utils.ts (1)
  • mockElementAnimate (34-36)
frontend/src/stores/__tests__/auth.test.ts (2)
frontend/src/lib/auth-init.ts (1)
  • isAuthenticated (154-160)
frontend/src/stores/auth.ts (5)
  • isAuthenticated (38-38)
  • username (39-39)
  • userRole (41-41)
  • login (59-88)
  • logout (100-109)
frontend/src/lib/admin/users/__tests__/rateLimits.test.ts (1)
frontend/src/lib/admin/users/rateLimits.ts (7)
  • getDefaultRules (44-53)
  • GROUP_COLORS (7-15)
  • getGroupColor (17-19)
  • ENDPOINT_GROUP_PATTERNS (22-29)
  • detectGroupFromEndpoint (31-37)
  • getDefaultRulesWithMultiplier (55-63)
  • createEmptyRule (66-77)
🪛 Gitleaks (8.30.0)
frontend/src/lib/admin/users/rateLimits.ts

[high] 13-13: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: E2E Tests
  • GitHub Check: Build Frontend
  • GitHub Check: Integration Tests
🔇 Additional comments (72)
frontend/src/stores/__tests__/toastStore.test.ts (1)

3-3: LGTM! Clean import alias refactor.

The migration from relative import path to the alias-based $stores/toastStore improves maintainability and aligns perfectly with the PR objectives. The test coverage remains comprehensive and unchanged.

frontend/src/components/__tests__/ErrorDisplay.test.ts (1)

4-4: LGTM! Clean alias migration.

The import path change from relative to alias-based resolution aligns with the PR's module resolution strategy. The test suite remains comprehensive with proper mocking, security checks, and user interaction testing.

frontend/src/components/__tests__/Header.test.ts (1)

47-47: LGTM: Alias import aligns with PR objectives.

The change from relative import to alias path ($components/) is consistent with the PR's goal of standardizing imports across the codebase.

frontend/src/components/ProtectedRoute.svelte (1)

4-6: LGTM! Import path aliases improve maintainability.

The migration from relative imports to path aliases ($stores/, $lib/, $components/) aligns with the PR objectives and improves code organization. No functional changes to the authentication flow.

frontend/src/lib/admin/events/__tests__/eventTypes.test.ts (1)

1-137: Excellent test coverage and structure!

This test suite provides comprehensive coverage of the eventTypes utilities with well-organized test cases. The use of parameterized tests (.each), the clean helper function withFilter, and coverage of edge cases (immutability check on line 74-76, pluralization on lines 118-119) demonstrate solid testing practices.

frontend/src/components/__tests__/Footer.test.ts (1)

3-3: LGTM! Clean migration to alias-based imports.

The import path update from relative to alias-based ($components/Footer.svelte) improves consistency across the codebase while preserving all test logic.

frontend/src/routes/admin/AdminLayout.svelte (2)

4-7: LGTM! Import paths migrated to aliases.

The migration from relative imports to alias-based paths ($stores/..., $components/...) improves code maintainability and aligns with the project-wide aliasing strategy.


9-9: LGTM! Icon componentization improves maintainability.

Replacing the inline SVG with the ShieldCheck component from @lucide/svelte reduces code duplication and makes icon usage more consistent across the application.

Also applies to: 82-82

frontend/src/utils/__tests__/meta.test.ts (1)

2-2: LGTM! Import path migrated to alias.

The update to use $utils/meta instead of a relative path improves consistency with the project's aliasing strategy.

frontend/src/routes/Register.svelte (1)

3-8: LGTM! Imports migrated to alias-based paths.

All import paths have been updated to use project aliases ($stores/..., $lib/..., $utils/..., $components/...), improving consistency and maintainability without altering any logic.

frontend/src/components/__tests__/ToastContainer.test.ts (1)

4-5: LGTM! Test imports migrated to aliases.

The imports now use $components/... and $stores/... aliases, aligning with the project-wide import strategy while preserving all test logic.

frontend/src/lib/admin/__tests__/constants.test.ts (1)

1-68: LGTM! Comprehensive test coverage for admin constants.

This new test file provides thorough validation of all admin color constants, including:

  • Status, background, and text color mappings
  • Dark mode variant verification
  • Role color lookups with appropriate handling of unknown roles
  • Active/inactive/disabled status colors

The test structure is clear and covers expected behavior well.

frontend/src/routes/admin/__tests__/AdminUsers.test.ts (5)

5-5: LGTM! Centralized test utilities improve maintainability.

The migration to alias-based imports and centralized mock utilities (mockElementAnimate, MockAdminLayout) reduces test setup duplication and improves consistency across admin test suites.

Also applies to: 89-93


7-43: LGTM! Centralized mock data factories improve test clarity.

The introduction of DEFAULT_USER, createMockUser, and createMockUsers provides a consistent foundation for test data creation, making tests more readable and maintainable.


394-414: LGTM! Important test coverage for default deletion behavior.

This new test ensures that user deletion defaults to cascade: false, which is the safer behavior. The explicit verification of the query parameter is valuable for preventing accidental data loss.


426-428: LGTM! Cascade deletion test now properly toggles the checkbox.

The updated test correctly simulates enabling cascade deletion via the checkbox before confirming, ensuring the UI flow matches the expected cascade: true API call.


481-500: LGTM! Comprehensive test for cascade warning visibility.

This test properly validates that:

  • The cascade warning is hidden by default (when cascade is disabled)
  • The warning appears when the cascade checkbox is enabled

This ensures users are appropriately warned about the destructive nature of cascade deletion.

frontend/src/lib/admin/users/__tests__/rateLimits.test.ts (1)

1-131: LGTM! Excellent test coverage for rate limit utilities.

This new test file provides comprehensive validation of the rate limit module:

  • GROUP_COLORS & getGroupColor: Verifies all expected groups with dark mode support and proper fallback behavior
  • detectGroupFromEndpoint: Extensive test cases covering all endpoint categories (execution, admin, auth, sse, websocket, public, api) with regex pattern handling
  • getDefaultRules: Validates rule structure and default values
  • getDefaultRulesWithMultiplier: Tests multiplier calculations including edge cases (non-positive multipliers default to 1.0)
  • createEmptyRule: Verifies default structure and object identity

The parameterized tests (using it.each) provide thorough coverage while maintaining readability.

frontend/src/components/admin/users/DeleteUserModal.svelte (1)

19-19: LGTM! Critical issue resolved.

The cascadeDelete prop now correctly defaults to false, requiring explicit opt-in for the destructive cascade operation. This addresses the critical safety concern from the previous review.

frontend/src/lib/api-interceptors.ts (2)

64-81: LGTM! Clean 401 handling with deduplication.

The handle401 function properly prevents repeated handling with the isHandling401 flag and provides clear user feedback via toast. The session expiry flow is well-structured.


83-114: LGTM! Consolidated error status handling.

The handleErrorStatus function effectively consolidates status-based error handling into a single, maintainable location. The logic covers network errors, auth errors, validation errors, and server errors appropriately.

frontend/src/stores/__tests__/errorStore.test.ts (1)

3-3: LGTM! Import path updated to use alias.

The migration from relative import to $stores/errorStore aligns with the project's path aliasing strategy.

frontend/src/stores/__tests__/notificationStore.test.ts (1)

46-458: LGTM! Import paths updated to use alias.

All dynamic imports have been migrated from relative paths to $stores/notificationStore, aligning with the project's path aliasing strategy. Test logic and assertions remain unchanged.

frontend/src/components/__tests__/Spinner.test.ts (1)

3-3: LGTM! Import path updated to use alias.

The migration to $components/Spinner.svelte aligns with the project's path aliasing strategy.

frontend/src/routes/Login.svelte (1)

3-9: LGTM! Import paths updated to use aliases.

All imports have been migrated to use path aliases ($stores, $components, $utils, $lib), aligning with the project's standardized import strategy. Component logic remains unchanged.

frontend/tsconfig.json (1)

19-30: LGTM! Comprehensive path alias configuration.

The expanded path mappings provide clean module resolution across the frontend codebase. This configuration properly supports both direct and wildcard imports for all major module categories.

frontend/src/components/admin/users/index.ts (1)

1-5: LGTM! Clean barrel export for admin user components.

This consolidates the public API surface for admin user UI components, making imports cleaner and more maintainable. The use of path aliases is consistent with the project's import strategy.

frontend/vitest.config.ts (1)

32-36: LGTM!

The new path aliases are correctly configured and follow the same pattern as the existing $lib alias. This enables consistent alias-based imports across tests.

frontend/src/routes/admin/AdminSettings.svelte (1)

7-10: LGTM!

The import paths have been correctly migrated from relative paths to alias-based paths ($lib, $stores, $routes, $components), consistent with the project-wide alias strategy.

frontend/src/stores/__tests__/theme.test.ts (2)

5-7: LGTM!

The mock is correctly renamed from saveThemeSetting to saveUserSettings, aligning with the unified user settings API change in the codebase.


42-42: LGTM!

The dynamic imports throughout the test file correctly use the $stores/theme alias path, consistent with the project-wide alias migration.

frontend/src/routes/admin/__tests__/AdminEvents.test.ts (2)

5-6: LGTM!

Good use of centralized test utilities. The mockElementAnimate and mockWindowGlobals helpers are correctly imported from the shared test-utils module, reducing code duplication across admin tests.


125-129: LGTM!

The mock AdminLayout and AdminEvents component imports correctly use alias-based paths, consistent with the project-wide migration.

frontend/src/components/admin/sagas/SagaStatsCards.svelte (2)

1-14: LGTM!

The component is well-structured with proper TypeScript typing and Svelte 5 runes usage. The getCount helper is appropriately scoped for this component's needs.


16-35: LGTM!

The template uses a clean responsive grid layout with proper dark mode support. The dynamic icon rendering via {@const IconComponent = info.icon} is an appropriate pattern for this use case.

frontend/src/components/admin/events/EventStatsCards.svelte (2)

1-10: LGTM!

Props are correctly typed, and the distinction between stats.total_events (24h events) and the totalEvents prop (total count) is appropriate for the UI's display of "X of Y total" pattern.


12-45: LGTM!

The component handles nullable data gracefully with optional chaining and appropriate fallbacks. The conditional styling for error rate (green for 0, red for > 0) provides good visual feedback.

frontend/src/lib/__mocks__/api-interceptors.ts (1)

7-7: LGTM!

The re-export path correctly uses the $lib alias, consistent with the project-wide migration. The mock correctly separates mocked side-effect functions from real pure function re-exports.

frontend/src/components/admin/events/EventFilters.svelte (2)

1-24: LGTM!

The component correctly uses Svelte 5's $bindable for two-way binding of the filters object, and the default values provide a clean initial state. The past review concern about an unused X import has been addressed.


26-156: LGTM!

The template is well-structured with:

  • Proper label-input associations via for/id attributes for accessibility
  • Responsive grid layouts for different screen sizes
  • Helpful title attribute on the multi-select explaining Ctrl/Cmd usage
  • Consistent styling classes throughout
frontend/src/lib/__tests__/auth-init.test.ts (2)

58-72: LGTM! Clean migration from localStorage to sessionStorage.

The mock implementation correctly handles getItem, setItem, and removeItem operations with proper type annotations. The migration aligns with the broader PR goal of moving auth persistence to sessionStorage.


108-108: Import path alias update looks correct.

The dynamic import path changed from '../auth-init' to '$lib/auth-init', which aligns with the PR's goal of using alias-based imports consistently across the codebase.

frontend/src/components/admin/events/EventDetailsModal.svelte (1)

1-17: Well-structured component with proper type safety.

The Props interface is clearly defined, and the component properly uses Svelte 5's $props() runes syntax. Imports use the alias paths consistently.

frontend/src/components/admin/sagas/SagaDetailsModal.svelte (2)

17-22: Good UX pattern for modal navigation.

The handleViewExecution function correctly closes the modal before invoking the callback, preventing potential visual glitches from having multiple modals open simultaneously.


88-136: Complex but well-structured execution steps visualization.

The step visualization logic handles multiple states (completed, compensated, current, failed) with appropriate visual feedback. The use of @const for derived values within the loop is a clean pattern.

One consideration: the hardcoded min-w-[600px] on line 90 might cause horizontal scrolling issues on smaller admin panels. The overflow-x-auto on the parent handles this gracefully.

frontend/src/routes/Settings.svelte (2)

8-16: Clean migration to centralized settings management.

The imports properly use alias paths and introduce setTheme and setUserSettings from the new centralized stores, replacing the previous scattered settings management approach.


103-106: Settings initialization and caching updated correctly.

The setUserSettings(settings) call properly updates the centralized store after loading from the API, and the fallback to 'auto' for theme is sensible.

frontend/src/stores/__tests__/auth.test.ts (2)

19-30: Consistent sessionStorage migration in auth store tests.

The mock setup mirrors the pattern established in auth-init.test.ts, ensuring consistent behavior across related test files.


62-80: Test description updated to reflect sessionStorage.

The test "restores auth state from sessionStorage" correctly uses sessionStorageData['authState'] and imports from the aliased path, maintaining consistency with the implementation changes.

frontend/src/lib/admin/users/rateLimits.ts (2)

7-15: Static analysis false positive - not an API key.

The Gitleaks warning on line 13 is a false positive. The string 'bg-neutral-100 text-neutral-800 dark:bg-neutral-700 dark:text-neutral-200' is a CSS class string for styling, not an API key. The api key in the object refers to an endpoint group name.


66-77: Factory function provides sensible defaults.

The createEmptyRule function returns a complete RateLimitRule with reasonable defaults, making it easy to create new rules in the UI.

frontend/src/components/editor/OutputPanel.svelte (2)

16-29: Proper XSS prevention with ANSI rendering.

The combination of escapeXML: true in the ANSI converter and DOMPurify sanitization with restricted tags/attributes provides good defense-in-depth against XSS when rendering terminal output.


50-56: Phase label handling addresses past review feedback.

The phaseLabel derived value now explicitly handles 'starting' (line 51). The 'idle' phase is correctly excluded by the conditional on line 64 (phase !== 'idle'), so phaseLabel is never evaluated for idle state. The fallback "Executing..." serves as a catch-all for any future phases.

frontend/src/components/editor/LanguageSelect.svelte (2)

35-85: Keyboard navigation implementation addresses accessibility feedback.

The component now fully supports keyboard navigation with:

  • Arrow keys for menu traversal
  • Enter/Space for selection
  • Escape to close menus progressively
  • Proper focus management with indices

This addresses the accessibility concerns from the past review.


103-157: Well-structured accessible menu implementation.

The two-tier menu uses proper ARIA roles (menu, menuitem, menuitemradio) with correct aria-expanded, aria-checked, and tabindex management. The fly transitions provide smooth UX.

frontend/src/components/NotificationCenter.svelte (1)

2-9: Clean migration to component-based icons and alias imports.

The refactoring from inline SVG to Lucide icon components improves maintainability. The type Component import from Svelte and the alias-based paths ($stores/, $lib/) are correctly applied.

frontend/src/components/__tests__/NotificationCenter.test.ts (1)

526-580: Good coverage for desktop notification permission flow.

The new tests comprehensively cover:

  • Button visibility based on permission state (default/granted/denied)
  • Request permission click behavior
  • Browser notification creation on SSE events

This addresses the past review comment about missing permission flow tests.

frontend/src/routes/admin/AdminUsers.svelte (1)

248-256: Good refactoring to component-based UI.

The extraction of filters to UserFilters component with proper two-way binding is clean. The hasFiltersActive derived state is a nice touch for UX.

frontend/src/routes/admin/AdminSagas.svelte (1)

193-196: Good UX improvement for client-side filtering clarity.

The messaging "Showing X of Y on this page (filtered locally)" clearly communicates to users that client-side filtering is active, addressing the past review concern about pagination inconsistency.

frontend/src/routes/Editor.svelte (3)

41-69: Persistent store implementation is now correct.

The refactored createPersistentStore properly wraps set and update to sync with localStorage on each mutation, rather than using a subscription. This addresses the past memory leak concern.


314-314: Reactive store access is now correct.

The template uses $script.trim() which creates a proper reactive subscription, addressing the past review about non-reactive get(script) usage.


97-100: Good cleanup pattern for subscriptions.

The unsubscribe functions are properly declared, assigned in onMount, and called in onDestroy. This prevents memory leaks from dangling subscriptions.

Also applies to: 156-161

frontend/src/lib/__tests__/user-settings.test.ts (2)

123-228: Comprehensive test coverage for saveUserSettings.

The test suite thoroughly covers:

  • Auth guard (returns false when not authenticated)
  • Partial settings saves (theme, editor, combined)
  • Store updates on success
  • Theme application when theme is included
  • Success/failure return values
  • Network error handling

This aligns well with the unified settings flow implementation.


54-65: Good test structure with module reset pattern.

Using vi.resetModules() and dynamic imports ensures each test gets fresh module state. The mock setup is clean and the assertions verify the expected behavior.

frontend/src/routes/admin/AdminEvents.svelte (8)

1-42: LGTM on imports refactoring.

The migration to alias-based imports ($lib, $stores, $components, $routes) improves maintainability and avoids fragile relative path navigation. The type aliasing (type EventFilters as EventFiltersType) cleanly resolves the naming conflict with the component.


44-76: Well-structured state management.

Good use of Svelte 5 runes with $state for reactive values and $derived for computed properties like totalPages and skip. The organization with section comments aids readability.


78-90: LGTM on lifecycle management.

Both intervals (refreshInterval and replayCheckInterval) are properly cleaned up in onDestroy, preventing memory leaks. The 30-second auto-refresh is a reasonable polling interval for admin dashboards.


120-138: LGTM on replay status polling.

Terminal states (completed, failed, cancelled) correctly stop the polling interval, and appropriate user feedback is provided via toasts.


140-171: LGTM on replay functionality.

Good null-safety with optional chaining on response?.events_preview and response?.session_id. The dry-run preview flow and the real replay with status polling are well-structured.


173-193: LGTM on delete and export handlers.

The delete function properly uses Promise.all for parallel data refresh. The export function correctly builds query parameters and uses window.open for file downloads, which is appropriate for triggering browser download dialogs.


195-204: LGTM on user overview loading.

Good UX pattern: the modal opens immediately with a loading state, then either displays data or closes on failure. The early return guard for empty userId prevents unnecessary API calls.


206-232: LGTM on filter and handler functions.

Clean separation of concerns with thin handler wrappers that provide appropriate interfaces for child components. The handleReplayFromModal correctly closes the modal before initiating the replay action.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
frontend/src/routes/admin/__tests__/AdminEvents.test.ts (1)

234-234: Fix incorrect event type format.

Line 234 uses 'execution.completed' (dot separator) while all other event types in this file and the EventType enum use underscore format ('execution_completed'). This inconsistency should be corrected.

🔎 Proposed fix
-      const events = [createMockEvent({ event_type: 'execution.completed', metadata: { user_id: 'user-1', service_name: 'test-service' } })];
+      const events = [createMockEvent({ event_type: 'execution_completed', metadata: { user_id: 'user-1', service_name: 'test-service' } })];
backend/app/services/notification_service.py (1)

1-1: Fix import sorting to resolve pipeline failure.

The Ruff linter flagged an import sorting issue. Run ruff --fix to automatically organize imports.

♻️ Duplicate comments (4)
frontend/src/components/editor/OutputPanel.svelte (2)

50-56: Add explicit handler for idle phase.

The phaseLabel derived value still lacks an explicit case for the idle phase. When phase === 'idle', it falls through to return 'Executing...', which is semantically incorrect since no execution is in progress.

🔎 Suggested fix
 const phaseLabel = $derived.by(() => {
+    if (phase === 'idle') return 'Ready';
     if (phase === 'starting') return 'Starting...';
     if (phase === 'queued') return 'Queued...';
     if (phase === 'scheduled') return 'Scheduled...';
     if (phase === 'running') return 'Running...';
     return 'Executing...';
 });

147-150: Fix unit label typo: m should be ms for milliseconds.

The CPU time values are in milliseconds, but the display shows m instead of ms.

🔎 Proposed fix
                                 <span class="text-fg-default dark:text-dark-fg-default font-medium">
                                     {result.resource_usage.cpu_time_jiffies === 0
-                                        ? `< ${(1000 / clkTck).toFixed(0)} m`
-                                        : `${cpuMs.toFixed(3)} m`}
+                                        ? `< ${(1000 / clkTck).toFixed(0)} ms`
+                                        : `${cpuMs.toFixed(3)} ms`}
                                 </span>
frontend/src/lib/editor/execution.svelte.ts (2)

82-85: Same issue: fetchFallback rejection in onerror leaves promise unsettled.

The onerror handler is now async and awaits fetchFallback, but if it throws, the rejection escapes unhandled and the wrapping promise never settles.

🔎 Proposed fix
-                eventSource.onerror = async () => {
-                    eventSource.close();
-                    await fetchFallback();
-                };
+                eventSource.onerror = () => {
+                    eventSource.close();
+                    fetchFallback().catch(reject);
+                };

73-80: fetchFallback rejection in onmessage may leave the promise unsettled.

While fetchFallback is now awaited (addressing the previous review), if it throws after closing the EventSource on lines 73-76, the rejection propagates to the async onmessage handler but isn't caught—leaving the outer Promise<ExecutionResult> pending indefinitely.

🔎 Proposed fix
                         if (FAILURE_EVENTS.includes(eventType as EventType)) {
                             eventSource.close();
-                            await fetchFallback();
+                            try {
+                                await fetchFallback();
+                            } catch (e) {
+                                reject(e);
+                            }
                         }
                     } catch (err) {
                         console.error('SSE parse error:', err);
                     }
🧹 Nitpick comments (2)
backend/app/dlq/models.py (1)

118-118: Consider updating DLQMessageFilter.event_type to EventType | None for consistency.

While other event_type fields in this file and related schemas have been migrated to EventType, this filter field remains str | None. If the filter is used to match against DLQMessage.event_type (now EventType), consider updating for type consistency.

🔎 Proposed fix
 @dataclass
 class DLQMessageFilter:
     """Filter criteria for querying DLQ messages."""

     status: DLQMessageStatus | None = None
     topic: str | None = None
-    event_type: str | None = None
+    event_type: EventType | None = None
backend/app/domain/events/event_models.py (1)

97-105: Consider: EventSummary.event_type remains str while Event.event_type is now EventType.

This asymmetry may be intentional for lightweight serialization, but worth confirming it aligns with the type consistency goals of the broader refactor.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d223c36 and 8a9ab47.

📒 Files selected for processing (34)
  • backend/app/api/routes/events.py
  • backend/app/api/routes/execution.py
  • backend/app/db/repositories/event_repository.py
  • backend/app/db/repositories/sse_repository.py
  • backend/app/dlq/models.py
  • backend/app/domain/events/event_models.py
  • backend/app/domain/sse/models.py
  • backend/app/domain/user/settings_models.py
  • backend/app/infrastructure/mappers/execution_api_mapper.py
  • backend/app/infrastructure/mappers/sse_mapper.py
  • backend/app/schemas_pydantic/dlq.py
  • backend/app/schemas_pydantic/execution.py
  • backend/app/schemas_pydantic/sse.py
  • backend/app/schemas_pydantic/user_settings.py
  • backend/app/services/event_service.py
  • backend/app/services/kafka_event_service.py
  • backend/app/services/notification_service.py
  • backend/app/services/sse/redis_bus.py
  • backend/app/services/sse/sse_service.py
  • backend/app/services/user_settings_service.py
  • backend/tests/unit/db/repositories/test_sse_repository.py
  • backend/tests/unit/infrastructure/mappers/test_execution_api_mapper.py
  • backend/tests/unit/services/sse/test_redis_bus.py
  • backend/tests/unit/services/sse/test_shutdown_manager.py
  • backend/tests/unit/services/sse/test_sse_service.py
  • backend/tests/unit/services/sse/test_sse_shutdown_manager.py
  • docs/reference/openapi.json
  • frontend/src/components/editor/OutputPanel.svelte
  • frontend/src/lib/admin/events/__tests__/eventTypes.test.ts
  • frontend/src/lib/admin/events/eventTypes.ts
  • frontend/src/lib/api/index.ts
  • frontend/src/lib/api/types.gen.ts
  • frontend/src/lib/editor/execution.svelte.ts
  • frontend/src/routes/admin/__tests__/AdminEvents.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • frontend/src/lib/admin/events/eventTypes.ts
  • frontend/src/lib/api/index.ts
🧰 Additional context used
🧬 Code graph analysis (20)
frontend/src/routes/admin/__tests__/AdminEvents.test.ts (2)
frontend/src/lib/admin/events/eventTypes.ts (1)
  • EVENT_TYPES (10-21)
frontend/src/routes/admin/__tests__/test-utils.ts (2)
  • mockElementAnimate (34-36)
  • mockWindowGlobals (41-44)
backend/app/domain/events/event_models.py (3)
frontend/src/lib/admin/events/eventTypes.ts (1)
  • EventType (7-7)
frontend/src/lib/api/types.gen.ts (1)
  • EventType (1028-1028)
backend/app/dlq/models.py (1)
  • event_type (93-95)
backend/app/services/user_settings_service.py (2)
backend/app/dlq/models.py (1)
  • event_type (93-95)
backend/tests/unit/services/idempotency/test_middleware.py (1)
  • event (32-36)
backend/app/schemas_pydantic/dlq.py (4)
frontend/src/lib/admin/events/eventTypes.ts (1)
  • EventType (7-7)
frontend/src/lib/api/index.ts (1)
  • EventType (4-4)
frontend/src/lib/api/types.gen.ts (1)
  • EventType (1028-1028)
backend/app/dlq/models.py (1)
  • event_type (93-95)
backend/app/domain/sse/models.py (2)
frontend/src/lib/api/index.ts (1)
  • ExecutionStatus (4-4)
frontend/src/lib/api/types.gen.ts (1)
  • ExecutionStatus (1213-1213)
frontend/src/lib/admin/events/__tests__/eventTypes.test.ts (1)
frontend/src/lib/admin/events/eventTypes.ts (8)
  • EventFilters (58-67)
  • createDefaultEventFilters (69-80)
  • EVENT_TYPES (10-21)
  • getEventTypeColor (24-44)
  • getEventTypeLabel (47-55)
  • hasActiveFilters (82-93)
  • getActiveFilterCount (95-106)
  • getActiveFilterSummary (108-120)
backend/tests/unit/services/sse/test_shutdown_manager.py (3)
frontend/src/lib/editor/execution.svelte.ts (1)
  • phase (108-108)
backend/app/services/sse/sse_shutdown_manager.py (1)
  • initiate_shutdown (119-146)
backend/tests/unit/services/sse/test_sse_service.py (1)
  • get_shutdown_status (88-95)
backend/tests/unit/infrastructure/mappers/test_execution_api_mapper.py (2)
frontend/src/lib/api/index.ts (1)
  • ExecutionErrorType (4-4)
frontend/src/lib/api/types.gen.ts (1)
  • ExecutionErrorType (1049-1049)
backend/tests/unit/services/sse/test_sse_service.py (2)
backend/app/schemas_pydantic/sse.py (2)
  • RedisNotificationMessage (102-112)
  • RedisSSEMessage (63-68)
backend/app/services/sse/redis_bus.py (1)
  • get (21-29)
backend/app/schemas_pydantic/sse.py (1)
backend/app/schemas_pydantic/execution.py (2)
  • ExecutionResult (97-110)
  • ResourceUsage (57-67)
backend/tests/unit/services/sse/test_sse_shutdown_manager.py (2)
backend/tests/unit/services/sse/test_sse_service.py (1)
  • get_shutdown_status (88-95)
frontend/src/lib/editor/execution.svelte.ts (1)
  • phase (108-108)
backend/app/services/sse/sse_service.py (2)
backend/app/schemas_pydantic/sse.py (4)
  • RedisNotificationMessage (102-112)
  • RedisSSEMessage (63-68)
  • SSEExecutionEventData (18-60)
  • SSENotificationEventData (75-99)
backend/app/services/sse/redis_bus.py (3)
  • open_subscription (62-66)
  • get (21-29)
  • open_notification_subscription (72-76)
backend/app/infrastructure/mappers/execution_api_mapper.py (1)
backend/app/domain/execution/models.py (1)
  • DomainExecution (13-26)
backend/app/services/kafka_event_service.py (4)
backend/app/dlq/models.py (1)
  • event_type (93-95)
frontend/src/lib/admin/events/eventTypes.ts (1)
  • EventType (7-7)
frontend/src/lib/api/types.gen.ts (1)
  • EventType (1028-1028)
backend/app/infrastructure/kafka/mappings.py (1)
  • get_event_class_for_type (75-144)
backend/app/infrastructure/mappers/sse_mapper.py (1)
backend/app/domain/execution/models.py (2)
  • DomainExecution (13-26)
  • ResourceUsageDomain (43-64)
backend/app/schemas_pydantic/user_settings.py (3)
backend/app/dlq/models.py (1)
  • event_type (93-95)
frontend/src/lib/admin/events/eventTypes.ts (1)
  • EventType (7-7)
frontend/src/lib/api/types.gen.ts (1)
  • EventType (1028-1028)
backend/tests/unit/services/sse/test_redis_bus.py (3)
backend/app/schemas_pydantic/sse.py (2)
  • RedisNotificationMessage (102-112)
  • RedisSSEMessage (63-68)
backend/app/services/sse/redis_bus.py (3)
  • SSERedisBus (38-76)
  • get (21-29)
  • publish_notification (68-70)
backend/tests/unit/services/sse/test_sse_service.py (1)
  • get (24-33)
backend/app/services/notification_service.py (2)
backend/app/schemas_pydantic/sse.py (1)
  • RedisNotificationMessage (102-112)
backend/app/services/sse/redis_bus.py (1)
  • publish_notification (68-70)
backend/app/domain/user/settings_models.py (2)
backend/app/dlq/models.py (1)
  • event_type (93-95)
frontend/src/lib/api/types.gen.ts (1)
  • EventType (1028-1028)
backend/app/services/event_service.py (4)
frontend/src/lib/api/index.ts (2)
  • SortOrder (4-4)
  • EventType (4-4)
frontend/src/lib/api/types.gen.ts (2)
  • SortOrder (2602-2602)
  • EventType (1028-1028)
frontend/src/lib/admin/events/eventTypes.ts (1)
  • EventType (7-7)
backend/app/domain/events/event_models.py (1)
  • Event (77-94)
🪛 GitHub Actions: Ruff Linting
backend/app/services/notification_service.py

[error] 1-1: Import block is un-sorted or un-formatted. Organize imports with 'ruff --fix'.

backend/app/schemas_pydantic/execution.py

[error] 1-1: Import block is un-sorted or un-formatted. Organize imports with 'ruff --fix'.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Scan Frontend
  • GitHub Check: E2E Tests
  • GitHub Check: Integration Tests
🔇 Additional comments (60)
backend/tests/unit/services/sse/test_sse_shutdown_manager.py (2)

34-34: LGTM: Improved type safety with attribute access.

The migration from dictionary-based access to attribute access on ShutdownStatus improves type safety and code clarity.


45-45: LGTM: Consistent attribute access for shutdown status.

The attribute-based access to .complete is consistent with the earlier change on line 34 and improves type safety.

frontend/src/routes/admin/__tests__/AdminEvents.test.ts (2)

5-6: AI summary inconsistency: Mock helpers not imported as claimed.

The AI summary states that DEFAULT_EVENT, EVENT_TYPES, createMockEvent, and createMockEvents are "imported test-utils helpers," but lines 17-42 show they're still defined locally in this file rather than imported from test-utils.


147-148: LGTM: Clean integration of test-utils functions.

The usage of mockElementAnimate() and mockWindowGlobals() from the centralized test-utils module is correct and demonstrates the benefits of modular test utilities.

backend/app/schemas_pydantic/dlq.py (1)

7-7: LGTM! Consistent enum typing for event_type fields.

The migration from str to EventType aligns with the domain model (DLQMessage.event_type property) and frontend type definitions. Pydantic 2.x will handle serialization correctly.

Also applies to: 24-24, 86-86

backend/app/dlq/models.py (1)

92-95: LGTM! Property now returns strongly-typed EventType.

The return type aligns with BaseEvent.event_type being an EventType enum.

backend/app/domain/sse/models.py (1)

6-7: LGTM! Status field now uses strongly-typed ExecutionStatus enum.

This aligns with the broader migration to enum-based typing across execution-related domain models and is consistent with the SSERepository construction.

Also applies to: 33-37

backend/tests/unit/db/repositories/test_sse_repository.py (2)

9-16: LGTM! Test correctly validates enum-based status.

The test inserts a document with string status "running" and correctly asserts that the repository returns ExecutionStatus.RUNNING, validating the conversion logic in SSERepository.get_execution_status.


25-41: LGTM! Streamlined test coverage for simplified repository.

The test_get_execution and test_get_execution_not_found tests adequately cover the new get_execution method behavior.

backend/tests/unit/infrastructure/mappers/test_execution_api_mapper.py (1)

1-226: LGTM! Test assertions updated to use ExecutionErrorType enum.

Comprehensive test coverage for all ExecutionErrorType variants (SCRIPT_ERROR, TIMEOUT, RESOURCE_LIMIT, SYSTEM_ERROR, PERMISSION_DENIED) with correct enum comparisons. The tests properly validate the mapper's direct pass-through behavior.

backend/app/domain/user/settings_models.py (2)

21-29: LGTM! Editor theme default updated to "auto".

The default theme change from "one-dark" to "auto" aligns with the PR objectives for updated editor settings.


74-82: LGTM! History entry enhanced with EventType and tracking fields.

The event_type migration to EventType is consistent with other domain models. The new reason and correlation_id fields provide useful context for history/audit tracking.

backend/app/db/repositories/sse_repository.py (2)

17-27: Simplified status retrieval with direct domain construction.

The refactored method efficiently projects only the status field and constructs SSEExecutionStatusDomain directly.

Note: ExecutionStatus(doc["status"]) at line 25 will raise ValueError if the database contains an unexpected status string. If data integrity is guaranteed, this is fine. Otherwise, consider defensive handling:

🔎 Optional defensive handling
+        try:
+            status = ExecutionStatus(doc["status"])
+        except ValueError:
+            return None  # or log and handle gracefully
         return SSEExecutionStatusDomain(
             execution_id=execution_id,
-            status=ExecutionStatus(doc["status"]),
+            status=status,
             timestamp=datetime.now(timezone.utc).isoformat(),
         )

29-33: LGTM! Streamlined execution retrieval.

The method correctly delegates to the mapper for document-to-domain conversion with proper null handling.

backend/app/infrastructure/mappers/execution_api_mapper.py (1)

16-31: LGTM! Simplified mapper with direct pass-through.

The null check for resource_usage and direct pass-through of error_type simplify the logic correctly. ResourceUsageDomain.to_dict() exists and works as expected, and ExecutionResult.error_type accepts ExecutionErrorType directly since both DomainExecution and ExecutionResult use the same Optional[ExecutionErrorType] type.

frontend/src/components/editor/OutputPanel.svelte (1)

138-166: Resource usage display implementation looks good.

The CPU time conversion now correctly uses clk_tck_hertz from the result data with a sensible fallback to 100 Hz. This addresses the previous concern about hardcoded jiffy conversion values.

frontend/src/lib/editor/execution.svelte.ts (1)

107-115: Clean public API surface for execution state.

The returned object provides a well-encapsulated interface with getters for reactive state (phase, result, error, isExecuting) and methods (execute, reset). This is a good pattern for Svelte 5's runes-based reactivity.

backend/app/services/user_settings_service.py (1)

248-276: Type alignment for event_type field looks correct.

The change from str(event.event_type) to event.event_type aligns with the updated DomainSettingsHistoryEntry.event_type field type (now EventType instead of str). This maintains type consistency across the domain layer.

backend/app/api/routes/events.py (1)

327-333: Verify: Removing replay. prefix may impact downstream event consumers.

The event type now publishes event.event_type directly instead of the previous f"replay.{event.event_type}". While replay_correlation_id still identifies these as replayed events, consumers that filter or handle events by type may not distinguish replays from originals.

If this is intentional (replayed events should be indistinguishable), consider documenting this behavior. If not, the prefix can be preserved while maintaining type safety:

 await kafka_event_service.publish_event(
-    event_type=event.event_type,
+    event_type=f"replay.{event.event_type}",
     payload=event.payload,

Note: This would require ensuring the Kafka service accepts string event types for replay scenarios, or defining dedicated REPLAY_* EventType variants.

backend/app/domain/events/event_models.py (1)

76-91: Event domain model correctly typed with EventType.

The Event.event_type field is now properly typed as EventType enum, aligning with the broader migration toward strongly-typed event handling across the codebase.

backend/tests/unit/services/sse/test_shutdown_manager.py (2)

29-31: Test correctly updated to use attribute access on ShutdownStatus.

The change from dict-style access (status["phase"]) to attribute access (status.phase) aligns with the ShutdownStatus being a typed object rather than a dictionary.


62-69: Phase transition test correctly validates typed status object.

The assertions properly check st0.phase and st1.phase as attributes, consistent with the ShutdownStatus model.

frontend/src/lib/admin/events/__tests__/eventTypes.test.ts (3)

13-14: Nice helper function for creating test fixtures.

The withFilter helper provides a clean way to create filter objects with specific overrides while maintaining default values. Good pattern for test readability.


28-46: Thorough parameterized tests for color mapping.

The getEventTypeColor tests cover all color categories and verify both the color class and dark mode variant presence. Good use of it.each for maintainable test cases.


112-136: Comprehensive filter summary tests with edge cases.

The getActiveFilterSummary tests cover empty filters, individual active filters (including singular/plural event type labels), and combinations. This ensures the summary generation logic handles all scenarios correctly.

backend/tests/unit/services/sse/test_redis_bus.py (2)

80-86: Tests correctly updated to use typed RedisSSEMessage model.

The subscription .get() method now properly receives a model type and returns a typed object. Assertions correctly access msg.event_type and msg.execution_id as attributes.


100-116: Notification test properly validates typed RedisNotificationMessage flow.

The test constructs a typed RedisNotificationMessage, publishes it, and verifies the subscription returns the correctly typed object with expected attributes. Good coverage of the typed message round-trip.

backend/tests/unit/services/sse/test_sse_service.py (2)

24-33: LGTM: Typed subscription validation enhances test coverage.

The updated get method now validates messages against Pydantic models, which better mirrors production behavior and catches schema violations during testing.


193-202: Well-structured notification payload for schema validation.

The notification payload correctly matches the RedisNotificationMessage schema fields, ensuring the test validates against the actual message structure used in production.

backend/app/services/notification_service.py (2)

794-804: Excellent migration to typed SSE messages.

The notification service now publishes strongly-typed RedisNotificationMessage objects instead of raw dictionaries, improving type safety and validation across the SSE delivery pipeline.


806-828: Improved skip reason handling with explicit return type.

The updated signature -> str | None makes the skip logic clearer by returning the skip reason directly, rather than relying on side effects or logging alone.

backend/app/schemas_pydantic/user_settings.py (2)

24-24: Sensible default theme change to "auto".

Changing the default editor theme from "one-dark" to "auto" enables automatic theme detection, improving the user experience by respecting system preferences.


94-94: Stronger typing with EventType enum.

Replacing str with EventType for event_type fields eliminates string typos and enables better IDE support and validation.

Also applies to: 119-119

backend/app/db/repositories/event_repository.py (1)

118-122: Proper EventType integration with MongoDB filtering.

The updated signature accepts list[EventType] and correctly converts to enum values via .value for MongoDB's $in operator. This maintains type safety at the API boundary while ensuring database compatibility.

backend/app/api/routes/execution.py (3)

54-54: Cleaner conversion with model_validate.

Using ExecutionInDB.model_validate(domain_exec) is more idiomatic than manual field mapping and leverages Pydantic's validation and conversion logic.


240-247: EventType parameter enables type-safe event filtering.

The route now accepts list[EventType] for event filtering, providing IDE autocomplete and preventing invalid event type strings. The response construction via model_validate ensures consistency.


298-298: Consistent model_validate usage for resource limits.

Direct model validation maintains the pattern established elsewhere in the routes and simplifies the conversion logic.

backend/app/services/event_service.py (2)

2-2: Modern Python typing with built-in generics.

Replacing typing.List with built-in list[...] syntax aligns with PEP 585 (Python 3.9+) and modern typing conventions, improving readability and reducing imports.

Also applies to: 37-37, 59-59, 121-121, 160-160, 176-176


198-198: EventType parameter enables type-safe aggregate queries.

The updated signature accepts list[EventType] for event filtering, ensuring type safety when querying events by aggregate ID.

backend/app/schemas_pydantic/sse.py (4)

12-12: Well-defined control event types using Literal.

Using Literal types for SSE control events ('connected', 'heartbeat', etc.) provides compile-time type checking and clear documentation of valid event types.

Also applies to: 72-72


18-61: Comprehensive SSE execution event payload model.

SSEExecutionEventData provides a unified, well-documented model for all execution-related SSE events. The optional fields accommodate different event types while maintaining type safety.


63-68: Redis message wrapper for typed SSE delivery.

RedisSSEMessage provides a clean wrapper for publishing execution events to Redis, separating the event type and execution ID from the full event payload.


75-100: Well-structured notification SSE schemas.

The separation between SSENotificationEventData (client-facing payload) and RedisNotificationMessage (Redis transport) provides clear boundaries for notification delivery through SSE.

Also applies to: 102-112

backend/app/services/kafka_event_service.py (2)

30-37: No changes needed. EventType extends StringEnum, which is based on Python's StrEnum. In StrEnum, enum members are string instances themselves (not wrapped values), so assigning event_type directly to headers is correct and type-safe. The dict[str, str] type annotation accepts StrEnum members without requiring .value.

Likely an incorrect or invalid review comment.


84-91: No changes needed — EventType usage is correct.

EventType extends StringEnum, which is explicitly designed to behave like a plain string in all representations. Passing the enum value directly (without .value) is the correct approach for both Pydantic models and string-typed dictionaries. The concrete event classes confirm this pattern by using event_type: Literal[EventType.XXX] = EventType.XXX without conversions.

backend/app/services/sse/sse_service.py (3)

54-59: Connected / error / status events look consistent with typed SSE payloads

The handshake (connected), early error on shutdown, and initial status events are now all going through SSEExecutionEventData and _format_sse_event, which keeps the SSE payload shape consistent. No issues spotted here.

Also applies to: 66-71, 80-85


148-164: Result enrichment on RESULT_STORED looks correct

Fetching the execution and validating it into ExecutionResult only for EventType.RESULT_STORED, then merging msg.data with explicit event_type / execution_id / type / result is sane and matches the new typed schema usage. Just be aware that if msg.data already contains those keys, they will be overwritten by the explicit values (which is likely what you want).


169-209: Notification stream flow and formatting are consistent with the execution SSE path

The notification stream now uses RedisNotificationMessageSSENotificationEventData_format_notification_event, with connected/heartbeat/notification events carrying the expected fields. This mirrors the execution SSE shape and should make the frontend consumption uniform.

Also applies to: 233-235

docs/reference/openapi.json (1)

369-382: OpenAPI updates correctly mirror new typed backend models; watch for client compatibility

The spec changes (wiring event_types params and various event/notification/ DLQ fields to EventType, introducing ExecutionErrorType, LanguageInfo, health (LivenessResponse/ReadinessResponse/ShutdownStatusResponse), and the rate‑limit/user‑admin response models) all look internally consistent and match the backend schema changes. The main follow‑up is ensuring any external clients/frontends that previously treated these fields as free‑form strings or simple arrays are updated to handle the new enum values and nested shapes.

Also applies to: 1165-1189, 2638-2760, 3145-3146, 3284-3285, 3335-3336, 4950-4975, 5012-5039, 6015-6026, 6033-6035, 6183-6247, 7718-7720, 7927-7928, 8384-8388, 8415-8465, 8786-8884

backend/app/services/sse/redis_bus.py (2)

14-21: Typed subscription wrapper is appropriate for the new Pydantic SSE messages

Wrapping the Redis PubSub with SSERedisSubscription.get(model, timeout) and close() (unsubscribe + aclose()) is a clean abstraction over the raw pubsub API and lines up with how SSEService now consumes RedisSSEMessage / RedisNotificationMessage.

Also applies to: 31-36


54-61: Event/notification publishing now correctly reuses the typed Redis message schemas

Building a RedisSSEMessage from BaseEvent and publishing its JSON, and similarly publishing RedisNotificationMessage JSON for notifications, keeps serialization centralized in the Pydantic models and matches how the subscribers now parse messages. No functional issues spotted.

Also applies to: 68-71

backend/app/schemas_pydantic/execution.py (5)

32-44: Execution models now align with ExecutionErrorType and attribute-based validation*

Switching error_type in ExecutionInDB and ExecutionResult to ExecutionErrorType | None and enabling from_attributes=True is consistent with the domain enums and supports validating from ORM/domain objects. This looks correct assuming the stored values are the same enum string values.

Also applies to: 97-111


57-68: ResourceUsage, LanguageInfo, and ResourceLimits.supported_runtimes wiring looks coherent

Adding model_config = ConfigDict(from_attributes=True) to ResourceUsage and LanguageInfo, and changing ResourceLimits.supported_runtimes to dict[str, LanguageInfo] matches the new runtime metadata structure and the OpenAPI spec. No functional issues apparent; just ensure settings and any persistence layer store supported_runtimes in the matching shape.

Also applies to: 113-120, 122-133


77-85: Runtime validation now leverages structured runtimes; behavior change is acceptable

Using SUPPORTED_RUNTIMES with lang_info.versions tightens ExecutionRequest validation and should give clearer errors when a language or version isn't allowed. This is good, but note it's a behavioral change: previously accepted languages/versions that are no longer present in SUPPORTED_RUNTIMES will now raise ValueError and surface as 422s. That seems intentional; just verify the settings are populated correctly in all envs.


155-163: Binding ExecutionEventResponse.event_type to EventType and enabling from_attributes is consistent

Typing event_type as EventType and adding from_attributes will help keep execution events in sync with the central enum and simplify mapping from domain objects. This matches the OpenAPI updates and looks correct.


9-13: Fix import ordering to satisfy Ruff linting

The imports at lines 9-11 are not in alphabetical order. Reorder them as follows:

-from app.domain.enums.storage import ExecutionErrorType
-from app.domain.enums.events import EventType
-from app.domain.enums.execution import ExecutionStatus
+from app.domain.enums.events import EventType
+from app.domain.enums.execution import ExecutionStatus
+from app.domain.enums.storage import ExecutionErrorType
frontend/src/lib/api/types.gen.ts (4)

2074-2101: No action required: supported_runtimes structure change has been properly implemented throughout the codebase.

The type change from { [key: string]: Array<string> } to { [key: string]: LanguageInfo } (line 2099) is confirmed. However, all usages in Editor.svelte correctly access the new structure:

  • Accessing .versions when needing the version array (lines 141, 145, 267)
  • Accessing .file_ext when needing the file extension (lines 232, 249, 255)
  • Backend models already use the correct dict[str, LanguageInfo] structure

No broken array-like access patterns exist in the codebase.


173-173: EventType is a discriminated union type, not an enum—the type safety improvement is compatible with existing code.

The event_type fields at lines 173, 243, 1061, and 2514 have been changed from string to EventType. EventType is defined as a union of 53 specific string literal values (line 1028), making this a type safety improvement rather than a breaking change. The consuming code uses valid EventType values ('execution_completed', 'pod_created', 'execution_started', etc.), and no direct string assignments to event_type fields were found in the codebase. The defensive fallback pattern in execution.svelte.ts:59 remains compatible with the stricter typing.


3263-3284: The event_types query parameter already expects Array<EventType> | null and is properly handled by the API client's built-in array serialization. No evidence suggests this is a breaking change—consuming code throughout the codebase (e.g., FAILURE_EVENTS in execution.svelte.ts, EventFilters in eventTypes.ts) already passes arrays to this parameter. The client's serializeArrayParam utility correctly handles array serialization.

Likely an incorrect or invalid review comment.


3792-3823: Type changes for health and user management endpoints are correctly implemented.

The upgrade from generic response types to concrete types (LivenessResponse, ReadinessResponse, DeleteUserResponse, UserRateLimitsResponse, RateLimitUpdateResponse) is properly integrated throughout the SDK and consuming code. Test mocks match the expected response shapes, and all property accesses align with the concrete type definitions.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (5)
frontend/src/lib/editor/execution.svelte.ts (3)

63-65: Type assertion without validation (duplicate concern).

Similar to line 37, casting eventData.status as ExecutionPhase without validation could assign an invalid phase if the backend sends an unexpected status string.


82-85: Critical: Unhandled rejection in onerror handler.

If fetchFallback() throws on line 84, the async arrow function will have an unhandled promise rejection. The outer promise (created on line 39) will never be rejected, leaving the execution in a pending state indefinitely.

🔎 Proposed fix
                eventSource.onerror = async () => {
                    eventSource.close();
-                   await fetchFallback();
+                   try {
+                       await fetchFallback();
+                   } catch (e) {
+                       reject(e);
+                   }
                };

73-79: Critical: Errors from fetchFallback are caught and logged but don't reject the promise.

When a failure event occurs, fetchFallback() is awaited (line 75), but if it throws, the error is caught by the try-catch on line 77 and only logged to console. The outer promise (created on line 39) is never rejected or resolved, leaving the execution hanging indefinitely.

The past review comment was marked as addressed, but the error handling is still incomplete.

🔎 Proposed fix
                    try {
                        const eventData = JSON.parse(event.data);
                        const eventType = eventData?.event_type || eventData?.type;

                        if (eventType === 'heartbeat' || eventType === 'connected') return;

                        if (eventData.status) {
                            phase = eventData.status as ExecutionPhase;
                        }

                        if (eventType === 'result_stored' && eventData.result) {
                            eventSource.close();
                            resolve(eventData.result);
                            return;
                        }

                        if (FAILURE_EVENTS.has(eventType)) {
                            eventSource.close();
-                           await fetchFallback();
+                           try {
+                               await fetchFallback();
+                           } catch (e) {
+                               reject(e);
+                           }
+                           return;
                        }
                    } catch (err) {
-                       console.error('SSE parse error:', err);
+                       // Only log parse errors, not fetchFallback errors
+                       if (err instanceof SyntaxError) {
+                           console.error('SSE parse error:', err);
+                       } else {
+                           throw err;
+                       }
                    }
backend/app/services/sse/sse_service.py (1)

146-156: Exception swallowing makes debugging malformed messages difficult.

The exception handler on lines 154-156 silently drops malformed Redis messages without logging. This was previously flagged and remains unaddressed, making it difficult to diagnose schema drift or publisher issues.

🔎 Add logging before continuing
         try:
             sse_event = await self._build_sse_event_from_redis(execution_id, msg)
             yield self._format_sse_event(sse_event)
 
             # End on terminal event types
             if msg.event_type in self.TERMINAL_EVENT_TYPES:
                 logger.info(f"Terminal event for execution {execution_id}: {msg.event_type}")
                 break
-        except Exception:
-            # Ignore malformed messages
+        except Exception:
+            logger.exception(
+                "Failed to build SSE event from Redis message for execution %s",
+                execution_id,
+                extra={"event_type": str(msg.event_type), "execution_id": execution_id},
+            )
             continue

Based on past review feedback that has not been addressed.

backend/app/services/sse/redis_bus.py (1)

21-29: Deserialization errors silently swallowed.

The get() method (lines 26-29) catches all exceptions during model_validate_json() and returns None without logging. This was flagged in a previous review and makes debugging schema drift or malformed messages difficult.

🔎 Add minimal logging
         try:
             return model.model_validate_json(msg["data"])
-        except Exception:
+        except Exception:
+            logger.warning(
+                "Failed to parse Redis message as %s on channel %s",
+                model.__name__,
+                self._channel,
+                exc_info=True,
+            )
             return None

Based on past review feedback that has not been addressed.

🧹 Nitpick comments (5)
frontend/src/lib/admin/sagas/sagaStates.ts (1)

85-89: Consider deriving step count from EXECUTION_SAGA_STEPS.

The hardcoded totalSteps = 5 for execution_saga creates a maintenance risk if steps are added or removed from EXECUTION_SAGA_STEPS. Similarly, the magic number 3 for other sagas lacks a clear source of truth.

🔎 Suggested refactor to derive step counts
 export function getSagaProgressPercentage(completedSteps: string[], sagaName: string): number {
     if (!completedSteps?.length) return 0;
-    const totalSteps = sagaName === 'execution_saga' ? 5 : 3;
+    const totalSteps = sagaName === 'execution_saga' ? EXECUTION_SAGA_STEPS.length : 3;
     return Math.min(100, (completedSteps.length / totalSteps) * 100);
 }

If other saga types also have defined steps, consider creating a mapping of saga names to their step arrays, then derive all step counts dynamically.

backend/app/schemas_pydantic/sse.py (3)

18-61: Consider discriminated unions for stronger type safety.

The SSEExecutionEventData model uses a flexible approach with many optional fields and a union type for event_type. While functional, this design makes it difficult to enforce type-specific field requirements at the schema level.

The duplicate type field (line 37-39) marked as "legacy" adds maintenance overhead.

💡 Alternative approach using discriminated unions

Consider splitting into specific event types with a discriminator:

class SSEControlEvent(BaseModel):
    event_type: SSEControlEventType
    execution_id: str
    timestamp: str
    # control-specific fields
    
class SSEBusinessEvent(BaseModel):
    event_type: EventType
    execution_id: str
    timestamp: str
    # business-specific fields

SSEExecutionEventData = SSEControlEvent | SSEBusinessEvent

This would provide compile-time type safety and eliminate the need for the legacy type field.


75-100: Consistent notification event structure.

The SSENotificationEventData follows the same flexible pattern as SSEExecutionEventData, using optional fields to accommodate different event types (connected, heartbeat, notification).

Similar to the execution events, discriminated unions could provide stronger type safety if needed in the future.


102-113: Well-structured notification message with minor suggestion.

The RedisNotificationMessage model is well-typed with appropriate use of enums for severity and status. All critical fields are required.

Consider using None instead of "" as the default for action_url (line 111) to better distinguish between "no URL provided" vs "empty string URL".

🔎 Suggested adjustment
-    action_url: str = Field(default="", description="Optional action URL")
+    action_url: str | None = Field(default=None, description="Optional action URL")
backend/app/services/sse/sse_service.py (1)

158-174: Add error handling for validation failures.

The _build_sse_event_from_redis method performs validation (line 164) and dict spreading (line 167) without handling potential failures. If model_validate or model_validate on line 166 fails, the exception propagates to the caller's exception handler (which currently swallows it silently).

Consider adding specific error handling or validation checks here to provide better diagnostics.

🔎 Suggested error handling
 async def _build_sse_event_from_redis(self, execution_id: str, msg: RedisSSEMessage) -> SSEExecutionEventData:
     """Build typed SSE event from Redis message."""
     result: ExecutionResult | None = None
     if msg.event_type == EventType.RESULT_STORED:
-        exec_domain = await self.repository.get_execution(execution_id)
-        if exec_domain:
+        try:
+            exec_domain = await self.repository.get_execution(execution_id)
+            if exec_domain:
-            result = ExecutionResult.model_validate(exec_domain)
+                result = ExecutionResult.model_validate(exec_domain)
+        except Exception:
+            logger.warning(
+                "Failed to fetch or validate execution result for %s",
+                execution_id,
+            )
 
-    return SSEExecutionEventData.model_validate(
-        {
-            **msg.data,
-            "event_type": msg.event_type,
-            "execution_id": execution_id,
-            "type": msg.event_type,
-            "result": result,
-        }
-    )
+    try:
+        return SSEExecutionEventData.model_validate(
+            {
+                **msg.data,
+                "event_type": msg.event_type,
+                "execution_id": execution_id,
+                "type": msg.event_type,
+                "result": result,
+            }
+        )
+    except Exception:
+        logger.exception("Failed to validate SSE event data for %s", execution_id)
+        raise
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8a9ab47 and 48c8d5c.

📒 Files selected for processing (14)
  • backend/app/db/repositories/sse_repository.py
  • backend/app/schemas_pydantic/execution.py
  • backend/app/schemas_pydantic/sse.py
  • backend/app/services/notification_service.py
  • backend/app/services/sse/redis_bus.py
  • backend/app/services/sse/sse_service.py
  • backend/tests/integration/notifications/test_notification_sse.py
  • backend/tests/integration/services/sse/test_partitioned_event_router_integration.py
  • backend/tests/integration/test_sse_routes.py
  • frontend/src/lib/admin/sagas/sagaStates.ts
  • frontend/src/lib/admin/users/__tests__/rateLimits.test.ts
  • frontend/src/lib/admin/users/rateLimits.ts
  • frontend/src/lib/editor/execution.svelte.ts
  • frontend/src/stores/userSettings.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • frontend/src/lib/admin/users/tests/rateLimits.test.ts
  • backend/app/db/repositories/sse_repository.py
  • frontend/src/stores/userSettings.ts
🧰 Additional context used
🧬 Code graph analysis (8)
backend/tests/integration/services/sse/test_partitioned_event_router_integration.py (2)
backend/app/schemas_pydantic/sse.py (1)
  • RedisSSEMessage (63-68)
backend/app/services/sse/redis_bus.py (1)
  • get (21-29)
backend/app/services/notification_service.py (2)
backend/app/schemas_pydantic/sse.py (1)
  • RedisNotificationMessage (102-112)
backend/app/services/sse/redis_bus.py (1)
  • publish_notification (68-70)
backend/app/services/sse/redis_bus.py (2)
backend/app/infrastructure/kafka/events/base.py (1)
  • BaseEvent (13-37)
backend/app/schemas_pydantic/sse.py (2)
  • RedisNotificationMessage (102-112)
  • RedisSSEMessage (63-68)
backend/tests/integration/notifications/test_notification_sse.py (2)
backend/app/schemas_pydantic/sse.py (1)
  • RedisNotificationMessage (102-112)
backend/app/services/sse/redis_bus.py (1)
  • get (21-29)
backend/app/schemas_pydantic/sse.py (1)
backend/app/schemas_pydantic/execution.py (2)
  • ExecutionResult (98-111)
  • ResourceUsage (57-67)
frontend/src/lib/editor/execution.svelte.ts (4)
frontend/src/lib/editor/index.ts (3)
  • ExecutionPhase (2-2)
  • createExecutionState (2-2)
  • ExecutionState (2-2)
frontend/src/lib/api/index.ts (4)
  • EventType (4-4)
  • ExecutionResult (4-4)
  • createExecutionApiV1ExecutePost (3-3)
  • getResultApiV1ResultExecutionIdGet (3-3)
frontend/src/lib/api/types.gen.ts (2)
  • EventType (1028-1028)
  • ExecutionResult (1178-1206)
frontend/src/lib/api-interceptors.ts (1)
  • getErrorMessage (32-48)
backend/tests/integration/test_sse_routes.py (2)
backend/app/schemas_pydantic/sse.py (1)
  • RedisNotificationMessage (102-112)
backend/app/services/sse/redis_bus.py (1)
  • publish_notification (68-70)
backend/app/services/sse/sse_service.py (3)
backend/app/schemas_pydantic/execution.py (1)
  • ExecutionResult (98-111)
backend/app/schemas_pydantic/sse.py (2)
  • RedisNotificationMessage (102-112)
  • RedisSSEMessage (63-68)
backend/app/services/sse/redis_bus.py (1)
  • get (21-29)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Scan Frontend
  • GitHub Check: E2E Tests
  • GitHub Check: Integration Tests
🔇 Additional comments (28)
frontend/src/lib/admin/sagas/sagaStates.ts (3)

1-12: LGTM! Clean interface definition.

The imports and SagaStateConfig interface are well-structured. Using typeof CheckCircle as the icon type is a reasonable way to type-hint lucide icon components, since they all share the same component signature.


14-68: Excellent state configuration and defensive fallback handling.

The SAGA_STATES mapping provides comprehensive coverage of all saga lifecycle states with semantically appropriate colors and icons. The getSagaStateInfo function correctly uses the spread operator to avoid mutating DEFAULT_STATE when handling unknown states—good defensive programming.


70-83: Well-designed saga step definitions following the saga pattern.

The EXECUTION_SAGA_STEPS correctly implements the saga pattern with compensating transactions for steps that have side effects (resource allocation, queueing, pod creation), while validation and monitoring appropriately have no compensations.

frontend/src/lib/admin/users/rateLimits.ts (1)

55-63: Multiplier fallback logic has been addressed.

The previous concern about the multiplier fallback has been resolved. The current implementation explicitly validates that only positive multipliers are accepted, with 0 and negative values falling back to 1. This is a reasonable design choice if the business logic requires strictly positive multipliers.

Note: This differs from the previous suggestion to use nullish coalescing (??), but achieves the intended goal of handling invalid multipliers appropriately.

frontend/src/lib/editor/execution.svelte.ts (4)

1-12: LGTM: Imports and type definitions are well-structured.

The imports are appropriate, and using Set<EventType> for FAILURE_EVENTS provides type-safe, efficient membership testing.


14-23: LGTM: State initialization and reset logic are clean.

The use of Svelte 5 runes and the straightforward reset implementation are appropriate.


88-104: Good error handling with fallback attempt.

The try-catch-finally structure is well-designed. The fallback fetch in the catch block (lines 91-100) provides resilience, and the silent catch on line 100 is acceptable since an error is already set. The finally block ensures proper cleanup.


107-117: LGTM: Clean API surface with proper encapsulation.

The use of getters for reactive state, the computed isExecuting property, and the ReturnType type alias are all excellent TypeScript and Svelte 5 patterns.

backend/app/schemas_pydantic/sse.py (2)

2-16: LGTM! Strong typing foundation established.

The imports and type definitions properly support the typed SSE payload models. The use of Literal for control event types and the bounded TypeVar for generic message parsing are good patterns.


63-69: LGTM! Clean Redis message wrapper.

The RedisSSEMessage model provides a clear structure for Redis pub/sub messages. The separation between Redis wire format and SSE client format is well-designed.

Note: execution_id is optional (line 67), which allows for flexibility but requires consumers to handle None cases.

backend/app/services/sse/sse_service.py (4)

11-31: LGTM! Typed model imports properly integrated.

The imports of typed SSE models (SSEExecutionEventData, SSENotificationEventData, RedisSSEMessage, RedisNotificationMessage) support the refactoring toward strongly-typed event handling throughout the service.


49-106: LGTM! Consistent typed event construction.

The stream creation logic properly constructs typed SSEExecutionEventData instances for error, connected, and status events. The use of explicit field names and ISO 8601 timestamps is good practice.


176-230: LGTM! Well-structured notification stream with typed messages.

The notification stream properly uses typed models throughout:

  • SSENotificationEventData for control events (connected, heartbeat)
  • RedisNotificationMessage for Redis subscription
  • Clean mapping between Redis messages and SSE events

245-251: LGTM! Clean JSON serialization for SSE.

The formatting methods properly use model_dump_json(exclude_none=True) to serialize typed events, reducing payload size by omitting null fields.

backend/app/schemas_pydantic/execution.py (7)

9-55: LGTM! Improved type specificity and ORM compatibility.

The changes enhance type safety:

  • ExecutionErrorType is more specific than the generic ErrorType
  • ConfigDict(from_attributes=True) enables population from ORM objects
  • Consistent application across ExecutionInDB and ExecutionUpdate

57-68: LGTM! ORM compatibility added.

Adding ConfigDict(from_attributes=True) enables direct population from ORM models, consistent with other schema updates.


77-87: LGTM! Runtime validation properly refactored.

The validation logic now uses structured LanguageInfo with versions list instead of plain lists. The walrus operator on line 80 provides clean existence checking.

The line-length issue from the previous review has been addressed by splitting the error message across lines 83-85.


98-112: LGTM! Consistent type and config updates.

The ExecutionResult model receives the same improvements as other execution schemas: ExecutionErrorType for specificity and from_attributes=True for ORM compatibility.


114-121: LGTM! Well-designed language metadata model.

The new LanguageInfo model provides structured runtime information with versions and file_ext fields. This is a significant improvement over the previous dict[str, list[str]] approach, offering better type safety and extensibility.


123-134: LGTM! Enhanced runtime metadata structure.

The ResourceLimits model now uses dict[str, LanguageInfo] for supported_runtimes, providing richer language metadata compared to the previous simple list-based approach.


155-164: LGTM! Stronger typing with EventType enum.

Changing event_type from str to EventType (line 159) provides compile-time type safety and eliminates magic strings. This aligns with the broader typed-events refactoring across the codebase.

backend/tests/integration/services/sse/test_partitioned_event_router_integration.py (1)

11-51: LGTM! Test properly updated for typed Redis messages.

The integration test now uses RedisSSEMessage for type-safe message retrieval (line 46) and properly handles enum comparisons with str() conversion (line 51). This aligns with the typed message infrastructure introduced in the codebase.

backend/tests/integration/test_sse_routes.py (1)

9-87: LGTM! Test properly uses typed notification messages.

The test now constructs a fully-typed RedisNotificationMessage (lines 77-86) instead of a dict, with proper enum values for severity and status. This ensures the test validates the actual message structure used in production.

backend/tests/integration/notifications/test_notification_sse.py (1)

7-47: LGTM! Test refactored for typed message consumption.

The test helper function now returns RedisNotificationMessage (line 38) and uses typed get() (line 39), with clean attribute access (lines 45-47) replacing dict key access. This validates the typed message contract.

backend/app/services/sse/redis_bus.py (2)

54-60: LGTM! Clean typed message publication.

The publish_event() method now constructs a typed RedisSSEMessage (lines 55-59) and serializes it with model_dump_json() (line 60), ensuring consistent message structure across the Redis bus.


68-70: LGTM! Type-safe notification publishing.

The publish_notification() method now accepts a typed RedisNotificationMessage parameter instead of a generic mapping, providing better type safety and making the API contract explicit.

backend/app/services/notification_service.py (2)

792-804: LGTM! Type-safe SSE notification publishing.

The _publish_notification_sse() method now constructs a fully-typed RedisNotificationMessage (lines 794-803) with proper field mapping from the domain model. The use of list() for tags and isoformat() for timestamps ensures correct data types.


806-828: LGTM! Enhanced skip detection with reason reporting.

The _should_skip_notification() method now returns str | None (line 808), providing the skip reason when a notification should be skipped. This improves observability and allows callers to log specific reasons for skipped notifications (used at line 854).

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/src/routes/admin/__tests__/AdminEvents.test.ts (1)

163-163: Fix event_type format inconsistency.

Line 163 uses 'execution.completed' (dot notation) while all other event_type values in this file use snake_case format ('execution_completed'). This inconsistency should be corrected to match the format used throughout the codebase and in the centralized test utilities.

🔎 Proposed fix
-      const events = [createMockEvent({ event_type: 'execution.completed', metadata: { user_id: 'user-1', service_name: 'test-service' } })];
+      const events = [createMockEvent({ event_type: 'execution_completed', metadata: { user_id: 'user-1', service_name: 'test-service' } })];
♻️ Duplicate comments (1)
backend/app/services/sse/sse_service.py (1)

146-159: Enhance exception logging with full context.

The current exception handling logs a warning but doesn't capture the full stack trace or the complete message payload. The past review comment requested using logger.exception and logging the raw Redis message details. This would significantly improve debugging when schema mismatches or validation errors occur.

🔎 Recommended enhancement
             try:
                 sse_event = await self._build_sse_event_from_redis(execution_id, msg)
                 yield self._format_sse_event(sse_event)
 
                 # End on terminal event types
                 if msg.event_type in self.TERMINAL_EVENT_TYPES:
                     logger.info(f"Terminal event for execution {execution_id}: {msg.event_type}")
                     break
             except Exception as e:
-                logger.warning(
-                    f"Failed to process SSE message for execution {execution_id}: {e}",
-                    extra={"execution_id": execution_id, "event_type": str(msg.event_type)},
+                logger.exception(
+                    "Failed to process SSE message for execution %s: %r",
+                    execution_id,
+                    msg,
+                    extra={"execution_id": execution_id, "event_type": str(msg.event_type), "msg_data": msg.data},
                 )
                 continue
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 48c8d5c and 4973dfe.

📒 Files selected for processing (4)
  • backend/app/services/sse/redis_bus.py
  • backend/app/services/sse/sse_service.py
  • frontend/src/routes/admin/__tests__/AdminEvents.test.ts
  • frontend/src/routes/admin/__tests__/test-utils.ts
🧰 Additional context used
🧬 Code graph analysis (3)
frontend/src/routes/admin/__tests__/AdminEvents.test.ts (1)
frontend/src/routes/admin/__tests__/test-utils.ts (3)
  • mockElementAnimate (35-37)
  • mockWindowGlobals (42-45)
  • createMockEvent (74-74)
backend/app/services/sse/sse_service.py (4)
backend/app/schemas_pydantic/execution.py (1)
  • ExecutionResult (98-111)
backend/app/schemas_pydantic/sse.py (4)
  • RedisNotificationMessage (102-112)
  • RedisSSEMessage (63-68)
  • SSEExecutionEventData (18-60)
  • SSENotificationEventData (75-99)
backend/app/services/sse/redis_bus.py (1)
  • get (22-34)
backend/app/dlq/models.py (1)
  • event_type (93-95)
backend/app/services/sse/redis_bus.py (1)
backend/app/schemas_pydantic/sse.py (2)
  • RedisNotificationMessage (102-112)
  • RedisSSEMessage (63-68)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: E2E Tests
  • GitHub Check: Build Frontend
  • GitHub Check: Integration Tests
🔇 Additional comments (13)
backend/app/services/sse/redis_bus.py (3)

1-12: LGTM!

The imports are clean and well-organized. The TypeVar bound to BaseModel provides proper type safety for the generic message parsing pattern.


22-34: LGTM!

The typed message parsing implementation is well done. The model: Type[T] parameter with return type T | None provides good type safety. The logging for parse failures includes appropriate context (channel and model name), which addresses the previous review feedback about silent error swallowing.


59-75: LGTM!

The typed message publishing is clean and well-implemented:

  • publish_event correctly constructs RedisSSEMessage with proper field mapping and uses model_dump_json() for serialization.
  • publish_notification now accepts a pre-constructed RedisNotificationMessage, providing a cleaner API with full type safety.

This is a solid improvement over the previous dict-based approach.

frontend/src/routes/admin/__tests__/AdminEvents.test.ts (3)

5-13: LGTM! Successfully centralized test utilities.

The refactor to import mock helpers from the centralized test-utils module addresses the previous review concern about code duplication. The centralized approach improves maintainability and consistency across admin test files.


76-77: LGTM! Clean mock setup.

Using the centralized mock functions simplifies the test setup and improves readability.


394-394: LGTM! Consistent event_type format.

The event_type values correctly use snake_case format (e.g., execution_completed, execution_started, execution_failed, pod_created) throughout these test cases, maintaining consistency with the centralized test utilities.

Also applies to: 407-407, 427-428, 742-742, 751-751, 759-759

frontend/src/routes/admin/__tests__/test-utils.ts (3)

4-37: LGTM! Comprehensive Web Animations API mock.

The animateMock object thoroughly covers the Web Animations API interface with all necessary properties and methods. The promise-returning getters for finished and ready are correctly implemented, and the mockElementAnimate function properly sets up the mock for Svelte transition testing.


42-45: LGTM! Clean window globals mock.

The mockWindowGlobals function provides a straightforward way to stub window globals for testing, properly using the vitest API to control window.open and window.confirm behavior.


51-126: LGTM! Well-designed mock factories with complete event type coverage.

The event mock helpers and factory functions are well-structured and address the previous review concern about incomplete event type coverage. By importing EVENT_TYPES directly from the source ($lib/admin/events/eventTypes) rather than maintaining a local array, the tests will automatically stay in sync with the full set of event types. The factory functions provide flexible, type-safe mock data generation with sensible defaults and override capabilities.

Based on learnings, this successfully resolves the past review feedback about centralizing mock helpers and ensuring complete event type coverage.

backend/app/services/sse/sse_service.py (4)

11-17: LGTM: Typed imports strengthen contract.

The addition of typed Pydantic models (ExecutionResult, RedisSSEMessage, SSEExecutionEventData, SSENotificationEventData) replaces ad-hoc dictionary construction with structured payloads, improving type safety and maintainability.


161-177: Validation pattern is sound but may silently skip malformed events.

The method correctly enriches RESULT_STORED events with execution details and uses Pydantic's model_validate for type safety. Validation errors (e.g., schema mismatches) are caught by the caller's exception handler at line 146-159, which logs and continues processing. This prevents one malformed message from breaking the entire stream, though it means clients won't receive that particular event. Ensure the logging enhancement suggested above is applied so debugging is tractable.


248-254: LGTM: Clean formatting helpers.

Both methods correctly serialize Pydantic models to JSON with exclude_none=True, which keeps SSE payloads lean by omitting null fields. The return structure {"data": ...} aligns with sse-starlette's expected format.


54-61: All string event type literals used in this code are valid. SSEControlEventType and SSENotificationControlEventType are Literal type aliases (not enums), and Pydantic validates them correctly. The values "error", "connected", "status", "shutdown", "heartbeat", and "notification" all match their respective Literal definitions in backend/app/schemas_pydantic/sse.py lines 12 and 72, so validation will succeed at runtime.

Likely an incorrect or invalid review comment.

Copy link

@coderabbitai coderabbitai bot left a comment

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 (2)
frontend/src/lib/editor/execution.svelte.ts (2)

98-108: Consider preserving original error context when fallback succeeds.

Lines 103-106 clear the error state when the fallback fetch successfully retrieves a result. While this may be intentional, it could hide valuable diagnostic information about why the SSE stream failed. If the execution itself succeeded but only the streaming mechanism failed, users/developers might want to know about the SSE issue.

Consider one of these approaches:

  • Log the original error even when clearing it: console.warn('SSE failed but result retrieved:', error);
  • Preserve both error and result if result indicates success
  • Add a separate state field for "streaming warnings" distinct from execution errors

21-122: Verify test coverage for critical execution paths.

The Codecov report indicates 82 missing lines of coverage in this file. Given that this module manages the entire execution lifecycle (SSE streaming, fallback fetching, error handling), ensuring comprehensive test coverage—especially for error paths, SSE failures, and timeout scenarios—would significantly improve reliability.

Consider adding tests for:

  • Successful execution with SSE
  • SSE failures triggering fallback
  • Parse errors in event handling
  • API errors during execution start
  • Fallback fetch failures
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4973dfe and f02b30e.

📒 Files selected for processing (2)
  • frontend/src/lib/admin/users/rateLimits.ts
  • frontend/src/lib/editor/execution.svelte.ts
🧰 Additional context used
🪛 Gitleaks (8.30.0)
frontend/src/lib/admin/users/rateLimits.ts

[high] 13-13: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: E2E Tests
  • GitHub Check: Build Frontend
  • GitHub Check: Integration Tests
🔇 Additional comments (5)
frontend/src/lib/admin/users/rateLimits.ts (2)

31-38: Regex anchor removal logic is correct.

The endpoint cleaning logic now correctly removes only the leading ^ anchor and trailing $ anchor, which aligns with the stated intent. The previous regex issue has been properly resolved.


56-64: Multiplier validation logic addresses previous concern.

The function now explicitly validates that only positive multipliers are accepted, with 0 and negative values falling back to 1. This is a clear design decision (documented in the comment on line 58) that differs from the previous review suggestion but appears intentional and reasonable.

frontend/src/lib/editor/execution.svelte.ts (3)

9-16: Previous type safety issue resolved.

The validation helper toExecutionPhase with VALID_BACKEND_PHASES successfully addresses the prior concern about unsafe type casting. Backend statuses are now validated before assignment, and invalid values fall back to a safe default.


80-92: Previous awaiting issues resolved.

Both identified issues from prior reviews have been addressed:

  • Line 82: fetchFallback() is now properly awaited when failure events occur
  • Line 91: The onerror handler is now async and awaits fetchFallback()

114-121: Well-designed API surface.

The return object provides a clean, reactive API with getters for state and appropriate methods. The computed isExecuting property nicely encapsulates the execution status check.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
backend/app/db/repositories/saga_repository.py (1)

126-126: Inconsistent string literal — use SagaState.COMPLETED.value.

Line 126 uses a hardcoded "completed" string in the aggregation pipeline, which is inconsistent with the enum migration applied to other methods in this file. This should use SagaState.COMPLETED.value for consistency and type safety.

🔎 Proposed fix
-            {"$match": {**query, "state": "completed", "completed_at": {"$ne": None}}},
+            {"$match": {**query, "state": SagaState.COMPLETED.value, "completed_at": {"$ne": None}}},
frontend/src/routes/admin/AdminUsers.svelte (1)

143-163: Unhandled error in saveUser.

The unwrap(result) at line 160 can throw if the create/update fails, but there's no error handling. The user won't see feedback on failure, and the form state may become inconsistent.

🔎 Proposed fix: wrap in try-catch with toast feedback
     async function saveUser(): Promise<void> {
         if (!userForm.username) { addToast('Username is required', 'error'); return; }
         if (!editingUser && !userForm.password) { addToast('Password is required', 'error'); return; }
         savingUser = true;
-        let result;
-        if (editingUser) {
-            const updateData: Record<string, string | boolean | null> = {
-                username: userForm.username, email: userForm.email || null, role: userForm.role, is_active: userForm.is_active
-            };
-            if (userForm.password) updateData.password = userForm.password;
-            result = await updateUserApiV1AdminUsersUserIdPut({ path: { user_id: editingUser.user_id }, body: updateData });
-        } else {
-            result = await createUserApiV1AdminUsersPost({
-                body: { username: userForm.username, email: userForm.email || null, password: userForm.password, role: userForm.role, is_active: userForm.is_active }
-            });
+        try {
+            let result;
+            if (editingUser) {
+                const updateData: Record<string, string | boolean | null> = {
+                    username: userForm.username, email: userForm.email || null, role: userForm.role, is_active: userForm.is_active
+                };
+                if (userForm.password) updateData.password = userForm.password;
+                result = await updateUserApiV1AdminUsersUserIdPut({ path: { user_id: editingUser.user_id }, body: updateData });
+            } else {
+                result = await createUserApiV1AdminUsersPost({
+                    body: { username: userForm.username, email: userForm.email || null, password: userForm.password, role: userForm.role, is_active: userForm.is_active }
+                });
+            }
+            unwrap(result);
+            showUserModal = false;
+            await loadUsers();
+            addToast(editingUser ? 'User updated successfully.' : 'User created successfully.', 'success');
+        } catch (err) {
+            addToast('Failed to save user. Please try again.', 'error');
+        } finally {
+            savingUser = false;
         }
-        savingUser = false;
-        unwrap(result);
-        showUserModal = false;
-        await loadUsers();
     }
♻️ Duplicate comments (3)
frontend/src/routes/admin/AdminUsers.svelte (2)

165-177: Unhandled error in deleteUser – previously flagged.

This issue was identified in a prior review. The unwrap(result) at line 173 can throw without error handling.


195-213: Unhandled errors in rate limit operations – previously flagged.

This issue was identified in a prior review. Both saveRateLimits and resetRateLimits call unwrap() without error handling.

frontend/src/routes/admin/AdminSagas.svelte (1)

137-142: Auto-refresh effect condition – previously flagged.

The condition if (autoRefresh || refreshRate) was flagged in a prior review. While setupAutoRefresh() handles this correctly internally (checking if (autoRefresh)), the effect still runs unnecessarily when only refreshRate changes and auto-refresh is disabled.

🧹 Nitpick comments (2)
frontend/src/components/admin/users/UserFilters.svelte (1)

1-36: LGTM – well-structured component with proper Svelte 5 patterns.

The props interface is well-defined, and the use of $bindable for two-way binding is appropriate. Accessibility is handled correctly with proper label associations.

Note: The filter types (RoleFilter, StatusFilter, BooleanFilter, MultiplierFilter, AdvancedFilters) are duplicated in AdminUsers.svelte. Consider extracting them into a shared types file (e.g., $lib/admin/users/types.ts) to maintain a single source of truth.

frontend/src/lib/admin/sagas/sagaStates.ts (1)

88-92: Replace magic numbers with constants or document the assumptions.

The function uses hardcoded values (5 and 3) without clear documentation. The 5 should reference EXECUTION_SAGA_STEPS.length, and the fallback 3 needs either a corresponding constant or a comment explaining what saga types use 3 steps.

🔎 Proposed fix
+// Default step count for non-execution sagas (e.g., cleanup, compensation sagas)
+const DEFAULT_SAGA_STEP_COUNT = 3;
+
 export function getSagaProgressPercentage(completedSteps: string[], sagaName: string): number {
     if (!completedSteps?.length) return 0;
-    const totalSteps = sagaName === 'execution_saga' ? 5 : 3;
+    const totalSteps = sagaName === 'execution_saga' 
+        ? EXECUTION_SAGA_STEPS.length 
+        : DEFAULT_SAGA_STEP_COUNT;
     return Math.min(100, (completedSteps.length / totalSteps) * 100);
 }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f02b30e and 1e40f7b.

📒 Files selected for processing (13)
  • backend/app/api/routes/dlq.py
  • backend/app/db/repositories/dlq_repository.py
  • backend/app/db/repositories/replay_repository.py
  • backend/app/db/repositories/saga_repository.py
  • backend/app/dlq/models.py
  • backend/tests/unit/db/repositories/test_replay_repository.py
  • backend/tests/unit/db/repositories/test_saga_repository.py
  • frontend/src/components/admin/sagas/SagaFilters.svelte
  • frontend/src/components/admin/users/UserFilters.svelte
  • frontend/src/lib/admin/events/eventTypes.ts
  • frontend/src/lib/admin/sagas/sagaStates.ts
  • frontend/src/routes/admin/AdminSagas.svelte
  • frontend/src/routes/admin/AdminUsers.svelte
🚧 Files skipped from review as they are similar to previous changes (2)
  • frontend/src/lib/admin/events/eventTypes.ts
  • frontend/src/components/admin/sagas/SagaFilters.svelte
🧰 Additional context used
🧬 Code graph analysis (6)
backend/app/db/repositories/replay_repository.py (1)
backend/app/domain/replay/models.py (1)
  • ReplaySessionState (85-102)
backend/app/dlq/models.py (4)
frontend/src/lib/admin/events/eventTypes.ts (1)
  • EventType (7-7)
frontend/src/lib/api/index.ts (1)
  • EventType (4-4)
frontend/src/lib/api/types.gen.ts (1)
  • EventType (1028-1028)
backend/tests/unit/services/idempotency/test_middleware.py (1)
  • event (32-36)
backend/app/db/repositories/dlq_repository.py (1)
backend/app/dlq/models.py (4)
  • DLQMessageStatus (10-16)
  • event_type (93-95)
  • DLQMessageListResult (219-225)
  • DLQMessageFilter (113-118)
backend/tests/unit/db/repositories/test_replay_repository.py (2)
backend/tests/unit/db/repositories/test_saga_repository.py (1)
  • repo (13-14)
backend/app/db/repositories/replay_repository.py (1)
  • update_session_status (62-65)
backend/app/api/routes/dlq.py (2)
frontend/src/lib/admin/events/eventTypes.ts (1)
  • EventType (7-7)
backend/app/dlq/models.py (1)
  • event_type (93-95)
backend/app/db/repositories/saga_repository.py (3)
backend/app/services/coordinator/queue_manager.py (1)
  • execution_id (30-31)
backend/app/domain/saga/models.py (1)
  • Saga (10-25)
backend/tests/unit/dlq/test_dlq_models.py (1)
  • value (60-61)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Scan Frontend
  • GitHub Check: E2E Tests
  • GitHub Check: Integration Tests
🔇 Additional comments (21)
backend/tests/unit/db/repositories/test_replay_repository.py (1)

29-29: LGTM! Test correctly updated to use enum.

The change from string "running" to ReplayStatus.RUNNING correctly aligns the test with the updated repository signature and improves type safety.

backend/app/db/repositories/replay_repository.py (4)

8-8: LGTM! Enum import enables type-safe status handling.

The addition of ReplayStatus import supports the migration from string-based to enum-based status handling throughout the repository.


45-52: LGTM! Type-safe signature with correct enum-to-value conversion.

The signature change to accept ReplayStatus | None and the use of status.value for MongoDB queries correctly implements enum-based status handling. The explicit query dict typing is also appropriate.

Note: This is a breaking API change for any callers passing string literals, but this aligns with the broader enum migration across the codebase.


62-64: LGTM! Enum-based status update with correct persistence.

The method signature now enforces type-safe ReplayStatus enum usage, and the implementation correctly uses status.value to persist the enum's string representation to MongoDB.


67-77: LGTM! Enum-derived terminal statuses improve maintainability.

Deriving terminal_statuses from the ReplayStatus enum eliminates hard-coded strings and establishes a single source of truth. This reduces the risk of typos and inconsistencies when status values change.

backend/app/db/repositories/saga_repository.py (3)

49-56: LGTM! State parameter correctly migrated to enum.

The signature change from str | None to SagaState | None improves type safety, and the use of state.value on line 52 correctly extracts the enum's string value for the MongoDB query.


72-80: LGTM! State parameter correctly migrated to enum.

The signature change from str to SagaState improves type safety, and the use of state.value on line 73 correctly extracts the enum's string value for the database update.


49-80: All existing callers correctly use SagaState enum.

The identified callers of get_sagas_by_execution in saga_service.py:75 and saga_orchestrator.py:393 both properly use the SagaState enum. The update_saga_state method has no production callers at this time; its only caller is in the test suite which correctly passes SagaState.COMPLETED.

backend/tests/unit/db/repositories/test_saga_repository.py (1)

6-6: LGTM! Test correctly updated to use SagaState enum.

The import of SagaState on line 6 and its usage on line 34 correctly align with the repository method signature changes. The raw string literals in the test data setup (lines 22-23) are appropriate since they're direct MongoDB document inserts.

Also applies to: 34-34

frontend/src/components/admin/users/UserFilters.svelte (1)

38-120: LGTM – clean and accessible filter UI.

The template is well-structured with:

  • Proper responsive layout using Tailwind's breakpoint classes
  • Accessible labels for all form controls
  • Conditional rendering for advanced filters with visual feedback (chevron rotation)
  • Disabled state for reset button when no filters are active
frontend/src/lib/admin/sagas/sagaStates.ts (2)

1-57: LGTM – well-organized state configuration.

Clean mapping of saga states to their visual configurations with appropriate icons and colors for each state.


59-68: LGTM – safe fallback pattern.

The getSagaStateInfo function correctly falls back to DEFAULT_STATE while preserving the unknown state value as the label.

frontend/src/routes/admin/AdminUsers.svelte (1)

241-296: LGTM – clean component composition.

The refactoring to use modular components (UserFilters, UsersTable, Pagination) improves maintainability and separates concerns appropriately.

frontend/src/routes/admin/AdminSagas.svelte (2)

41-44: Good fix for client-side filtering UX concern.

The addition of hasClientFilters tracking, the explanatory comment, and the UI message ("Showing X of Y on this page (filtered locally)") effectively addresses the pagination confusion previously flagged. Users now have clear feedback about local filtering behavior.

Also applies to: 192-209


155-220: LGTM – well-structured component composition.

The refactoring to use modular components (SagaStatsCards, SagaFilters, SagasTable, SagaDetailsModal, AutoRefreshControl) significantly improves maintainability and readability.

backend/app/api/routes/dlq.py (2)

13-13: LGTM: EventType import added.

The import supports the type-safe event filtering introduced at line 54.


54-59: Verify EventType enum Query parameter handling in FastAPI.

The migration from str | None to EventType | None improves type safety, but requires confirmation that:

  1. The EventType enum is properly defined and imported
  2. FastAPI correctly validates and deserializes enum values from query parameters
  3. The repository's get_messages() method signature accepts the EventType type
backend/app/dlq/models.py (2)

6-6: LGTM: EventType import added.

The import enables type-safe event type handling in DLQMessage and DLQMessageFilter.


93-95: Verify BaseEvent.event_type returns EventType.

The property now returns self.event.event_type directly without string conversion, which requires BaseEvent.event_type to be typed as EventType. Verify that:

  1. BaseEvent.event_type is defined as EventType (not str)
  2. All event classes properly set event_type as an EventType enum value
backend/app/db/repositories/dlq_repository.py (2)

21-21: LGTM: EventType import added.

The import supports type-safe event filtering in the repository layer.


107-118: The enum-to-string conversion concern is unfounded. DLQMessageStatus and EventType are StringEnum subclasses that inherit from Python's StrEnum, making them first-class string objects. When passed to MongoDB queries, they function as strings without requiring explicit .value conversion. The mapper implementation is correct.

However, the test file should be updated to use EventType.EXECUTION_REQUESTED instead of the string literal "execution_requested" to align with the new type signature that expects EventType | None.

Likely an incorrect or invalid review comment.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
frontend/src/routes/admin/AdminUsers.svelte (1)

143-163: Unhandled error in saveUser.

The unwrap(result) at line 160 can throw if the API call fails, but there's no error handling. The user won't see feedback on failure, and the modal will remain open.

🔎 Proposed fix: wrap in try-catch with toast feedback
     async function saveUser(): Promise<void> {
         if (!userForm.username) { addToast('Username is required', 'error'); return; }
         if (!editingUser && !userForm.password) { addToast('Password is required', 'error'); return; }
         savingUser = true;
-        let result;
-        if (editingUser) {
-            const updateData: Record<string, string | boolean | null> = {
-                username: userForm.username, email: userForm.email || null, role: userForm.role, is_active: userForm.is_active
-            };
-            if (userForm.password) updateData.password = userForm.password;
-            result = await updateUserApiV1AdminUsersUserIdPut({ path: { user_id: editingUser.user_id }, body: updateData });
-        } else {
-            result = await createUserApiV1AdminUsersPost({
-                body: { username: userForm.username, email: userForm.email || null, password: userForm.password, role: userForm.role, is_active: userForm.is_active }
-            });
+        try {
+            let result;
+            if (editingUser) {
+                const updateData: Record<string, string | boolean | null> = {
+                    username: userForm.username, email: userForm.email || null, role: userForm.role, is_active: userForm.is_active
+                };
+                if (userForm.password) updateData.password = userForm.password;
+                result = await updateUserApiV1AdminUsersUserIdPut({ path: { user_id: editingUser.user_id }, body: updateData });
+            } else {
+                result = await createUserApiV1AdminUsersPost({
+                    body: { username: userForm.username, email: userForm.email || null, password: userForm.password, role: userForm.role, is_active: userForm.is_active }
+                });
+            }
+            unwrap(result);
+            showUserModal = false;
+            await loadUsers();
+            addToast(editingUser ? 'User updated successfully' : 'User created successfully', 'success');
+        } catch (err) {
+            addToast('Failed to save user. Please try again.', 'error');
+        } finally {
+            savingUser = false;
         }
-        savingUser = false;
-        unwrap(result);
-        showUserModal = false;
-        await loadUsers();
     }
backend/app/db/repositories/replay_repository.py (1)

97-112: Critical: Caller still uses old filter keyword argument instead of replay_filter.

The method signature was updated to use replay_filter, but backend/app/services/event_replay/replay_service.py lines 189-191 still passes filter= as a keyword argument:

async for batch_docs in self._repository.fetch_events(
    filter=session.config.filter, batch_size=session.config.batch_size
):

Update this call to use replay_filter= instead of filter=.

♻️ Duplicate comments (3)
frontend/src/routes/admin/AdminUsers.svelte (3)

165-177: Still unhandled: error in deleteUser.

This issue was flagged in a previous review but remains unfixed. The unwrap(result) at line 173 can throw if the delete fails, but there's no error handling. The user won't see feedback on failure.

🔎 Proposed fix: wrap in try-catch with toast feedback
     async function deleteUser(): Promise<void> {
         if (!userToDelete) return;
         deletingUser = true;
-        const result = await deleteUserApiV1AdminUsersUserIdDelete({
-            path: { user_id: userToDelete.user_id },
-            query: { cascade: cascadeDelete }
-        });
-        deletingUser = false;
-        unwrap(result);
-        await loadUsers();
-        showDeleteModal = false;
-        userToDelete = null;
+        try {
+            const result = await deleteUserApiV1AdminUsersUserIdDelete({
+                path: { user_id: userToDelete.user_id },
+                query: { cascade: cascadeDelete }
+            });
+            unwrap(result);
+            await loadUsers();
+            showDeleteModal = false;
+            userToDelete = null;
+            addToast('User deleted successfully', 'success');
+        } catch (err) {
+            addToast('Failed to delete user. Please try again.', 'error');
+        } finally {
+            deletingUser = false;
+        }
     }

202-212: Still unhandled: error in saveRateLimits.

This issue was flagged in a previous review but remains unfixed. The unwrap(result) at line 210 can throw if the save fails, but there's no error handling. The modal won't close, and the user won't see feedback on failure.

🔎 Proposed fix: wrap in try-catch with toast feedback
     async function saveRateLimits(): Promise<void> {
         if (!rateLimitUser || !rateLimitConfig) return;
         savingRateLimits = true;
-        const result = await updateUserRateLimitsApiV1AdminUsersUserIdRateLimitsPut({
-            path: { user_id: rateLimitUser.user_id },
-            body: rateLimitConfig
-        });
-        savingRateLimits = false;
-        unwrap(result);
-        showRateLimitModal = false;
+        try {
+            const result = await updateUserRateLimitsApiV1AdminUsersUserIdRateLimitsPut({
+                path: { user_id: rateLimitUser.user_id },
+                body: rateLimitConfig
+            });
+            unwrap(result);
+            showRateLimitModal = false;
+            addToast('Rate limits updated successfully', 'success');
+        } catch (err) {
+            addToast('Failed to update rate limits. Please try again.', 'error');
+        } finally {
+            savingRateLimits = false;
+        }
     }

214-220: Still unhandled: error in resetRateLimits.

This issue was flagged in a previous review but remains unfixed. The unwrap at line 216 can throw if the reset fails, but there's no error handling. The user won't see feedback on failure.

🔎 Proposed fix: wrap in try-catch with toast feedback
     async function resetRateLimits(): Promise<void> {
         if (!rateLimitUser) return;
-        unwrap(await resetUserRateLimitsApiV1AdminUsersUserIdRateLimitsResetPost({
-            path: { user_id: rateLimitUser.user_id }
-        }));
-        rateLimitUsage = {};
+        try {
+            unwrap(await resetUserRateLimitsApiV1AdminUsersUserIdRateLimitsResetPost({
+                path: { user_id: rateLimitUser.user_id }
+            }));
+            rateLimitUsage = {};
+            addToast('Rate limits reset successfully', 'success');
+        } catch (err) {
+            addToast('Failed to reset rate limits. Please try again.', 'error');
+        }
     }
🧹 Nitpick comments (2)
backend/app/db/repositories/replay_repository.py (1)

67-77: Consider a more concise list comprehension (optional).

The enum-based status filtering is correct. For slightly cleaner code, you could use a list comprehension.

🔎 Optional refactor using list comprehension
 async def delete_old_sessions(self, cutoff_time: str) -> int:
     """Delete old completed/failed/cancelled sessions"""
-    terminal_statuses = [
-        ReplayStatus.COMPLETED.value,
-        ReplayStatus.FAILED.value,
-        ReplayStatus.CANCELLED.value,
-    ]
+    terminal_statuses = [s.value for s in [ReplayStatus.COMPLETED, ReplayStatus.FAILED, ReplayStatus.CANCELLED]]
     result = await self.replay_collection.delete_many(
         {"created_at": {"$lt": cutoff_time}, "status": {"$in": terminal_statuses}}
     )
backend/tests/unit/infrastructure/mappers/test_dlq_mapper.py (1)

116-116: Stylistic: Consistent quote usage in patch decorators.

Changed patch path strings from single to double quotes. This is purely stylistic and has no functional impact. Consider using a formatter (like black or ruff format) to automatically maintain consistent quote styles across the codebase.

Also applies to: 140-140, 161-161, 175-175, 188-188, 201-201, 212-212

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1e40f7b and 50f79b8.

📒 Files selected for processing (11)
  • backend/app/api/routes/admin/events.py
  • backend/app/db/repositories/admin/admin_events_repository.py
  • backend/app/db/repositories/dlq_repository.py
  • backend/app/db/repositories/replay_repository.py
  • backend/app/db/repositories/saga_repository.py
  • backend/app/domain/events/query_builders.py
  • backend/app/infrastructure/mappers/saga_mapper.py
  • backend/app/services/admin/admin_events_service.py
  • backend/tests/unit/dlq/test_dlq_models.py
  • backend/tests/unit/infrastructure/mappers/test_dlq_mapper.py
  • frontend/src/routes/admin/AdminUsers.svelte
🚧 Files skipped from review as they are similar to previous changes (1)
  • backend/app/db/repositories/dlq_repository.py
🧰 Additional context used
🧬 Code graph analysis (7)
backend/app/db/repositories/admin/admin_events_repository.py (4)
backend/app/domain/events/event_models.py (4)
  • EventFields (13-45)
  • SortDirection (53-55)
  • EventBrowseResult (149-155)
  • EventExportRow (248-259)
backend/app/infrastructure/mappers/event_mapper.py (1)
  • EventFilterMapper (317-366)
backend/app/api/routes/admin/events.py (1)
  • export_events_csv (80-103)
backend/app/services/admin/admin_events_service.py (1)
  • export_events_csv (183-185)
backend/app/infrastructure/mappers/saga_mapper.py (1)
backend/app/domain/saga/models.py (1)
  • SagaFilter (29-38)
backend/app/api/routes/admin/events.py (1)
backend/app/services/admin/admin_events_service.py (2)
  • export_events_csv_content (187-217)
  • export_events_json_content (219-258)
backend/tests/unit/dlq/test_dlq_models.py (2)
backend/app/dlq/models.py (9)
  • AgeStatistics (180-185)
  • DLQFields (29-49)
  • DLQMessageFilter (113-118)
  • DLQMessageStatus (10-16)
  • DLQStatistics (189-196)
  • EventTypeStatistic (172-176)
  • RetryPolicy (122-158)
  • TopicStatistic (163-168)
  • event_type (93-95)
backend/app/infrastructure/mappers/dlq_mapper.py (2)
  • DLQMapper (22-239)
  • filter_to_query (231-239)
backend/tests/unit/infrastructure/mappers/test_dlq_mapper.py (2)
backend/app/infrastructure/mappers/dlq_mapper.py (1)
  • filter_to_query (231-239)
backend/app/dlq/models.py (2)
  • event_type (93-95)
  • DLQFields (29-49)
backend/app/db/repositories/saga_repository.py (2)
backend/app/domain/saga/models.py (3)
  • Saga (10-25)
  • SagaFilter (29-38)
  • SagaListResult (53-64)
backend/app/infrastructure/mappers/saga_mapper.py (3)
  • from_mongo (13-29)
  • from_mongo (120-149)
  • to_mongodb_query (217-247)
backend/app/db/repositories/replay_repository.py (1)
backend/app/domain/replay/models.py (2)
  • ReplaySessionState (85-102)
  • ReplayFilter (11-53)
🪛 GitHub Actions: MyPy Type Checking
backend/app/db/repositories/replay_repository.py

[error] 97-97: ReplayRepository.fetch_events signature does not accept a 'filter' keyword argument. This is likely causing the mypy error reported in the caller.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: E2E Tests
  • GitHub Check: Build Frontend
  • GitHub Check: Integration Tests
🔇 Additional comments (23)
backend/app/domain/events/query_builders.py (1)

54-56: Good refactor: Avoids shadowing the built-in format function.

Renaming the parameter from format to date_format is a good practice as it avoids shadowing Python's built-in format() function. The change is backward compatible since callers use the default value.

backend/app/api/routes/admin/events.py (3)

45-51: LGTM: Parameter naming aligned with service layer.

The keyword argument event_filter=event_filter correctly matches the service method signature browse_events(*, event_filter: EventFilter, ...).


95-95: LGTM: Consistent parameter naming.


131-131: LGTM: Consistent parameter naming.

backend/app/db/repositories/admin/admin_events_repository.py (2)

59-68: LGTM: Parameter rename improves clarity and avoids shadowing built-in filter.

The rename from filter to event_filter is more descriptive and avoids shadowing Python's built-in filter() function. The internal usage at line 68 is correctly updated.


194-196: LGTM: Consistent parameter naming with browse_events.

backend/app/services/admin/admin_events_service.py (3)

59-70: LGTM: Service method correctly propagates the renamed parameter.

The parameter rename and repository call are consistent with the repository layer changes.


183-188: LGTM: Consistent parameter naming across export methods.

Both export_events_csv and export_events_csv_content correctly use the renamed event_filter parameter.


219-244: LGTM: Complete and consistent parameter rename in JSON export.

The method signature, repository call, and all metadata field accesses (lines 237-243) correctly reference event_filter. The coordinated refactor across routes, service, and repository layers is complete.

backend/app/infrastructure/mappers/saga_mapper.py (1)

217-247: LGTM - Excellent type safety and naming improvements!

The changes improve code quality in several ways:

  • Renaming filtersaga_filter avoids shadowing Python's built-in filter function
  • Making the parameter nullable (SagaFilter | None) adds defensive flexibility
  • Early return for None case is clean and efficient
  • All field accesses include appropriate None checks
  • Proper use of saga_filter.state.value for enum-to-string conversion
backend/app/db/repositories/saga_repository.py (4)

49-56: LGTM - Proper enum type usage!

The explicit SagaState | None typing and use of state.value for the MongoDB query ensures type safety and correct enum-to-string conversion.


58-70: LGTM - Consistent parameter naming!

The parameter renaming from filter to saga_filter maintains consistency with the mapper and avoids shadowing Python's built-in filter function. The non-nullable type here is appropriate since this method requires a filter, while the mapper defensively accepts nullable values.


72-80: LGTM - Type-safe state updates!

Explicit SagaState typing and use of state.value ensures type safety and correct enum-to-string conversion for MongoDB updates.


111-136: LGTM - Clean optional filtering!

The nullable saga_filter parameter with conditional query construction is a clean pattern for optional filtering. The parameter renaming maintains consistency across the codebase.

frontend/src/routes/admin/AdminUsers.svelte (1)

180-200: Good fix: error handling added to openRateLimitModal.

The try-catch-finally block properly handles errors when loading rate limits. The catch block closes the modal and shows user feedback, and the finally block ensures the loading state is always reset. This addresses the issue from the previous review.

backend/app/db/repositories/replay_repository.py (4)

8-8: LGTM!

The import of ReplayStatus enables type-safe enum usage throughout the repository methods.


92-95: Parameter rename is good and safe.

Renaming the parameter from filter to replay_filter avoids shadowing the Python builtin and improves clarity. All callers use positional arguments (count_events(ReplayFilter()) and count_events(session.config.filter)), so the rename introduces no breaking changes.


62-65: All callers correctly use ReplayStatus enum values.

The method signature and implementation are correct, and all callers in replay_service.py and the test file pass ReplayStatus enum values (e.g., ReplayStatus.RUNNING, ReplayStatus.PAUSED, ReplayStatus.CANCELLED).


45-60: Type-safe refactoring is complete and well-integrated.

The method correctly accepts ReplayStatus enum instead of raw strings, improving type safety. All callers—including the API endpoint, service wrappers, and tests—are properly updated. FastAPI's automatic enum deserialization on the Query parameter ensures type safety at the API boundary, and the implementation correctly uses status.value for MongoDB queries.

backend/tests/unit/dlq/test_dlq_models.py (2)

2-2: LGTM! Necessary imports for enum-based testing.

The addition of timezone, DLQStatistics, and EventType imports properly supports the test updates that migrate from string literals to type-safe enum values.

Also applies to: 9-9, 15-15


98-98: LGTM! Enum-based filtering improves type safety.

The migration from string literal "execution_requested" to EventType.EXECUTION_REQUESTED enum aligns with the domain model changes and provides better type safety, IDE support, and prevents typos.

Also applies to: 102-102

backend/tests/unit/infrastructure/mappers/test_dlq_mapper.py (2)

17-21: LGTM! Necessary import additions.

The EventType import addition supports enum-based filtering in the test assertions. Minor import reorganization has no functional impact.


403-403: LGTM! Type-safe enum usage in filter tests.

Replacing string literal with EventType.EXECUTION_REQUESTED enum improves type safety and aligns with the domain model's migration to enum-based event typing. The test correctly verifies that the mapper preserves enum values in query construction.

Also applies to: 410-410

@sonarqubecloud
Copy link

@HardMax71 HardMax71 merged commit 0b1ef49 into main Dec 26, 2025
17 checks passed
@HardMax71 HardMax71 deleted the frontend-extra-tests branch December 26, 2025 21:02
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.

3 participants