Skip to content

fix: improve provider setup in wizard, subscription auth, dashboard bugs#914

Merged
Aureliolo merged 8 commits intomainfrom
fix/improve-provider-setup-in-wizard
Mar 29, 2026
Merged

fix: improve provider setup in wizard, subscription auth, dashboard bugs#914
Aureliolo merged 8 commits intomainfrom
fix/improve-provider-setup-in-wizard

Conversation

@Aureliolo
Copy link
Copy Markdown
Owner

Summary

  • Provider form: Replace right-side drawer with centered modal for better visibility; enlarge preset tiles and fix text cutoff (line-clamp-2)
  • Subscription auth: Pass token as LiteLLM auth_token kwarg instead of extra_headers so Anthropic handler picks it up correctly (fixes "Missing Anthropic API Key" error)
  • LiteLLM model database: Dynamically populate provider models from litellm.model_cost with pricing and context data, filtered to 4.5+ generation models. Falls back to preset default_models when unavailable. Anthropic now gets 5 models (Opus 4.6, Sonnet 4.6, Opus 4.5, Sonnet 4.5, Haiku 4.5) instead of 2
  • NaN% gauge: Fix NaN% in Org Health by filtering non-finite health_percent values in computeOrgHealth + NaN guard in ProgressGauge
  • Budget Burn chart: Fix "Today" label cutoff (increase top margin), replace hardcoded $ Y-axis with currency-aware formatCurrencyCompact using configured currency
  • Theme preview: Replace dark/light toggle with 5 named color palettes (warm-ops, ice-station, stealth, signal, neon), add animation demo with cycling card, add mini sidebar mockup
  • Test connection timeout: Increase to 120s for local providers that need model loading time (Ollama)
  • Accessibility: Focus trap in centered modal dialog (Tab cycling within panel)
  • Logging: Event constants for LiteLLM model lookup, WARNING-level for import failures, zero-match logging
  • Docs: Update operations.md with LiteLLM model_cost as model source and subscription auth_token description

Test plan

  • uv run ruff check src/ tests/ -- lint passes
  • uv run mypy src/ tests/ -- type-check passes
  • uv run python -m pytest tests/ -n auto -- 11035 passed
  • npm --prefix web run lint -- zero warnings
  • npm --prefix web run type-check -- passes
  • npm --prefix web run test -- 2075 passed
  • Manual: setup wizard provider step shows centered modal with larger tiles
  • Manual: Anthropic subscription auth connects without "Missing API Key" error
  • Manual: theme preview changes color when switching palettes
  • Manual: dashboard Org Health shows 0% or N/A (not NaN%)
  • Manual: Budget Burn Y-axis shows EUR, "Today" label visible

Pre-reviewed by 4 agents (code-reviewer, frontend-reviewer, silent-failure-hunter, docs-consistency), 10 findings addressed.

🤖 Generated with Claude Code

Aureliolo and others added 2 commits March 29, 2026 14:41
- Replace provider form right-side drawer with centered modal for better
  visibility; enlarge preset tiles and fix text cutoff (line-clamp-2)
- Fix subscription auth: pass token as LiteLLM auth_token kwarg instead
  of extra_headers so Anthropic handler picks it up correctly
- Populate provider models from litellm.model_cost database instead of
  hardcoded presets; filters to 4.5+ generation models, falls back to
  preset defaults when litellm unavailable
- Fix NaN% in Org Health gauge by filtering non-finite health_percent
  values in computeOrgHealth and adding NaN guard in ProgressGauge
- Fix "Today" label cutoff in Budget Burn chart (increase top margin)
- Fix Y-axis currency: replace hardcoded $ with formatCurrencyCompact
  using the configured currency prop
- Theme preview: add palette switching (5 named palettes instead of
  dark/light), animation demo with cycling card, mini sidebar mockup
- Increase test-connection timeout to 120s for local providers that
  need model loading time (Ollama)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pre-reviewed by 4 agents, 10 findings addressed:

- Add Tab focus trap to centered modal (accessibility regression fix)
- Replace raw string log events with constants from events/provider.py
- Move vendor-specific MODEL_VERSION_FILTERS to presets.py (allowed zone)
- Upgrade ImportError log from DEBUG to WARNING (litellm is required dep)
- Add zero-match logging for litellm model lookup
- Add error logging to formatCurrencyCompact catch block
- Remove dead isRail ternary in SidebarPreview
- Add console.warn for non-finite health_percent filtering
- Update docs/design/operations.md: LiteLLM model_cost as model source
- Update docs/design/operations.md: subscription auth_token description

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 29, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 77e29a0a-7d24-4976-ad0b-7a6220d1f18c

📥 Commits

Reviewing files that changed from the base of the PR and between 855c130 and 7caa08f.

📒 Files selected for processing (1)
  • web/src/__tests__/utils/format.test.ts
📜 Recent review details
⏰ 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). (7)
  • GitHub Check: Test (Python 3.14)
  • GitHub Check: Dashboard Test
  • GitHub Check: Build Web
  • GitHub Check: Build Sandbox
  • GitHub Check: Build Backend
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (4)
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

web/src/**/*.{ts,tsx}: Web Dashboard: ALWAYS reuse existing components from web/src/components/ui/ before creating new ones. Reuse StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton variants, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup, Drawer, InputField, SelectField, SliderField, ToggleField, TaskStatusIndicator, PriorityBadge, ProviderHealthBadge, TokenUsageBar, CodeMirrorEditor, SegmentedControl, ThemeToggle.
Do NOT recreate status dots inline—use <StatusBadge>. Do NOT build card-with-header layouts from scratch—use <SectionCard>. Do NOT create metric displays with text-metric font-bold—use <MetricCard>. Do NOT render initials circles manually—use <Avatar>. Do NOT create complex (>8 line) JSX inside .map()—extract to a shared component.
Web design system enforcement: A PostToolUse hook (scripts/check_web_design_system.py) runs automatically on every Edit/Write to web/src/ files. It catches hardcoded hex colors, rgba values, font-family declarations, missing Storybook stories, duplicate patterns, and complex map blocks. Fix all violations before proceeding.
React 19 + shadcn/ui + Tailwind CSS 4 dashboard: Use React Router, Zustand stores, @tanstack/react-query, @xyflow/react, @dagrejs/dagre, d3-force, @dnd-kit, Recharts, Framer Motion, cmdk, js-yaml, Axios.

Files:

  • web/src/__tests__/utils/format.test.ts
web/src/**/*.{ts,tsx,css}

📄 CodeRabbit inference engine (CLAUDE.md)

Design tokens: Colors use Tailwind semantic classes (text-foreground, bg-card, text-accent, etc.) or CSS variables (var(--so-accent)). NEVER hardcode hex values. Typography uses font-sans or font-mono. Spacing uses density-aware tokens or standard Tailwind. Shadows/borders use token variables.

Files:

  • web/src/__tests__/utils/format.test.ts
**/*.{ts,tsx,json}

📄 CodeRabbit inference engine (CLAUDE.md)

TypeScript 6.0: Remove baseUrl (deprecated); paths entries are relative to tsconfig directory. esModuleInterop always true—cannot be set to false. types defaults to []—must explicitly list (e.g. 'types': ['vitest/globals']). DOM.Iterable merged into DOM. moduleResolution: 'classic' and 'node10' removed—use 'bundler' or 'nodenext'. strict defaults to true. noUncheckedSideEffectImports defaults to true.

Files:

  • web/src/__tests__/utils/format.test.ts
**/*.{py,ts,tsx,go}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{py,ts,tsx,go}: Lint & format: Python via ruff (uv run ruff check + uv run ruff format). TypeScript web via ESLint (zero warnings enforced: npm --prefix web run lint). Go CLI via golangci-lint and go vet. All enforced in pre-commit hooks.
Type checking: Python via mypy strict mode (uv run mypy src/ tests/). TypeScript via npm --prefix web run type-check. Go via go -C cli vet ./....

Files:

  • web/src/__tests__/utils/format.test.ts
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/providers/**/*.py : Providers: LLM provider abstraction (LiteLLM adapter), auth types (api_key/oauth/custom_header/none), presets (PROVIDER_PRESETS), runtime CRUD (ProviderManagementService with asyncio.Lock serialization), hot-reload via AppState swap.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to src/synthorg/providers/**/*.py : Providers: LLM provider abstraction, presets, model auto-discovery, runtime CRUD management, provider families, discovery SSRF allowlist, health tracking. Apply retry + rate limiting automatically via `BaseCompletionProvider`.
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to web/src/__tests__/**/*.{ts,js} : Dashboard testing: Vitest unit tests organized by feature under `web/src/__tests__/`. Use fast-check for property-based testing (`fc.assert` + `fc.property`).

Applied to files:

  • web/src/__tests__/utils/format.test.ts
🔇 Additional comments (2)
web/src/__tests__/utils/format.test.ts (2)

5-5: Import is correctly wired.

formatCurrencyCompact is imported and used by the new test block.


186-227: Coverage improvements look solid.

The new suite validates normalization, fallback, compact notation, and non-finite handling in a focused way.


Walkthrough

Adds LiteLLM model-database integration: new models_from_litellm parses litellm.model_cost entries with provider version filters, deduplication, cost and context extraction, and fallback to preset defaults. Updates LiteLLM auth handling to pass subscription tokens as auth_token. Adds three observability event constants for LiteLLM lookup. Provider creation now prefers LiteLLM-discovered models when available. Frontend changes: provider form switched from drawer to modal, new theme palettes and preview animation, compact currency formatter, progress gauge and dashboard health handling for non-finite numbers, various UI layout/density tweaks, and corresponding test updates.

Suggested labels

autorelease: tagged

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title addresses multiple key changes: provider setup modal improvement, subscription auth fix, and dashboard bug fixes, which align with the main changes in the changeset.
Description check ✅ Passed The description comprehensively covers the major changes across frontend, backend, logging, and docs, with explicit mentions of provider form, subscription auth, LiteLLM model database, NaN guards, budget chart, theme preview, and documentation updates.
Docstring Coverage ✅ Passed Docstring coverage is 59.18% which is sufficient. The required threshold is 40.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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

@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 29, 2026 13:20 — with GitHub Actions Inactive
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 29, 2026

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Snapshot Warnings

⚠️: No snapshots were found for the head SHA 7caa08f.
Ensure that dependencies are being submitted on PR branches. Re-running this action after a short time may resolve the issue. See the documentation for more information and troubleshooting advice.

Scanned Files

None

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request implements dynamic model metadata discovery using the LiteLLM model database, enabling automatic population of pricing and context window information during provider creation. It also updates the subscription authentication mechanism to utilize LiteLLM's native auth_token parameter. On the frontend, the provider configuration drawer has been refactored into a custom portal-based dialog with enhanced focus management and animations, and the theme selection system now includes additional palettes and a more comprehensive preview component. Feedback was provided regarding the use of magic numbers for default context window values in the model lookup utility.

Comment on lines +297 to +303
max_input = info.get("max_input_tokens", 200_000)

seen[base_name] = ProviderModelConfig(
id=model_id,
cost_per_1k_input=round(input_cost * 1000, 6),
cost_per_1k_output=round(output_cost * 1000, 6),
max_context=max_input if isinstance(max_input, int) else 200_000,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The magic number 200_000 is repeated on lines 297 and 303. To improve readability and maintainability, consider defining it as a module-level constant.

For example, you could add this near the top of the file:

_DEFAULT_MAX_CONTEXT = 200_000

And then use it in this function:

max_input = info.get("max_input_tokens", _DEFAULT_MAX_CONTEXT)
# ...
max_context=max_input if isinstance(max_input, int) else _DEFAULT_MAX_CONTEXT

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 29, 2026

Codecov Report

❌ Patch coverage is 81.81818% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.15%. Comparing base (81253d9) to head (7caa08f).
⚠️ Report is 1 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/synthorg/providers/management/_helpers.py 81.81% 4 Missing ⚠️
src/synthorg/providers/management/service.py 50.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #914      +/-   ##
==========================================
- Coverage   92.17%   92.15%   -0.02%     
==========================================
  Files         596      596              
  Lines       31508    31536      +28     
  Branches     3055     3058       +3     
==========================================
+ Hits        29043    29063      +20     
- Misses       1943     1950       +7     
- Partials      522      523       +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown

@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 (1)
web/src/pages/providers/ProviderFormDrawer.tsx (1)

148-188: 🧹 Nitpick | 🔵 Trivial

Consider refactoring render-phase state updates to useEffect.

This synchronization pattern with refs and conditional setState during render works but is unconventional and can be harder to reason about. Consider moving these to useEffect with appropriate dependencies for clearer separation of concerns.

♻️ Example refactor for edit-mode prefill
-  // Clear credentials when switching to edit mode
-  if (open && mode === 'edit' && (openChanged || modeChanged || providerChanged)) {
-    setSelectedPreset(null)
-    setApiKey('')
-    setSubscriptionToken('')
-  }
-
-  // Pre-fill in edit mode (also fires on reopen with same provider)
-  if (open && mode === 'edit' && provider && (openChanged || modeChanged || providerChanged)) {
-    setName(provider.name)
-    setAuthType(provider.auth_type)
-    setBaseUrl(provider.base_url ?? '')
-    setLitellmProvider(provider.litellm_provider ?? '')
-    setTosAccepted(provider.tos_accepted_at !== null)
-  }
+  // Sync form state when opening in edit mode
+  useEffect(() => {
+    if (open && mode === 'edit') {
+      setSelectedPreset(null)
+      setApiKey('')
+      setSubscriptionToken('')
+      if (provider) {
+        setName(provider.name)
+        setAuthType(provider.auth_type)
+        setBaseUrl(provider.base_url ?? '')
+        setLitellmProvider(provider.litellm_provider ?? '')
+        setTosAccepted(provider.tos_accepted_at !== null)
+      }
+    }
+  }, [open, mode, provider])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/providers/ProviderFormDrawer.tsx` around lines 148 - 188, The
component is updating state during render using refs and conditional checks
(e.g., prevModeRef, prevProviderRef, prevOpenRef, prevSelectedPresetRef) which
should be moved into useEffect hooks; create a useEffect that depends on [open,
mode, provider] to perform the edit-mode prefill/reset calls (setName,
setAuthType, setBaseUrl, setLitellmProvider, setTosAccepted, setApiKey,
setSubscriptionToken, setSelectedPreset) and another useEffect that depends on
[selectedPreset, preset] to handle the preset selection branch (reset to
defaults for '__custom__' or fill from preset), and remove the render-phase
conditional setState and prev refs or update those refs inside the same effects
after applying state so behavior remains identical.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/synthorg/providers/management/_helpers.py`:
- Around line 246-255: Docstring and inline examples in the _helpers.py snippet
use real vendor/model names which violate the project's rule; update the
examples to neutral placeholders instead. Edit the docstring mentioning
provider-specific filters (presets.MODEL_VERSION_FILTERS) and any example usage
of litellm_provider to replace concrete names like "anthropic", "openai", or
model examples (e.g. "claude-opus-4-6") with neutral tokens such as
"example-provider" and "example-large-001" (and similarly replace dated variants
with "example-large-001-20260101" if needed) so all references in the
_helpers.py documentation and examples are vendor-agnostic. Ensure both places
flagged (around the docstring and the example lines referenced) are updated
consistently.
- Around line 239-319: The models_from_litellm function is over the 50-line
limit and mixes concerns; split it into small helpers such as a litellm
import/lookup wrapper, a row-parsing helper (e.g. _parse_litellm_row(info,
model_name, litellm_provider) that returns a ProviderModelConfig or None), and a
dedupe/collect helper (e.g. _collect_models_from_litellm(model_cost_dict,
litellm_provider, version_filter) that builds the seen dict). Update
models_from_litellm to only import litellm, fetch MODEL_VERSION_FILTERS, call
_collect_models_from_litellm, sort/log results and return; ensure the new
_parse_litellm_row handles type checks, prefix stripping
(model_name.removeprefix), version_filter application, cost rounding, and
max_context fallback exactly as current logic so behavior and logs
(PROVIDER_LITELLM_LOOKUP_SKIPPED, PROVIDER_LITELLM_MODELS_LOADED/EMPTY) remain
unchanged.

In `@src/synthorg/providers/management/service.py`:
- Around line 391-396: The code eagerly calls
models_from_litellm(preset.litellm_provider) even when request.models is
provided; change the logic so you only call models_from_litellm when
request.models is None: if request.models is not None, set models =
request.models; otherwise call models_from_litellm(...) and fall back to
preset.default_models (preserving the existing litellm_models or default_models
fallback). Update the assignment around litellm_models/models to avoid
performing the LiteLLM lookup unnecessarily.

In `@src/synthorg/providers/presets.py`:
- Around line 308-319: MODEL_VERSION_FILTERS in presets.py contains real
vendor/model identifiers; replace them with policy-compliant generic identifiers
or move the mapping to an external/runtime config. Specifically, change the
mapping key "anthropic" to a neutral name (e.g., "provider_x") and replace the
regex r"^claude-(opus|sonnet|haiku)-4-[56789]" with a generic pattern (e.g.,
r"^model-(name1|name2|name3)-4-[0-9]+") or remove the hard-coded mapping and
load it from an approved external source at runtime; update any call sites such
as models_from_litellm() that expect the old key to use the new generic key or
configuration location so behavior remains consistent.

In `@web/src/components/ui/progress-gauge.tsx`:
- Around line 30-33: The computed percentage can still become NaN if max is
non-finite; change safeMax to sanitize max first (e.g., use Number.isFinite(max)
? Math.max(max, 1) : 1) so safeMax is always a finite >=1 number, then keep
safeValue, clampedValue, and percentage as-is; update the declaration of safeMax
to use this finite-check and fallback value to prevent NaN in percentage.

In `@web/src/pages/setup/ThemePreview.tsx`:
- Around line 53-66: The SIDEBAR_NAV .map() block in ThemePreview.tsx is over 8
lines and should be extracted into a new presentational component (e.g.,
SidebarNavItem). Create SidebarNavItem(props: {icon: IconType, label: string,
isCompact: boolean}) that returns the div with the cn(...) classes (preserving
the label === 'Overview' conditional), renders <Icon className="size-3.5
shrink-0" /> and conditionally the <span className="truncate
text-[9px]">{label}</span>, then replace the map body with a single
<SidebarNavItem key={label} icon={Icon} label={label} isCompact={isCompact} />
to keep keys and behavior identical.
- Around line 47-51: Replace the non-existent design token classes with the
correct Tailwind utilities: in the ThemePreview component update className usage
replacing bg-bg-surface with bg-surface and text-text-muted with
text-muted-foreground (keep text-text-secondary as-is); apply the same
replacements in ProviderDetailHeader, ProviderCard, and PresetPicker wherever
bg-bg-surface or text-text-muted are used so all components reference bg-surface
and text-muted-foreground instead of the invalid tokens.

---

Outside diff comments:
In `@web/src/pages/providers/ProviderFormDrawer.tsx`:
- Around line 148-188: The component is updating state during render using refs
and conditional checks (e.g., prevModeRef, prevProviderRef, prevOpenRef,
prevSelectedPresetRef) which should be moved into useEffect hooks; create a
useEffect that depends on [open, mode, provider] to perform the edit-mode
prefill/reset calls (setName, setAuthType, setBaseUrl, setLitellmProvider,
setTosAccepted, setApiKey, setSubscriptionToken, setSelectedPreset) and another
useEffect that depends on [selectedPreset, preset] to handle the preset
selection branch (reset to defaults for '__custom__' or fill from preset), and
remove the render-phase conditional setState and prev refs or update those refs
inside the same effects after applying state so behavior remains identical.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: cad76c06-3a29-417f-a60d-ac6c8ec98ce4

📥 Commits

Reviewing files that changed from the base of the PR and between 81253d9 and 44dc453.

📒 Files selected for processing (19)
  • docs/design/operations.md
  • src/synthorg/observability/events/provider.py
  • src/synthorg/providers/drivers/litellm_driver.py
  • src/synthorg/providers/management/_helpers.py
  • src/synthorg/providers/management/service.py
  • src/synthorg/providers/presets.py
  • tests/unit/providers/drivers/test_litellm_auth.py
  • tests/unit/providers/management/test_service_allowlist.py
  • tests/unit/providers/management/test_service_discovery.py
  • web/src/api/endpoints/providers.ts
  • web/src/components/ui/progress-gauge.tsx
  • web/src/pages/dashboard/BudgetBurnChart.tsx
  • web/src/pages/providers/PresetPicker.tsx
  • web/src/pages/providers/ProviderFormDrawer.tsx
  • web/src/pages/setup/ThemePreview.tsx
  • web/src/pages/setup/ThemeStep.tsx
  • web/src/stores/setup-wizard.ts
  • web/src/utils/dashboard.ts
  • web/src/utils/format.ts
📜 Review details
⏰ 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). (7)
  • GitHub Check: Dashboard Test
  • GitHub Check: Test (Python 3.14)
  • GitHub Check: Build Sandbox
  • GitHub Check: Build Web
  • GitHub Check: Build Backend
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (13)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Python version 3.14+ with PEP 649 native lazy annotations; use PEP 758 except syntax (except A, B: without parentheses). Do NOT use from __future__ import annotations.
Type hints: all public functions must have type hints. Use mypy strict mode for type checking.
Docstrings: Google style is required on all public classes and functions. Enforced by ruff D rules.
Immutability: create new objects, never mutate existing ones. For non-Pydantic internal collections (registries, BaseTool), use copy.deepcopy() at construction + MappingProxyType wrapping for read-only enforcement.
For dict/list fields in frozen Pydantic models, rely on frozen=True for field reassignment prevention and copy.deepcopy() at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, serializing for persistence).
Config vs runtime state: use frozen Pydantic models for config/identity; use separate mutable-via-copy models (using model_copy(update=...)) for runtime state that evolves. Never mix static config fields with mutable runtime fields in one model.
Pydantic v2: use BaseModel, model_validator, computed_field, ConfigDict. Adopt conventions: use allow_inf_nan=False in all ConfigDict declarations to reject NaN/Inf; use @computed_field for derived values; use NotBlankStr for all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators.
Async concurrency: prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code (e.g. multiple tool invocations, parallel agent calls). Prefer structured concurrency over bare create_task.
Line length: 88 characters enforced by ruff.
Functions must be < 50 lines, files < 800 lines.
Handle errors explicitly, never silently swallow. Validate at system boundaries (user input, external APIs, config files).
NEVER use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, tests, or co...

Files:

  • tests/unit/providers/management/test_service_allowlist.py
  • tests/unit/providers/drivers/test_litellm_auth.py
  • src/synthorg/providers/management/service.py
  • src/synthorg/observability/events/provider.py
  • src/synthorg/providers/drivers/litellm_driver.py
  • src/synthorg/providers/presets.py
  • src/synthorg/providers/management/_helpers.py
  • tests/unit/providers/management/test_service_discovery.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Test markers: @pytest.mark.unit, @pytest.mark.integration, @pytest.mark.e2e, @pytest.mark.slow. Coverage: 80% minimum enforced in CI.
Async testing: asyncio_mode = 'auto' in pyproject.toml—no manual @pytest.mark.asyncio needed.
Test timeout: 30 seconds per test (global in pyproject.toml)—do not add per-file pytest.mark.timeout(30) markers; non-default overrides like timeout(60) are allowed.
Prefer @pytest.mark.parametrize for testing similar cases in Python tests.
Property-based testing: Python uses Hypothesis (@given + @settings). Profiles: ci (50 examples, default) and dev (1000 examples). Run dev profile: HYPOTHESIS_PROFILE=dev uv run python -m pytest tests/ -m unit -n auto -k properties.
NEVER skip, dismiss, or ignore flaky tests—always fix them fully and fundamentally. For timing-sensitive tests, mock time.monotonic() and asyncio.sleep(). For tasks that must block indefinitely, use asyncio.Event().wait() instead of asyncio.sleep(large_number).
Testing: Python pytest with markers (@pytest.mark.unit, @pytest.mark.integration, @pytest.mark.e2e) and coverage >=80%. Web dashboard Vitest with coverage scoped to files changed vs origin/main. Go go -C cli test ./....

Files:

  • tests/unit/providers/management/test_service_allowlist.py
  • tests/unit/providers/drivers/test_litellm_auth.py
  • tests/unit/providers/management/test_service_discovery.py
**/*.{py,ts,tsx,go}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{py,ts,tsx,go}: Lint & format: Python via ruff (uv run ruff check + uv run ruff format). TypeScript web via ESLint (zero warnings enforced: npm --prefix web run lint). Go CLI via golangci-lint and go vet. All enforced in pre-commit hooks.
Type checking: Python via mypy strict mode (uv run mypy src/ tests/). TypeScript via npm --prefix web run type-check. Go via go -C cli vet ./....

Files:

  • tests/unit/providers/management/test_service_allowlist.py
  • web/src/api/endpoints/providers.ts
  • web/src/components/ui/progress-gauge.tsx
  • web/src/pages/dashboard/BudgetBurnChart.tsx
  • web/src/utils/dashboard.ts
  • web/src/pages/providers/PresetPicker.tsx
  • tests/unit/providers/drivers/test_litellm_auth.py
  • src/synthorg/providers/management/service.py
  • web/src/pages/setup/ThemeStep.tsx
  • src/synthorg/observability/events/provider.py
  • web/src/utils/format.ts
  • web/src/stores/setup-wizard.ts
  • src/synthorg/providers/drivers/litellm_driver.py
  • src/synthorg/providers/presets.py
  • web/src/pages/providers/ProviderFormDrawer.tsx
  • src/synthorg/providers/management/_helpers.py
  • tests/unit/providers/management/test_service_discovery.py
  • web/src/pages/setup/ThemePreview.tsx
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

web/src/**/*.{ts,tsx}: Web Dashboard: ALWAYS reuse existing components from web/src/components/ui/ before creating new ones. Reuse StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton variants, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup, Drawer, InputField, SelectField, SliderField, ToggleField, TaskStatusIndicator, PriorityBadge, ProviderHealthBadge, TokenUsageBar, CodeMirrorEditor, SegmentedControl, ThemeToggle.
Do NOT recreate status dots inline—use <StatusBadge>. Do NOT build card-with-header layouts from scratch—use <SectionCard>. Do NOT create metric displays with text-metric font-bold—use <MetricCard>. Do NOT render initials circles manually—use <Avatar>. Do NOT create complex (>8 line) JSX inside .map()—extract to a shared component.
Web design system enforcement: A PostToolUse hook (scripts/check_web_design_system.py) runs automatically on every Edit/Write to web/src/ files. It catches hardcoded hex colors, rgba values, font-family declarations, missing Storybook stories, duplicate patterns, and complex map blocks. Fix all violations before proceeding.
React 19 + shadcn/ui + Tailwind CSS 4 dashboard: Use React Router, Zustand stores, @tanstack/react-query, @xyflow/react, @dagrejs/dagre, d3-force, @dnd-kit, Recharts, Framer Motion, cmdk, js-yaml, Axios.

Files:

  • web/src/api/endpoints/providers.ts
  • web/src/components/ui/progress-gauge.tsx
  • web/src/pages/dashboard/BudgetBurnChart.tsx
  • web/src/utils/dashboard.ts
  • web/src/pages/providers/PresetPicker.tsx
  • web/src/pages/setup/ThemeStep.tsx
  • web/src/utils/format.ts
  • web/src/stores/setup-wizard.ts
  • web/src/pages/providers/ProviderFormDrawer.tsx
  • web/src/pages/setup/ThemePreview.tsx
web/src/**/*.{ts,tsx,css}

📄 CodeRabbit inference engine (CLAUDE.md)

Design tokens: Colors use Tailwind semantic classes (text-foreground, bg-card, text-accent, etc.) or CSS variables (var(--so-accent)). NEVER hardcode hex values. Typography uses font-sans or font-mono. Spacing uses density-aware tokens or standard Tailwind. Shadows/borders use token variables.

Files:

  • web/src/api/endpoints/providers.ts
  • web/src/components/ui/progress-gauge.tsx
  • web/src/pages/dashboard/BudgetBurnChart.tsx
  • web/src/utils/dashboard.ts
  • web/src/pages/providers/PresetPicker.tsx
  • web/src/pages/setup/ThemeStep.tsx
  • web/src/utils/format.ts
  • web/src/stores/setup-wizard.ts
  • web/src/pages/providers/ProviderFormDrawer.tsx
  • web/src/pages/setup/ThemePreview.tsx
**/*.{ts,tsx,json}

📄 CodeRabbit inference engine (CLAUDE.md)

TypeScript 6.0: Remove baseUrl (deprecated); paths entries are relative to tsconfig directory. esModuleInterop always true—cannot be set to false. types defaults to []—must explicitly list (e.g. 'types': ['vitest/globals']). DOM.Iterable merged into DOM. moduleResolution: 'classic' and 'node10' removed—use 'bundler' or 'nodenext'. strict defaults to true. noUncheckedSideEffectImports defaults to true.

Files:

  • web/src/api/endpoints/providers.ts
  • web/src/components/ui/progress-gauge.tsx
  • web/src/pages/dashboard/BudgetBurnChart.tsx
  • web/src/utils/dashboard.ts
  • web/src/pages/providers/PresetPicker.tsx
  • web/src/pages/setup/ThemeStep.tsx
  • web/src/utils/format.ts
  • web/src/stores/setup-wizard.ts
  • web/src/pages/providers/ProviderFormDrawer.tsx
  • web/src/pages/setup/ThemePreview.tsx
web/src/components/ui/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Web components: Place new shared components in web/src/components/ui/ with kebab-case filenames. Create .stories.tsx with all states. Export props as TypeScript interface. Use design tokens exclusively—no hardcoded colors, fonts, or spacing. Import cn from @/lib/utils for conditional class merging.

Files:

  • web/src/components/ui/progress-gauge.tsx
web/src/pages/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Web dashboard pages: Lazy-loaded page components (one per route). Page-scoped sub-components in pages/<page-name>/ subdirs. Feature dirs added as pages are built. Stores for auth, WebSocket, toast, analytics, setup wizard, company, agents, budget, tasks, settings, providers, theme.

Files:

  • web/src/pages/dashboard/BudgetBurnChart.tsx
  • web/src/pages/providers/PresetPicker.tsx
  • web/src/pages/setup/ThemeStep.tsx
  • web/src/pages/providers/ProviderFormDrawer.tsx
  • web/src/pages/setup/ThemePreview.tsx
src/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.py: Every module with business logic MUST have: from synthorg.observability import get_logger then logger = get_logger(__name__). Never use import logging / logging.getLogger() / print() in application code.
Always use constants from domain-specific modules under synthorg.observability.events (e.g., API_REQUEST_STARTED from events.api). Import directly and use structured kwargs: logger.info(EVENT, key=value). Never use logger.info('msg %s', val).
All error paths must log at WARNING or ERROR with context before raising. All state transitions must log at INFO. DEBUG for object creation, internal flow, entry/exit of key functions.
Pure data models, enums, and re-exports do NOT need logging.

Files:

  • src/synthorg/providers/management/service.py
  • src/synthorg/observability/events/provider.py
  • src/synthorg/providers/drivers/litellm_driver.py
  • src/synthorg/providers/presets.py
  • src/synthorg/providers/management/_helpers.py
src/synthorg/providers/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/synthorg/providers/**/*.py: All provider calls go through BaseCompletionProvider which applies retry + rate limiting automatically. Never implement retry logic in driver subclasses or calling code.
RetryConfig and RateLimiterConfig are set per-provider in ProviderConfig. Retryable errors: RateLimitError, ProviderTimeoutError, ProviderConnectionError, ProviderInternalError. Non-retryable errors raise immediately. RetryExhaustedError signals all retries failed.
Rate limiter respects RateLimitError.retry_after from providers—automatically pauses future requests.
Providers: LLM provider abstraction, presets, model auto-discovery, runtime CRUD management, provider families, discovery SSRF allowlist, health tracking. Apply retry + rate limiting automatically via BaseCompletionProvider.

Files:

  • src/synthorg/providers/management/service.py
  • src/synthorg/providers/drivers/litellm_driver.py
  • src/synthorg/providers/presets.py
  • src/synthorg/providers/management/_helpers.py
docs/design/*.md

📄 CodeRabbit inference engine (CLAUDE.md)

When approved deviations occur, update the relevant docs/design/ page to reflect the new reality.

Files:

  • docs/design/operations.md
docs/**/*.md

📄 CodeRabbit inference engine (CLAUDE.md)

Documentation: docs/ (Markdown, built with Zensical, config: mkdocs.yml). Design spec in docs/design/ (11 pages), architecture in docs/architecture/, roadmap in docs/roadmap/. REST API reference auto-generated by scripts/export_openapi.py. Library reference auto-generated via mkdocstrings + Griffe. Landing page in site/ (Astro).

Files:

  • docs/design/operations.md
src/synthorg/observability/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Observability: Structured logging, correlation tracking, redaction, third-party logger taming, events. Use get_logger(__name__) and event constants from synthorg.observability.events.*.

Files:

  • src/synthorg/observability/events/provider.py
🧠 Learnings (28)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/providers/**/*.py : Providers: LLM provider abstraction (LiteLLM adapter), auth types (api_key/oauth/custom_header/none), presets (PROVIDER_PRESETS), runtime CRUD (ProviderManagementService with asyncio.Lock serialization), hot-reload via AppState swap.
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/**/*.{ts,tsx} : React 19 + shadcn/ui + Tailwind CSS 4 dashboard: Use React Router, Zustand stores, tanstack/react-query, xyflow/react, dagrejs/dagre, d3-force, dnd-kit, Recharts, Framer Motion, cmdk, js-yaml, Axios.

Applied to files:

  • web/src/pages/dashboard/BudgetBurnChart.tsx
  • web/src/pages/providers/PresetPicker.tsx
  • web/src/pages/providers/ProviderFormDrawer.tsx
  • web/src/pages/setup/ThemePreview.tsx
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do NOT recreate status dots inline—use `<StatusBadge>`. Do NOT build card-with-header layouts from scratch—use `<SectionCard>`. Do NOT create metric displays with `text-metric font-bold`—use `<MetricCard>`. Do NOT render initials circles manually—use `<Avatar>`. Do NOT create complex (>8 line) JSX inside `.map()`—extract to a shared component.

Applied to files:

  • web/src/pages/dashboard/BudgetBurnChart.tsx
  • web/src/pages/providers/PresetPicker.tsx
  • web/src/pages/providers/ProviderFormDrawer.tsx
  • web/src/pages/setup/ThemePreview.tsx
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/**/*.{ts,tsx} : Web Dashboard: ALWAYS reuse existing components from `web/src/components/ui/` before creating new ones. Reuse StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton variants, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup, Drawer, InputField, SelectField, SliderField, ToggleField, TaskStatusIndicator, PriorityBadge, ProviderHealthBadge, TokenUsageBar, CodeMirrorEditor, SegmentedControl, ThemeToggle.

Applied to files:

  • web/src/pages/dashboard/BudgetBurnChart.tsx
  • web/src/pages/setup/ThemePreview.tsx
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/**/*.{ts,tsx,css} : Design tokens: Colors use Tailwind semantic classes (`text-foreground`, `bg-card`, `text-accent`, etc.) or CSS variables (`var(--so-accent)`). NEVER hardcode hex values. Typography uses `font-sans` or `font-mono`. Spacing uses density-aware tokens or standard Tailwind. Shadows/borders use token variables.

Applied to files:

  • web/src/pages/providers/PresetPicker.tsx
  • web/src/pages/setup/ThemeStep.tsx
  • web/src/stores/setup-wizard.ts
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to **/*.{ts,tsx,json} : TypeScript 6.0: Remove `baseUrl` (deprecated); `paths` entries are relative to tsconfig directory. `esModuleInterop` always true—cannot be set to `false`. `types` defaults to `[]`—must explicitly list (e.g. `'types': ['vitest/globals']`). `DOM.Iterable` merged into `DOM`. `moduleResolution: 'classic'` and `'node10'` removed—use `'bundler'` or `'nodenext'`. `strict` defaults to `true`. `noUncheckedSideEffectImports` defaults to `true`.

Applied to files:

  • web/src/pages/providers/PresetPicker.tsx
  • web/src/pages/setup/ThemeStep.tsx
  • web/src/stores/setup-wizard.ts
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/providers/**/*.py : Providers: LLM provider abstraction (LiteLLM adapter), auth types (api_key/oauth/custom_header/none), presets (PROVIDER_PRESETS), runtime CRUD (ProviderManagementService with asyncio.Lock serialization), hot-reload via AppState swap.

Applied to files:

  • src/synthorg/providers/management/service.py
  • docs/design/operations.md
  • src/synthorg/observability/events/provider.py
  • src/synthorg/providers/drivers/litellm_driver.py
  • src/synthorg/providers/presets.py
  • src/synthorg/providers/management/_helpers.py
  • tests/unit/providers/management/test_service_discovery.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to src/synthorg/**/*.py : Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (via `model_copy(update=...)`) for runtime state that evolves

Applied to files:

  • src/synthorg/providers/management/service.py
  • src/synthorg/providers/presets.py
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to src/synthorg/providers/**/*.py : Providers: LLM provider abstraction, presets, model auto-discovery, runtime CRUD management, provider families, discovery SSRF allowlist, health tracking. Apply retry + rate limiting automatically via `BaseCompletionProvider`.

Applied to files:

  • src/synthorg/providers/management/service.py
  • docs/design/operations.md
  • src/synthorg/observability/events/provider.py
  • src/synthorg/providers/presets.py
  • src/synthorg/providers/management/_helpers.py
  • tests/unit/providers/management/test_service_discovery.py
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to src/synthorg/**/*.py : Use frozen Pydantic models for config/identity; separate mutable-via-copy models (using `model_copy(update=...)`) for runtime state

Applied to files:

  • src/synthorg/providers/management/service.py
  • src/synthorg/providers/presets.py
📚 Learning: 2026-03-20T11:18:48.128Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T11:18:48.128Z
Learning: Applies to src/synthorg/**/*.py : Set `RetryConfig` and `RateLimiterConfig` per-provider in `ProviderConfig`.

Applied to files:

  • src/synthorg/providers/management/service.py
  • src/synthorg/providers/presets.py
  • src/synthorg/providers/management/_helpers.py
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : Web components: Place new shared components in `web/src/components/ui/` with kebab-case filenames. Create `.stories.tsx` with all states. Export props as TypeScript interface. Use design tokens exclusively—no hardcoded colors, fonts, or spacing. Import `cn` from `@/lib/utils` for conditional class merging.

Applied to files:

  • web/src/pages/setup/ThemeStep.tsx
  • web/src/stores/setup-wizard.ts
  • web/src/pages/setup/ThemePreview.tsx
📚 Learning: 2026-03-17T18:52:05.142Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T18:52:05.142Z
Learning: Applies to src/synthorg/providers/**/*.py : All provider calls go through BaseCompletionProvider which applies retry + rate limiting automatically. Never implement retry logic in driver subclasses or calling code — it's handled by the base class.

Applied to files:

  • docs/design/operations.md
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/**/*.py : All provider calls go through `BaseCompletionProvider` which applies retry + rate limiting automatically. Never implement retry logic in driver subclasses or calling code — it's handled by the base class.

Applied to files:

  • docs/design/operations.md
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to src/synthorg/providers/**/*.py : All provider calls go through `BaseCompletionProvider` which applies retry + rate limiting automatically. Never implement retry logic in driver subclasses or calling code.

Applied to files:

  • docs/design/operations.md
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/**/*.py : All provider calls go through `BaseCompletionProvider` which applies retry + rate limiting automatically. Never implement retry logic in driver subclasses or calling code.

Applied to files:

  • docs/design/operations.md
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from synthorg.observability.events domain-specific modules (e.g., PROVIDER_CALL_START from events.provider). Import directly: from synthorg.observability.events.<domain> import EVENT_CONSTANT.

Applied to files:

  • src/synthorg/observability/events/provider.py
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to src/synthorg/**/*.py : Event names: always use constants from domain-specific modules under synthorg.observability.events (e.g., PROVIDER_CALL_START from events.provider, BUDGET_RECORD_ADDED from events.budget, etc.). Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`.

Applied to files:

  • src/synthorg/observability/events/provider.py
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to src/synthorg/**/*.py : Always use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `PROVIDER_CALL_START` from `events.provider`); import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`

Applied to files:

  • src/synthorg/observability/events/provider.py
📚 Learning: 2026-03-14T16:18:57.267Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T16:18:57.267Z
Learning: Applies to src/ai_company/!(observability)/**/*.py : Use event name constants from domain-specific modules under `ai_company.observability.events` (e.g., `PROVIDER_CALL_START` from `events.provider`). Import directly: `from ai_company.observability.events.<domain> import EVENT_CONSTANT`.

Applied to files:

  • src/synthorg/observability/events/provider.py
📚 Learning: 2026-03-14T15:43:05.601Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T15:43:05.601Z
Learning: Applies to src/**/*.py : Use event name constants from domain-specific modules under ai_company.observability.events (e.g., PROVIDER_CALL_START from events.provider, BUDGET_RECORD_ADDED from events.budget, etc.) — import directly

Applied to files:

  • src/synthorg/observability/events/provider.py
📚 Learning: 2026-03-20T11:18:48.128Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T11:18:48.128Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`). Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`.

Applied to files:

  • src/synthorg/observability/events/provider.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/observability/**/*.py : Observability package (observability/): structured logging, correlation tracking, log sinks; event constants organized by domain under observability/events/ (e.g., events.api, events.tool, events.git, events.context_budget, events.backup)

Applied to files:

  • src/synthorg/observability/events/provider.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`. For derived values use `computed_field` instead of storing + validating redundant fields. Use `NotBlankStr` (from `core.types`) for all identifier/name fields — including optional (`NotBlankStr | None`) and tuple (`tuple[NotBlankStr, ...]`) variants — instead of manual whitespace validators.

Applied to files:

  • src/synthorg/providers/presets.py
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 BaseModel, model_validator, computed_field, ConfigDict.

Applied to files:

  • src/synthorg/providers/presets.py
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to **/*.py : Pydantic v2: use `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`. Adopt conventions: use `allow_inf_nan=False` in all `ConfigDict` declarations to reject `NaN`/`Inf`; use `computed_field` for derived values; use `NotBlankStr` for all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators.

Applied to files:

  • src/synthorg/providers/presets.py
📚 Learning: 2026-03-16T19:13:36.562Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T19:13:36.562Z
Learning: Applies to src/synthorg/providers/**/*.py : RetryConfig and RateLimiterConfig are set per-provider in ProviderConfig.

Applied to files:

  • src/synthorg/providers/management/_helpers.py
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/pages/**/*.{ts,tsx} : Web dashboard pages: Lazy-loaded page components (one per route). Page-scoped sub-components in `pages/<page-name>/` subdirs. Feature dirs added as pages are built. Stores for auth, WebSocket, toast, analytics, setup wizard, company, agents, budget, tasks, settings, providers, theme.

Applied to files:

  • web/src/pages/setup/ThemePreview.tsx
🔇 Additional comments (28)
web/src/pages/providers/PresetPicker.tsx (2)

32-41: Looks good: card spacing/icon/description updates are consistent and safe.

This improves readability without violating design-token usage or component structure expectations.


58-58: Good alignment: skeleton height now matches the larger preset cards.

This should reduce perceived jump/flicker when loading completes.

web/src/api/endpoints/providers.ts (1)

62-67: LGTM!

The extended timeout is well-justified for local providers like Ollama that need to load models into memory before responding. The implementation correctly passes the timeout config as the third argument to axios post, and the comment clearly documents the rationale. The 4x increase from the default 30s to 120s is reasonable for cold-start scenarios.

web/src/utils/dashboard.ts (1)

99-108: Good defensive handling for invalid health metrics.

Filtering non-finite values and returning null when no valid departments remain is the right behavior for this aggregation path.

web/src/pages/dashboard/BudgetBurnChart.tsx (1)

127-127: Nice fix for chart readability and currency correctness.

The top-margin adjustment and formatCurrencyCompact(v, currency) tick formatting are both solid improvements for dashboard clarity.

Also applies to: 143-143

web/src/utils/format.ts (1)

61-77: formatCurrencyCompact addition looks good.

Input guarding plus Intl fallback behavior is robust and fits the existing formatter utility pattern.

web/src/pages/providers/ProviderFormDrawer.tsx (4)

98-135: Solid focus management implementation.

The focus trap, Escape-to-close, and opener restoration logic is well-structured. Minor note: the cleanup runs immediately when open becomes false, which restores focus while the exit animation is still playing—this is acceptable but can feel slightly abrupt. If smoother UX is desired, consider using onAnimationComplete from framer-motion to delay focus restoration until exit completes.


280-312: Well-structured modal with proper accessibility attributes.

Good use of role="dialog", aria-modal="true", dynamic aria-label, and tabIndex={-1} for programmatic focus. The overlay correctly uses aria-hidden="true" and click-to-close. The semantic Tailwind classes (bg-background/80, bg-card, border-border, etc.) and CSS variable for shadow align with design token guidelines.


41-50: Animation variants are well-defined.

Spring-based entrance and eased exit provide good visual feedback. The as const assertions ensure proper TypeScript inference for the transition types.


383-383: text-text-secondary is a valid design token and requires no changes.

The class is properly defined in web/src/styles/design-tokens.css as --so-text-secondary: #94a3b8; and used consistently across 40+ locations throughout the dashboard (components, pages, layout). It maps correctly to --color-text-secondary and follows the design system's semantic color pattern, though with a double-prefix naming convention that differs from some guidelines examples.

web/src/pages/setup/ThemeStep.tsx (1)

55-61: LGTM!

The palette options are correctly updated to the five new theme variants. The as const assertions ensure proper type narrowing, and the labels/descriptions provide clear guidance to users.

web/src/stores/setup-wizard.ts (1)

113-127: LGTM!

The ThemeSettings.palette type and DEFAULT_THEME.palette default are correctly aligned with the new five-palette system. The union type matches the options in ThemeStep.tsx and the CSS class mappings in ThemePreview.tsx.

web/src/pages/setup/ThemePreview.tsx (4)

19-25: LGTM!

The PALETTE_CLASS mapping correctly aligns with the CSS theme classes defined in design-tokens.css. The warm-ops palette correctly maps to an empty string since it uses the :root defaults, while the other palettes map to their corresponding .theme-* classes.


27-32: LGTM!

Animation transition configurations are well-structured with appropriate values for each mode. The instant mode correctly uses duration: 0 to disable animations.


76-118: LGTM!

The AnimationDemo component correctly demonstrates animation modes with proper cleanup of the interval timer. The instant mode appropriately disables the cycling animation, and the fallback to 'status-driven' handles unexpected values gracefully.


124-178: LGTM!

The ThemePreview component properly integrates the new sidebar mockup and animation demo while maintaining excellent reuse of existing shared components (MetricCard, AgentCard, DeptHealthBar, Button, SectionCard, StatPill). The flex layout correctly adapts to show/hide the sidebar based on settings.

src/synthorg/providers/drivers/litellm_driver.py (1)

353-356: Subscription token wiring looks correct.

Line 355 correctly passes subscription credentials via auth_token and avoids the previous header-based path.

tests/unit/providers/drivers/test_litellm_auth.py (2)

124-135: Updated subscription-auth expectation is aligned with driver behavior.

The new assertions validate the intended auth_token contract and guard against legacy header usage.


136-146: Good coverage for the no-token subscription path.

This closes the branch where subscription auth is selected but token material is unavailable.

src/synthorg/observability/events/provider.py (1)

83-87: Event constants are well-scoped and consistent.

The new LiteLLM lookup keys follow the existing provider event taxonomy and fit the observability module boundary.

tests/unit/providers/management/test_service_allowlist.py (2)

299-309: Nice test isolation for preset trust flow.

Mocking models_from_litellm alongside discovery keeps this test focused on allowlist trust semantics.


324-334: Consistent patch strategy in the custom-URL trust test.

This prevents LiteLLM model lookup side effects from masking the trust-url assertion.

tests/unit/providers/management/test_service_discovery.py (2)

118-128: Good isolation in auto-discovery test setup.

Patching models_from_litellm here avoids coupling this test to model DB availability.


146-156: Patch alignment is correct in the non-trusted URL path too.

This keeps the assertion on trust_url=False independent of LiteLLM model ingestion.

docs/design/operations.md (2)

124-124: Auth-type documentation update is accurate.

Line 124 correctly documents subscription auth via LiteLLM auth_token.


107-107: ⚠️ Potential issue | 🟠 Major

Replace real vendor/model names with project-generic placeholders.

Line 107 introduces concrete vendor/model names (Anthropic, claude-*) in project-owned docs. Please rewrite this line using the repository’s generic naming scheme (example-provider, example-large-001, etc.).

As per coding guidelines: "NEVER use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, tests, or config examples. Use generic names..."

⛔ Skipped due to learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to **/*.py : NEVER use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, tests, or config examples. Use generic names: `example-provider`, `example-large-001`, `example-medium-001`, `example-small-001`, `large`/`medium`/`small` as aliases. Tests must use `test-provider`, `test-small-001`, etc.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to {src,tests,web,cli,site}/**/*.{py,ts,tsx,go,astro} : Vendor-agnostic everywhere: NEVER use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, tests, or config examples. Use generic names: example-provider, example-large-001, example-medium-001, example-small-001. Vendor names may only appear in: (1) Operations design page provider list (docs/design/operations.md), (2) .claude/ skill/agent files, (3) third-party import paths/module names.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:33:01.580Z
Learning: Applies to web/**/*.vue : Never use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in dashboard code — use generic names: `example-provider`, `example-large-001`, etc.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to src/synthorg/providers/**/*.py : Providers: LLM provider abstraction, presets, model auto-discovery, runtime CRUD management, provider families, discovery SSRF allowlist, health tracking. Apply retry + rate limiting automatically via `BaseCompletionProvider`.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to tests/**/*.py : Tests must use test-provider, test-small-001, etc. for vendor-agnostic test data.
src/synthorg/providers/presets.py (1)

7-9: Import additions are clean and purposeful.

re and Final are both used by the new immutable regex filter mapping.

src/synthorg/providers/management/_helpers.py (1)

260-268: ⚠️ Potential issue | 🟠 Major

Wrap model config construction in try-except to skip malformed LiteLLM rows.

The import-failure case is handled properly, but once inside the loop the code passes raw litellm.model_cost values directly to ProviderModelConfig without validation. If any row has non-numeric/NaN/Inf costs or a non-positive max_input_tokens, Pydantic validation or arithmetic will raise and abort the entire lookup instead of skipping the bad model with a warning. Per coding guidelines: "Handle errors explicitly, never silently swallow. Validate at system boundaries."

Wrap the ProviderModelConfig construction (lines 299–304) in a try-except block that logs a warning and continues on validation or type errors.

⛔ Skipped due to learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to src/synthorg/providers/**/*.py : Providers: LLM provider abstraction, presets, model auto-discovery, runtime CRUD management, provider families, discovery SSRF allowlist, health tracking. Apply retry + rate limiting automatically via `BaseCompletionProvider`.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/**/*.py : `RetryConfig` and `RateLimiterConfig` are set per-provider in `ProviderConfig`. Retryable errors: `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`. Non-retryable errors raise immediately.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/providers/**/*.py : Providers: LLM provider abstraction (LiteLLM adapter), auth types (api_key/oauth/custom_header/none), presets (PROVIDER_PRESETS), runtime CRUD (ProviderManagementService with asyncio.Lock serialization), hot-reload via AppState swap.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/**/*.py : Handle errors explicitly, never silently swallow. Validate at system boundaries (user input, external APIs, config files).
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to src/synthorg/**/*.py : Never implement retry logic in provider subclasses or calling code — it is handled automatically by `BaseCompletionProvider` with `RetryConfig` and `RateLimiterConfig` per-provider
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T11:18:48.128Z
Learning: Applies to src/synthorg/**/*.py : Set `RetryConfig` and `RateLimiterConfig` per-provider in `ProviderConfig`.

Comment on lines +308 to +319
# ── Model version filters ────────────────────────────────────
# Provider-specific minimum model version filters for
# ``models_from_litellm()``. Only models matching the pattern
# are included. Providers not listed here include all models.
# Vendor-specific names are allowed here per CLAUDE.md:
# "provider presets (presets.py) which are user-facing runtime data".

MODEL_VERSION_FILTERS: Final[MappingProxyType[str, re.Pattern[str]]] = MappingProxyType(
{
"anthropic": re.compile(r"^claude-(opus|sonnet|haiku)-4-[56789]"),
}
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Model filter constants introduce disallowed real vendor/model identifiers.

Lines 315-318 add concrete vendor/model names (anthropic, claude-*) in project-owned source and comments. Please replace these with policy-compliant generic identifiers or move this mapping to an approved external/runtime source that is exempt from the naming rule.

As per coding guidelines: "NEVER use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, tests, or config examples. Use generic names..."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/synthorg/providers/presets.py` around lines 308 - 319,
MODEL_VERSION_FILTERS in presets.py contains real vendor/model identifiers;
replace them with policy-compliant generic identifiers or move the mapping to an
external/runtime config. Specifically, change the mapping key "anthropic" to a
neutral name (e.g., "provider_x") and replace the regex
r"^claude-(opus|sonnet|haiku)-4-[56789]" with a generic pattern (e.g.,
r"^model-(name1|name2|name3)-4-[0-9]+") or remove the hard-coded mapping and
load it from an approved external source at runtime; update any call sites such
as models_from_litellm() that expect the old key to use the new generic key or
configuration location so behavior remains consistent.

Comment on lines +47 to +51
<div
className={cn(
'flex flex-col gap-2 rounded-lg border border-border bg-bg-surface p-2 transition-all duration-200',
isCompact ? 'w-10' : 'w-28',
)}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify if these class patterns are defined in the project's Tailwind config or CSS

# Check for bg-surface token definition in Tailwind config
fd -t f -e js -e ts -e json 'tailwind' --exec grep -l 'bg-surface\|text-secondary\|text-muted' {} \;

# Check CSS files for these token definitions
rg -n 'bg-surface|text-secondary|text-muted' --type css -g '!node_modules'

# Check if these patterns are used elsewhere in the codebase for consistency
rg -n 'bg-bg-surface|text-text-secondary|text-text-muted' --type tsx --type ts -g '!node_modules' | head -20

Repository: Aureliolo/synthorg

Length of output: 1211


🏁 Script executed:

#!/bin/bash
# Find and examine Tailwind config files
fd -t f -name 'tailwind.config.*' -o -name 'tailwind.config.ts'

# Read the Tailwind config to see how tokens are extended
if [ -f "web/tailwind.config.ts" ]; then
  head -100 web/tailwind.config.ts
fi

# Check the actual file mentioned in the review
if [ -f "web/src/pages/setup/ThemePreview.tsx" ]; then
  sed -n '45,75p' web/src/pages/setup/ThemePreview.tsx
fi

Repository: Aureliolo/synthorg

Length of output: 1172


🏁 Script executed:

#!/bin/bash
# Find Tailwind config file
find . -maxdepth 3 -name 'tailwind.config.ts' -o -name 'tailwind.config.js'

# Read it if found
cat web/tailwind.config.ts 2>/dev/null | head -150

Repository: Aureliolo/synthorg

Length of output: 44


🏁 Script executed:

#!/bin/bash
# Search for tailwind config files more broadly
find web -type f \( -name 'tailwind.config*' -o -name 'postcss.config*' \)

# Check package.json for tailwind version
cat web/package.json | grep -A 2 '"tailwind'

# Search for where these classes might be defined
rg 'bg-bg-surface|text-text-secondary|text-text-muted' web/src/styles/ -A 1 -B 1

# Look at the design-tokens.css more closely
cat web/src/styles/design-tokens.css | head -80

Repository: Aureliolo/synthorg

Length of output: 2412


🏁 Script executed:

#!/bin/bash
# Read global.css to see the `@theme` directives
cat web/src/styles/global.css | head -200

Repository: Aureliolo/synthorg

Length of output: 5688


🏁 Script executed:

#!/bin/bash
# Search for other usage of these classes to understand the pattern
rg 'bg-surface|bg-bg-surface' web/src --type tsx -c | head -20

rg 'text-secondary|text-text-secondary' web/src --type tsx -c | head -20

# Check if there's custom Tailwind config that might extend these
find web -name '*.config.*' -type f | xargs cat 2>/dev/null | grep -A 20 -B 5 'colors\|extend'

Repository: Aureliolo/synthorg

Length of output: 117


🏁 Script executed:

#!/bin/bash
# Search for usage of these classes without type filter
rg 'bg-bg-surface' web/src | head -10
rg 'text-text-secondary' web/src | head -10
rg 'text-text-muted' web/src | head -10

# Also check for correct versions
rg 'bg-surface' web/src | head -10
rg 'text-muted-foreground' web/src | head -10

Repository: Aureliolo/synthorg

Length of output: 5165


Replace incorrect design token classes with properly mapped Tailwind utilities.

The classes bg-bg-surface and text-text-muted don't exist in the Tailwind config. Based on the @theme inline definitions in global.css:

  • bg-bg-surface → use bg-surface (maps to --color-surface)
  • text-text-muted → use text-muted-foreground (maps to --color-muted-foreground)

text-text-secondary is correct.

Also fix this in:

  • web/src/pages/providers/ProviderDetailHeader.tsx
  • web/src/pages/providers/ProviderCard.tsx
  • web/src/pages/providers/PresetPicker.tsx
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/setup/ThemePreview.tsx` around lines 47 - 51, Replace the
non-existent design token classes with the correct Tailwind utilities: in the
ThemePreview component update className usage replacing bg-bg-surface with
bg-surface and text-text-muted with text-muted-foreground (keep
text-text-secondary as-is); apply the same replacements in ProviderDetailHeader,
ProviderCard, and PresetPicker wherever bg-bg-surface or text-text-muted are
used so all components reference bg-surface and text-muted-foreground instead of
the invalid tokens.

…mini

Backend:
- Fix TypeError crash on None cost values in models_from_litellm
- Replace vendor names in _helpers.py docstrings/comments with generic placeholders
- Split models_from_litellm into _parse_litellm_entry helper (50-line rule)
- Add per-entry error handling to prevent single malformed entry aborting all model loading
- Extract _DEFAULT_MAX_CONTEXT constant (200_000)
- Lazy litellm lookup: skip when request.models is explicitly supplied
- Add logging to infer_preset_hint ValueError path
- Fix operations.md YAML comment to reflect auth_token instead of bearer token
- Fix presets.py comment: "generation allowlists" not "minimum version filters"
- Fix provider.py event separator to Unicode box-drawing style

Frontend:
- Replace custom portal/motion modal with Radix Dialog in ProviderFormDrawer
  (body scroll lock, proper focus trap, ARIA, consistency with other dialogs)
- Sanitize ProgressGauge max prop for NaN
- Type SidebarPreview mode prop as ThemeSettings['sidebar']
- Extract SidebarNavItem from .map() block
- Fix formatCurrencyCompact JSDoc to reflect actual Intl output

Tests:
- Add 12 unit tests for models_from_litellm (filtering, dedup, None costs,
  version filter, import failure, sorting, prefix stripping)
- Add computeOrgHealth NaN/Infinity tests (3 cases)
- Add formatCurrencyCompact tests (7 cases)
- Add ProgressGauge NaN/Infinity tests (3 cases)
- Clarify test_litellm_auth object.__setattr__ comment

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@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: 8

Caution

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

⚠️ Outside diff range comments (1)
web/src/pages/providers/ProviderFormDrawer.tsx (1)

40-46: 🧹 Nitpick | 🔵 Trivial

Consider renaming the component to reflect the UI change.

The component is now rendering a centered modal (Radix Dialog) rather than a right-side drawer, but the filename and component name still reference "Drawer." This creates a naming inconsistency that could confuse future maintainers.

Consider renaming to ProviderFormModal.tsx and updating all import sites accordingly.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/providers/ProviderFormDrawer.tsx` around lines 40 - 46, The
component ProviderFormDrawer has been converted from a side drawer to a centered
modal, so rename the component and file to reflect that: rename the component
identifier ProviderFormDrawer to ProviderFormModal and move/rename the file to
ProviderFormModal.tsx; update all import sites and any exports that reference
ProviderFormDrawer to use ProviderFormModal (including default/named exports)
and adjust any test or route references; ensure the component props type
ProviderFormDrawerProps is renamed (or create an alias) to
ProviderFormModalProps to keep types consistent and run the project’s
search/replace to update every usage.
♻️ Duplicate comments (1)
web/src/pages/setup/ThemePreview.tsx (1)

70-70: ⚠️ Potential issue | 🟠 Major

Use the supported semantic utilities here.

bg-bg-surface and text-text-muted still won’t resolve, so the sidebar and demo card inherit the wrong surface/muted styling. Swap them to bg-surface and text-muted-foreground.

♻️ Suggested fix
-        'flex flex-col gap-2 rounded-lg border border-border bg-bg-surface p-2 transition-all duration-200',
+        'flex flex-col gap-2 rounded-lg border border-border bg-surface p-2 transition-all duration-200',

-        <div className="mt-auto flex justify-center pt-1 text-text-muted">
+        <div className="mt-auto flex justify-center pt-1 text-muted-foreground">

-            className="flex items-center gap-2 rounded-md border border-border bg-bg-surface px-3 py-1.5"
+            className="flex items-center gap-2 rounded-md border border-border bg-surface px-3 py-1.5"

-      <span className="text-[9px] text-text-muted italic">
+      <span className="text-[9px] text-muted-foreground italic">

As per coding guidelines: "Design tokens: Colors use Tailwind semantic classes (text-foreground, bg-card, text-accent, etc.) or CSS variables (var(--so-accent)). NEVER hardcode hex values."

Also applies to: 84-84, 113-113, 129-129

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/setup/ThemePreview.tsx` at line 70, In ThemePreview.tsx replace
unsupported semantic utilities: change any occurrences of "bg-bg-surface" to
"bg-surface" and "text-text-muted" to "text-muted-foreground" inside the
className strings used by the ThemePreview component (e.g., the card/sidebar
class string 'flex flex-col gap-2 rounded-lg border border-border bg-bg-surface
p-2 ...') and update the other similar className usages in this file (the other
occurrences noted) so they use the supported Tailwind semantic tokens instead.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/src/__tests__/components/ui/progress-gauge.test.tsx`:
- Around line 95-98: The test name is misleading: Infinity is handled as
non-finite so safeValue defaults to 0, producing "0%", not "clamped to max".
Update the test case title in the it(...) for ProgressGauge to reflect this
behavior (e.g. "treats Infinity as non-finite and defaults to 0"), and if you
prefer behavior change instead, modify the ProgressGauge logic that computes
safeValue to clamp Infinity to max instead; locate the ProgressGauge component
and the safeValue computation to apply the chosen fix.

In `@web/src/__tests__/utils/format.test.ts`:
- Around line 192-196: The test for formatCurrencyCompact is too permissive;
update the assertion to verify semantic compact notation instead of length. In
the 'formats large values with compact notation' test that calls
formatCurrencyCompact(1500, 'USD'), assert that the returned string includes the
expected currency marker (e.g., '$' or 'USD') and an abbreviation character
(e.g., 'K' or 'M') or matches a regex for compact forms—use
formatCurrencyCompact result and assert via a regex like /(\$|USD).*(K|M|B)/ or
similar to ensure the output is both currency-labeled and abbreviated.

In `@web/src/pages/providers/ProviderFormDrawer.tsx`:
- Around line 236-245: Add an explicit Dialog.Description or suppress the Radix
accessibility warning by setting aria-describedby on Dialog.Content; locate the
Dialog.Content component in ProviderFormDrawer (the JSX block using
Dialog.Content) and either add a <Dialog.Description> wrapping the form
intro/summary text to serve as the description, or add
aria-describedby={undefined} to the Dialog.Content props to silence the warning
if the form itself is the implicit description; ensure to update the
Dialog.Content element (and any close sibling text) so the warning no longer
appears.
- Around line 358-366: The nested ternary for the hint prop is confusing and
reachable branches are incorrect; update the hint logic in ProviderFormDrawer
(the hint prop that references isCustom, mode and preset.default_base_url) to an
explicit sequence: if isCustom || mode === 'edit' return undefined; else if
preset is falsy return 'Optional for known cloud providers'; else if
preset.default_base_url is falsy return 'Required for this provider'; otherwise
return undefined — replace the nested ternary with this clearer conditional
order using the hint prop.

In `@web/src/pages/setup/ThemePreview.tsx`:
- Around line 115-124: Replace the inline status circle inside the motion.div
with the shared StatusBadge component: keep the motion.div wrapper and its
animate/transition logic (including use of animation and transition), remove the
manual classes (size-2 rounded-full bg-success), and render <StatusBadge> inside
the motion container with the matching sizing/variant props (e.g., size="2" or
className="size-2" and variant/status="success") so the shared status primitive
is used for the active indicator.
- Around line 19-25: The lookup maps are too loose; narrow their types to the
ThemeSettings unions so TypeScript catches new palettes/animations: change
PALETTE_CLASS from Record<string,string> to Record<ThemeSettings['palette'],
string> and any animation map or variables typed as string to
Record<ThemeSettings['animation'], string> (and the animation variable type to
ThemeSettings['animation']); import or reference ThemeSettings as needed and
remove the cast/fallback on the preview render so the code will fail type-check
if a new key is missing, then run the web type-check to verify.
- Around line 97-101: The useEffect that starts the interval when "animation"
changes can leave "cycling" false causing a blank preview; inside the effect
(the one referencing animation and creating interval), reset cycling to true
before creating the interval so the demo card shows immediately when switching
away from 'instant' (use the existing setCycling state setter), then create the
interval and clear it in the cleanup as before.

In `@web/src/utils/format.ts`:
- Around line 65-76: The formatCurrencyCompact function passes raw currencyCode
into Intl.NumberFormat causing repeated exceptions; normalize and validate
currencyCode before constructing the formatter (e.g., coerce to a safe 3-letter
uppercase ISO code or fall back to 'EUR' if invalid) and then use that
normalized value in the Intl.NumberFormat call so the catch path is avoided
during chart tick rendering; update formatCurrencyCompact to sanitize
currencyCode first and only use the try/catch for genuinely unexpected failures.

---

Outside diff comments:
In `@web/src/pages/providers/ProviderFormDrawer.tsx`:
- Around line 40-46: The component ProviderFormDrawer has been converted from a
side drawer to a centered modal, so rename the component and file to reflect
that: rename the component identifier ProviderFormDrawer to ProviderFormModal
and move/rename the file to ProviderFormModal.tsx; update all import sites and
any exports that reference ProviderFormDrawer to use ProviderFormModal
(including default/named exports) and adjust any test or route references;
ensure the component props type ProviderFormDrawerProps is renamed (or create an
alias) to ProviderFormModalProps to keep types consistent and run the project’s
search/replace to update every usage.

---

Duplicate comments:
In `@web/src/pages/setup/ThemePreview.tsx`:
- Line 70: In ThemePreview.tsx replace unsupported semantic utilities: change
any occurrences of "bg-bg-surface" to "bg-surface" and "text-text-muted" to
"text-muted-foreground" inside the className strings used by the ThemePreview
component (e.g., the card/sidebar class string 'flex flex-col gap-2 rounded-lg
border border-border bg-bg-surface p-2 ...') and update the other similar
className usages in this file (the other occurrences noted) so they use the
supported Tailwind semantic tokens instead.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 18db3c5d-ce30-4c84-bdd9-3cf278e1743c

📥 Commits

Reviewing files that changed from the base of the PR and between 44dc453 and ceab9e8.

📒 Files selected for processing (14)
  • docs/design/operations.md
  • src/synthorg/observability/events/provider.py
  • src/synthorg/providers/management/_helpers.py
  • src/synthorg/providers/management/service.py
  • src/synthorg/providers/presets.py
  • tests/unit/providers/drivers/test_litellm_auth.py
  • tests/unit/providers/management/test_helpers.py
  • web/src/__tests__/components/ui/progress-gauge.test.tsx
  • web/src/__tests__/utils/dashboard.test.ts
  • web/src/__tests__/utils/format.test.ts
  • web/src/components/ui/progress-gauge.tsx
  • web/src/pages/providers/ProviderFormDrawer.tsx
  • web/src/pages/setup/ThemePreview.tsx
  • web/src/utils/format.ts
📜 Review details
⏰ 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). (6)
  • GitHub Check: Build Sandbox
  • GitHub Check: Build Backend
  • GitHub Check: Dashboard Test
  • GitHub Check: Test (Python 3.14)
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (13)
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

web/src/**/*.{ts,tsx}: Web Dashboard: ALWAYS reuse existing components from web/src/components/ui/ before creating new ones. Reuse StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton variants, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup, Drawer, InputField, SelectField, SliderField, ToggleField, TaskStatusIndicator, PriorityBadge, ProviderHealthBadge, TokenUsageBar, CodeMirrorEditor, SegmentedControl, ThemeToggle.
Do NOT recreate status dots inline—use <StatusBadge>. Do NOT build card-with-header layouts from scratch—use <SectionCard>. Do NOT create metric displays with text-metric font-bold—use <MetricCard>. Do NOT render initials circles manually—use <Avatar>. Do NOT create complex (>8 line) JSX inside .map()—extract to a shared component.
Web design system enforcement: A PostToolUse hook (scripts/check_web_design_system.py) runs automatically on every Edit/Write to web/src/ files. It catches hardcoded hex colors, rgba values, font-family declarations, missing Storybook stories, duplicate patterns, and complex map blocks. Fix all violations before proceeding.
React 19 + shadcn/ui + Tailwind CSS 4 dashboard: Use React Router, Zustand stores, @tanstack/react-query, @xyflow/react, @dagrejs/dagre, d3-force, @dnd-kit, Recharts, Framer Motion, cmdk, js-yaml, Axios.

Files:

  • web/src/components/ui/progress-gauge.tsx
  • web/src/__tests__/components/ui/progress-gauge.test.tsx
  • web/src/__tests__/utils/format.test.ts
  • web/src/__tests__/utils/dashboard.test.ts
  • web/src/pages/providers/ProviderFormDrawer.tsx
  • web/src/pages/setup/ThemePreview.tsx
  • web/src/utils/format.ts
web/src/**/*.{ts,tsx,css}

📄 CodeRabbit inference engine (CLAUDE.md)

Design tokens: Colors use Tailwind semantic classes (text-foreground, bg-card, text-accent, etc.) or CSS variables (var(--so-accent)). NEVER hardcode hex values. Typography uses font-sans or font-mono. Spacing uses density-aware tokens or standard Tailwind. Shadows/borders use token variables.

Files:

  • web/src/components/ui/progress-gauge.tsx
  • web/src/__tests__/components/ui/progress-gauge.test.tsx
  • web/src/__tests__/utils/format.test.ts
  • web/src/__tests__/utils/dashboard.test.ts
  • web/src/pages/providers/ProviderFormDrawer.tsx
  • web/src/pages/setup/ThemePreview.tsx
  • web/src/utils/format.ts
web/src/components/ui/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Web components: Place new shared components in web/src/components/ui/ with kebab-case filenames. Create .stories.tsx with all states. Export props as TypeScript interface. Use design tokens exclusively—no hardcoded colors, fonts, or spacing. Import cn from @/lib/utils for conditional class merging.

Files:

  • web/src/components/ui/progress-gauge.tsx
**/*.{ts,tsx,json}

📄 CodeRabbit inference engine (CLAUDE.md)

TypeScript 6.0: Remove baseUrl (deprecated); paths entries are relative to tsconfig directory. esModuleInterop always true—cannot be set to false. types defaults to []—must explicitly list (e.g. 'types': ['vitest/globals']). DOM.Iterable merged into DOM. moduleResolution: 'classic' and 'node10' removed—use 'bundler' or 'nodenext'. strict defaults to true. noUncheckedSideEffectImports defaults to true.

Files:

  • web/src/components/ui/progress-gauge.tsx
  • web/src/__tests__/components/ui/progress-gauge.test.tsx
  • web/src/__tests__/utils/format.test.ts
  • web/src/__tests__/utils/dashboard.test.ts
  • web/src/pages/providers/ProviderFormDrawer.tsx
  • web/src/pages/setup/ThemePreview.tsx
  • web/src/utils/format.ts
**/*.{py,ts,tsx,go}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{py,ts,tsx,go}: Lint & format: Python via ruff (uv run ruff check + uv run ruff format). TypeScript web via ESLint (zero warnings enforced: npm --prefix web run lint). Go CLI via golangci-lint and go vet. All enforced in pre-commit hooks.
Type checking: Python via mypy strict mode (uv run mypy src/ tests/). TypeScript via npm --prefix web run type-check. Go via go -C cli vet ./....

Files:

  • web/src/components/ui/progress-gauge.tsx
  • web/src/__tests__/components/ui/progress-gauge.test.tsx
  • tests/unit/providers/drivers/test_litellm_auth.py
  • web/src/__tests__/utils/format.test.ts
  • web/src/__tests__/utils/dashboard.test.ts
  • src/synthorg/providers/management/service.py
  • src/synthorg/providers/presets.py
  • src/synthorg/observability/events/provider.py
  • web/src/pages/providers/ProviderFormDrawer.tsx
  • tests/unit/providers/management/test_helpers.py
  • web/src/pages/setup/ThemePreview.tsx
  • web/src/utils/format.ts
  • src/synthorg/providers/management/_helpers.py
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Python version 3.14+ with PEP 649 native lazy annotations; use PEP 758 except syntax (except A, B: without parentheses). Do NOT use from __future__ import annotations.
Type hints: all public functions must have type hints. Use mypy strict mode for type checking.
Docstrings: Google style is required on all public classes and functions. Enforced by ruff D rules.
Immutability: create new objects, never mutate existing ones. For non-Pydantic internal collections (registries, BaseTool), use copy.deepcopy() at construction + MappingProxyType wrapping for read-only enforcement.
For dict/list fields in frozen Pydantic models, rely on frozen=True for field reassignment prevention and copy.deepcopy() at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, serializing for persistence).
Config vs runtime state: use frozen Pydantic models for config/identity; use separate mutable-via-copy models (using model_copy(update=...)) for runtime state that evolves. Never mix static config fields with mutable runtime fields in one model.
Pydantic v2: use BaseModel, model_validator, computed_field, ConfigDict. Adopt conventions: use allow_inf_nan=False in all ConfigDict declarations to reject NaN/Inf; use @computed_field for derived values; use NotBlankStr for all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators.
Async concurrency: prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code (e.g. multiple tool invocations, parallel agent calls). Prefer structured concurrency over bare create_task.
Line length: 88 characters enforced by ruff.
Functions must be < 50 lines, files < 800 lines.
Handle errors explicitly, never silently swallow. Validate at system boundaries (user input, external APIs, config files).
NEVER use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, tests, or co...

Files:

  • tests/unit/providers/drivers/test_litellm_auth.py
  • src/synthorg/providers/management/service.py
  • src/synthorg/providers/presets.py
  • src/synthorg/observability/events/provider.py
  • tests/unit/providers/management/test_helpers.py
  • src/synthorg/providers/management/_helpers.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Test markers: @pytest.mark.unit, @pytest.mark.integration, @pytest.mark.e2e, @pytest.mark.slow. Coverage: 80% minimum enforced in CI.
Async testing: asyncio_mode = 'auto' in pyproject.toml—no manual @pytest.mark.asyncio needed.
Test timeout: 30 seconds per test (global in pyproject.toml)—do not add per-file pytest.mark.timeout(30) markers; non-default overrides like timeout(60) are allowed.
Prefer @pytest.mark.parametrize for testing similar cases in Python tests.
Property-based testing: Python uses Hypothesis (@given + @settings). Profiles: ci (50 examples, default) and dev (1000 examples). Run dev profile: HYPOTHESIS_PROFILE=dev uv run python -m pytest tests/ -m unit -n auto -k properties.
NEVER skip, dismiss, or ignore flaky tests—always fix them fully and fundamentally. For timing-sensitive tests, mock time.monotonic() and asyncio.sleep(). For tasks that must block indefinitely, use asyncio.Event().wait() instead of asyncio.sleep(large_number).
Testing: Python pytest with markers (@pytest.mark.unit, @pytest.mark.integration, @pytest.mark.e2e) and coverage >=80%. Web dashboard Vitest with coverage scoped to files changed vs origin/main. Go go -C cli test ./....

Files:

  • tests/unit/providers/drivers/test_litellm_auth.py
  • tests/unit/providers/management/test_helpers.py
src/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.py: Every module with business logic MUST have: from synthorg.observability import get_logger then logger = get_logger(__name__). Never use import logging / logging.getLogger() / print() in application code.
Always use constants from domain-specific modules under synthorg.observability.events (e.g., API_REQUEST_STARTED from events.api). Import directly and use structured kwargs: logger.info(EVENT, key=value). Never use logger.info('msg %s', val).
All error paths must log at WARNING or ERROR with context before raising. All state transitions must log at INFO. DEBUG for object creation, internal flow, entry/exit of key functions.
Pure data models, enums, and re-exports do NOT need logging.

Files:

  • src/synthorg/providers/management/service.py
  • src/synthorg/providers/presets.py
  • src/synthorg/observability/events/provider.py
  • src/synthorg/providers/management/_helpers.py
src/synthorg/providers/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/synthorg/providers/**/*.py: All provider calls go through BaseCompletionProvider which applies retry + rate limiting automatically. Never implement retry logic in driver subclasses or calling code.
RetryConfig and RateLimiterConfig are set per-provider in ProviderConfig. Retryable errors: RateLimitError, ProviderTimeoutError, ProviderConnectionError, ProviderInternalError. Non-retryable errors raise immediately. RetryExhaustedError signals all retries failed.
Rate limiter respects RateLimitError.retry_after from providers—automatically pauses future requests.
Providers: LLM provider abstraction, presets, model auto-discovery, runtime CRUD management, provider families, discovery SSRF allowlist, health tracking. Apply retry + rate limiting automatically via BaseCompletionProvider.

Files:

  • src/synthorg/providers/management/service.py
  • src/synthorg/providers/presets.py
  • src/synthorg/providers/management/_helpers.py
src/synthorg/observability/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Observability: Structured logging, correlation tracking, redaction, third-party logger taming, events. Use get_logger(__name__) and event constants from synthorg.observability.events.*.

Files:

  • src/synthorg/observability/events/provider.py
web/src/pages/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Web dashboard pages: Lazy-loaded page components (one per route). Page-scoped sub-components in pages/<page-name>/ subdirs. Feature dirs added as pages are built. Stores for auth, WebSocket, toast, analytics, setup wizard, company, agents, budget, tasks, settings, providers, theme.

Files:

  • web/src/pages/providers/ProviderFormDrawer.tsx
  • web/src/pages/setup/ThemePreview.tsx
docs/design/*.md

📄 CodeRabbit inference engine (CLAUDE.md)

When approved deviations occur, update the relevant docs/design/ page to reflect the new reality.

Files:

  • docs/design/operations.md
docs/**/*.md

📄 CodeRabbit inference engine (CLAUDE.md)

Documentation: docs/ (Markdown, built with Zensical, config: mkdocs.yml). Design spec in docs/design/ (11 pages), architecture in docs/architecture/, roadmap in docs/roadmap/. REST API reference auto-generated by scripts/export_openapi.py. Library reference auto-generated via mkdocstrings + Griffe. Landing page in site/ (Astro).

Files:

  • docs/design/operations.md
🧠 Learnings (41)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/providers/**/*.py : Providers: LLM provider abstraction (LiteLLM adapter), auth types (api_key/oauth/custom_header/none), presets (PROVIDER_PRESETS), runtime CRUD (ProviderManagementService with asyncio.Lock serialization), hot-reload via AppState swap.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to src/synthorg/providers/**/*.py : Providers: LLM provider abstraction, presets, model auto-discovery, runtime CRUD management, provider families, discovery SSRF allowlist, health tracking. Apply retry + rate limiting automatically via `BaseCompletionProvider`.
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to web/src/__tests__/**/*.{ts,js} : Dashboard testing: Vitest unit tests organized by feature under `web/src/__tests__/`. Use fast-check for property-based testing (`fc.assert` + `fc.property`).

Applied to files:

  • web/src/__tests__/components/ui/progress-gauge.test.tsx
  • web/src/__tests__/utils/format.test.ts
  • web/src/__tests__/utils/dashboard.test.ts
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/providers/**/*.py : Providers: LLM provider abstraction (LiteLLM adapter), auth types (api_key/oauth/custom_header/none), presets (PROVIDER_PRESETS), runtime CRUD (ProviderManagementService with asyncio.Lock serialization), hot-reload via AppState swap.

Applied to files:

  • src/synthorg/providers/management/service.py
  • src/synthorg/providers/presets.py
  • src/synthorg/observability/events/provider.py
  • docs/design/operations.md
  • src/synthorg/providers/management/_helpers.py
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to src/synthorg/providers/**/*.py : Providers: LLM provider abstraction, presets, model auto-discovery, runtime CRUD management, provider families, discovery SSRF allowlist, health tracking. Apply retry + rate limiting automatically via `BaseCompletionProvider`.

Applied to files:

  • src/synthorg/providers/management/service.py
  • src/synthorg/providers/presets.py
  • src/synthorg/observability/events/provider.py
  • docs/design/operations.md
  • src/synthorg/providers/management/_helpers.py
📚 Learning: 2026-03-20T11:18:48.128Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T11:18:48.128Z
Learning: Applies to src/synthorg/**/*.py : Set `RetryConfig` and `RateLimiterConfig` per-provider in `ProviderConfig`.

Applied to files:

  • src/synthorg/providers/management/service.py
  • src/synthorg/providers/management/_helpers.py
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to {src,tests,web,cli,site}/**/*.{py,ts,tsx,go,astro} : Vendor-agnostic everywhere: NEVER use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, tests, or config examples. Use generic names: example-provider, example-large-001, example-medium-001, example-small-001. Vendor names may only appear in: (1) Operations design page provider list (docs/design/operations.md), (2) .claude/ skill/agent files, (3) third-party import paths/module names.

Applied to files:

  • src/synthorg/providers/presets.py
  • src/synthorg/providers/management/_helpers.py
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to **/*.py : NEVER use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, tests, or config examples. Use generic names: `example-provider`, `example-large-001`, `example-medium-001`, `example-small-001`, `large`/`medium`/`small` as aliases. Tests must use `test-provider`, `test-small-001`, etc.

Applied to files:

  • src/synthorg/providers/presets.py
  • src/synthorg/providers/management/_helpers.py
📚 Learning: 2026-03-19T11:33:01.580Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:33:01.580Z
Learning: Applies to web/**/*.vue : Never use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in dashboard code — use generic names: `example-provider`, `example-large-001`, etc.

Applied to files:

  • src/synthorg/providers/presets.py
  • src/synthorg/providers/management/_helpers.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`. For derived values use `computed_field` instead of storing + validating redundant fields. Use `NotBlankStr` (from `core.types`) for all identifier/name fields — including optional (`NotBlankStr | None`) and tuple (`tuple[NotBlankStr, ...]`) variants — instead of manual whitespace validators.

Applied to files:

  • src/synthorg/providers/presets.py
  • src/synthorg/providers/management/_helpers.py
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/**/*.py : Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (using model_copy(update=...)) for runtime state that evolves. Never mix static config fields with mutable runtime fields in one model.

Applied to files:

  • src/synthorg/providers/presets.py
📚 Learning: 2026-03-26T15:18:16.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T15:18:16.848Z
Learning: Applies to src/**/*.py : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`. Use `computed_field` for derived values instead of storing redundant fields. Use `NotBlankStr` for all identifier/name fields.

Applied to files:

  • src/synthorg/providers/presets.py
📚 Learning: 2026-03-15T18:42:17.990Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:42:17.990Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`

Applied to files:

  • src/synthorg/providers/presets.py
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to **/*.py : Pydantic v2: use `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`. Adopt conventions: use `allow_inf_nan=False` in all `ConfigDict` declarations to reject `NaN`/`Inf`; use `computed_field` for derived values; use `NotBlankStr` for all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators.

Applied to files:

  • src/synthorg/providers/presets.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to src/synthorg/**/*.py : Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (via `model_copy(update=...)`) for runtime state that evolves

Applied to files:

  • src/synthorg/providers/presets.py
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 BaseModel, model_validator, computed_field, ConfigDict.

Applied to files:

  • src/synthorg/providers/presets.py
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from synthorg.observability.events domain-specific modules (e.g., PROVIDER_CALL_START from events.provider). Import directly: from synthorg.observability.events.<domain> import EVENT_CONSTANT.

Applied to files:

  • src/synthorg/observability/events/provider.py
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to src/synthorg/**/*.py : Always use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `PROVIDER_CALL_START` from `events.provider`); import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`

Applied to files:

  • src/synthorg/observability/events/provider.py
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to src/synthorg/**/*.py : Event names: always use constants from domain-specific modules under synthorg.observability.events (e.g., PROVIDER_CALL_START from events.provider, BUDGET_RECORD_ADDED from events.budget, etc.). Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`.

Applied to files:

  • src/synthorg/observability/events/provider.py
📚 Learning: 2026-03-14T16:18:57.267Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T16:18:57.267Z
Learning: Applies to src/ai_company/!(observability)/**/*.py : Use event name constants from domain-specific modules under `ai_company.observability.events` (e.g., `PROVIDER_CALL_START` from `events.provider`). Import directly: `from ai_company.observability.events.<domain> import EVENT_CONSTANT`.

Applied to files:

  • src/synthorg/observability/events/provider.py
📚 Learning: 2026-03-14T15:43:05.601Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T15:43:05.601Z
Learning: Applies to src/**/*.py : Use event name constants from domain-specific modules under ai_company.observability.events (e.g., PROVIDER_CALL_START from events.provider, BUDGET_RECORD_ADDED from events.budget, etc.) — import directly

Applied to files:

  • src/synthorg/observability/events/provider.py
📚 Learning: 2026-03-20T11:18:48.128Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T11:18:48.128Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`). Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`.

Applied to files:

  • src/synthorg/observability/events/provider.py
📚 Learning: 2026-03-19T11:33:01.580Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:33:01.580Z
Learning: Applies to src/synthorg/**/*.py : Use event constants from `synthorg.observability.events.<domain>` (e.g., `API_REQUEST_STARTED` from `events.api`); import directly and log with structured kwargs: `logger.info(EVENT, key=value)`, never interpolated strings

Applied to files:

  • src/synthorg/observability/events/provider.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to src/synthorg/**/*.py : Always use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`); import directly rather than using string literals

Applied to files:

  • src/synthorg/observability/events/provider.py
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/**/*.{ts,tsx} : React 19 + shadcn/ui + Tailwind CSS 4 dashboard: Use React Router, Zustand stores, tanstack/react-query, xyflow/react, dagrejs/dagre, d3-force, dnd-kit, Recharts, Framer Motion, cmdk, js-yaml, Axios.

Applied to files:

  • web/src/pages/providers/ProviderFormDrawer.tsx
  • web/src/pages/setup/ThemePreview.tsx
📚 Learning: 2026-03-17T18:52:05.142Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T18:52:05.142Z
Learning: Applies to src/synthorg/providers/**/*.py : All provider calls go through BaseCompletionProvider which applies retry + rate limiting automatically. Never implement retry logic in driver subclasses or calling code — it's handled by the base class.

Applied to files:

  • docs/design/operations.md
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/**/*.py : All provider calls go through `BaseCompletionProvider` which applies retry + rate limiting automatically. Never implement retry logic in driver subclasses or calling code — it's handled by the base class.

Applied to files:

  • docs/design/operations.md
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to src/synthorg/providers/**/*.py : All provider calls go through `BaseCompletionProvider` which applies retry + rate limiting automatically. Never implement retry logic in driver subclasses or calling code.

Applied to files:

  • docs/design/operations.md
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/**/*.py : All provider calls go through `BaseCompletionProvider` which applies retry + rate limiting automatically. Never implement retry logic in driver subclasses or calling code.

Applied to files:

  • docs/design/operations.md
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do NOT recreate status dots inline—use `<StatusBadge>`. Do NOT build card-with-header layouts from scratch—use `<SectionCard>`. Do NOT create metric displays with `text-metric font-bold`—use `<MetricCard>`. Do NOT render initials circles manually—use `<Avatar>`. Do NOT create complex (>8 line) JSX inside `.map()`—extract to a shared component.

Applied to files:

  • web/src/pages/setup/ThemePreview.tsx
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/**/*.{ts,tsx,css} : Design tokens: Colors use Tailwind semantic classes (`text-foreground`, `bg-card`, `text-accent`, etc.) or CSS variables (`var(--so-accent)`). NEVER hardcode hex values. Typography uses `font-sans` or `font-mono`. Spacing uses density-aware tokens or standard Tailwind. Shadows/borders use token variables.

Applied to files:

  • web/src/pages/setup/ThemePreview.tsx
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/**/*.{ts,tsx} : Web design system enforcement: A PostToolUse hook (`scripts/check_web_design_system.py`) runs automatically on every Edit/Write to `web/src/` files. It catches hardcoded hex colors, rgba values, font-family declarations, missing Storybook stories, duplicate patterns, and complex map blocks. Fix all violations before proceeding.

Applied to files:

  • web/src/pages/setup/ThemePreview.tsx
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : Web components: Place new shared components in `web/src/components/ui/` with kebab-case filenames. Create `.stories.tsx` with all states. Export props as TypeScript interface. Use design tokens exclusively—no hardcoded colors, fonts, or spacing. Import `cn` from `@/lib/utils` for conditional class merging.

Applied to files:

  • web/src/pages/setup/ThemePreview.tsx
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/**/*.{ts,tsx} : Web Dashboard: ALWAYS reuse existing components from `web/src/components/ui/` before creating new ones. Reuse StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton variants, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup, Drawer, InputField, SelectField, SliderField, ToggleField, TaskStatusIndicator, PriorityBadge, ProviderHealthBadge, TokenUsageBar, CodeMirrorEditor, SegmentedControl, ThemeToggle.

Applied to files:

  • web/src/pages/setup/ThemePreview.tsx
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/pages/**/*.{ts,tsx} : Web dashboard pages: Lazy-loaded page components (one per route). Page-scoped sub-components in `pages/<page-name>/` subdirs. Feature dirs added as pages are built. Stores for auth, WebSocket, toast, analytics, setup wizard, company, agents, budget, tasks, settings, providers, theme.

Applied to files:

  • web/src/pages/setup/ThemePreview.tsx
📚 Learning: 2026-03-15T21:20:09.993Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:20:09.993Z
Learning: Applies to web/src/components/** : Vue components organized by feature (agents/, approvals/, budget/, common/, dashboard/, layout/, messages/, org-chart/, tasks/).

Applied to files:

  • web/src/pages/setup/ThemePreview.tsx
📚 Learning: 2026-03-16T07:22:28.134Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T07:22:28.134Z
Learning: Applies to **/*.py : Functions must be less than 50 lines; files must be less than 800 lines

Applied to files:

  • src/synthorg/providers/management/_helpers.py
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to **/*.py : Functions must be < 50 lines, files < 800 lines.

Applied to files:

  • src/synthorg/providers/management/_helpers.py
📚 Learning: 2026-03-17T13:48:18.114Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T13:48:18.114Z
Learning: Applies to **/*.py : Functions: < 50 lines, files < 800 lines.

Applied to files:

  • src/synthorg/providers/management/_helpers.py
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to tests/**/*.py : Tests must use test-provider, test-small-001, etc. for vendor-agnostic test data.

Applied to files:

  • src/synthorg/providers/management/_helpers.py
📚 Learning: 2026-03-16T19:13:36.562Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T19:13:36.562Z
Learning: Applies to src/synthorg/providers/**/*.py : RetryConfig and RateLimiterConfig are set per-provider in ProviderConfig.

Applied to files:

  • src/synthorg/providers/management/_helpers.py
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/**/*.py : `RetryConfig` and `RateLimiterConfig` are set per-provider in `ProviderConfig`. Retryable errors: `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`. Non-retryable errors raise immediately.

Applied to files:

  • src/synthorg/providers/management/_helpers.py
🔇 Additional comments (15)
web/src/__tests__/utils/dashboard.test.ts (1)

216-238: Good edge-case coverage for non-finite org health values.

The added tests on Line 216, Line 224, and Line 232 correctly cover NaN/Infinity filtering and the all-invalid fallback to null. This is a solid safeguard against NaN% regressions in dashboard health rendering.

web/src/components/ui/progress-gauge.tsx (1)

30-33: LGTM! Non-finite value sanitization is now complete.

Both max and value are properly guarded with Number.isFinite() checks before arithmetic operations. This ensures:

  • NaN / Infinity / -Infinity for value → defaults to 0
  • NaN / Infinity / -Infinity for max → defaults to 1
  • max=0 → still handled by Math.max(max, 1) to avoid division by zero

This resolves the past review comment and makes the component robust against non-finite inputs from upstream calculations (e.g., OrgHealthSection.overallHealth or BudgetGauge.usedPercent).

web/src/__tests__/components/ui/progress-gauge.test.tsx (1)

90-93: Good edge-case coverage for non-finite inputs.

The NaN tests clearly document the component's defensive behavior:

  • NaN value → 0%
  • NaN max → treated as 1, so value=50 clamps to 1, yielding 100%

The inline comment on line 102 is helpful for understanding the expected math.

Also applies to: 100-104

web/src/__tests__/utils/format.test.ts (1)

5-5: Import update looks correct.

formatCurrencyCompact is correctly added to support the new test suite.

web/src/pages/providers/ProviderFormDrawer.tsx (2)

2-2: Verify the Radix UI import path.

The import from 'radix-ui' appears incorrect. Radix UI components are published under the @radix-ui/react-* namespace. The Dialog component should be imported from @radix-ui/react-dialog.

[raise_critical_issue, request_verification]

🐛 Proposed fix
-import { Dialog } from 'radix-ui'
+import * as Dialog from '@radix-ui/react-dialog'
What is the correct import path for Radix UI Dialog component?

225-227: LGTM!

The handleOpenChange callback correctly delegates to handleClose() on dismissal, maintaining behavioral parity with the previous Drawer component where Escape, overlay click, and explicit close button all trigger form reset and the onClose callback.

src/synthorg/providers/presets.py (1)

313-319: Past review flagged vendor names in MODEL_VERSION_FILTERS.

This concern was already raised in a previous review. The anthropic key and claude-* pattern violate the vendor-agnostic naming rule. The inline comment claims exemption, but the coding guidelines don't list presets.py as an allowed location for vendor names (only docs/design/operations.md and .claude/ files are exempt).

src/synthorg/observability/events/provider.py (1)

83-88: LGTM!

The new LiteLLM event constants follow the established naming pattern (provider.litellm.<action>) and are correctly typed with Final[str]. The section header and organization are consistent with the rest of the file.

tests/unit/providers/drivers/test_litellm_auth.py (1)

124-148: LGTM!

The subscription auth tests correctly verify the updated behavior where auth_token is set at the top level instead of extra_headers. The object.__setattr__ workaround for simulating a cleared token is well-documented with the comment explaining why direct construction with None isn't possible due to validators.

src/synthorg/providers/management/service.py (1)

391-395: LGTM! Past review concern addressed.

The lazy lookup pattern correctly avoids unnecessary models_from_litellm calls when request.models is explicitly provided. The fallback chain (request.modelslitellm_modelspreset.default_models) is clear and handles all cases appropriately.

docs/design/operations.md (1)

50-51: LGTM!

The documentation updates accurately reflect the implementation changes:

  • Subscription token is now passed via LiteLLM auth_token (matching the driver change)
  • LiteLLM model database behavior (filtering, deduplication, fallback) is well documented
  • Vendor-specific references (e.g., "4.5+ for Anthropic") are appropriate for this design document

Also applies to: 107-107, 124-124

tests/unit/providers/management/test_helpers.py (1)

158-331: LGTM!

Comprehensive test coverage for models_from_litellm:

  • Filtering, deduplication, prefix stripping, cost conversion, max_context fallback
  • Edge cases: None costs, non-integer max_input_tokens, malformed entries, unknown providers
  • Version filter application via MODEL_VERSION_FILTERS mock
  • Import failure simulation with proper cleanup in finally block
  • Uses vendor-agnostic test identifiers (test-provider, test-large-001, etc.)
src/synthorg/providers/management/_helpers.py (3)

299-365: Past review addressed: function length and vendor names.

The refactoring extracted _parse_litellm_entry as a helper, and the docstrings now use vendor-agnostic placeholders (example-large-001, example-provider). The function body of models_from_litellm is ~46 lines (excluding docstring), which is compliant with the <50 line guideline.

The deduplication logic correctly prefers shorter model IDs over dated variants, and the sorting ensures deterministic output.


234-238: LGTM!

Good defensive logging for the edge case where URL port parsing fails. DEBUG level is appropriate since this is informational rather than an error condition.


245-296: LGTM!

Well-structured helper that cleanly separates the parsing logic. The exception handling with DEBUG-level logging for malformed entries is appropriate—these are data quality issues in the upstream LiteLLM database rather than application errors.

Comment on lines +358 to +366
hint={
isCustom || mode === 'edit'
? undefined
: preset && !preset.default_base_url
? 'Required for this provider'
: !preset?.default_base_url
? 'Optional for known cloud providers'
: undefined
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Simplify the nested ternary logic for the Base URL hint.

The nested ternary has confusing conditions. The third branch (!preset?.default_base_url at line 363) can only be reached when preset is falsy, which would display "Optional for known cloud providers" in an unexpected context. Consider simplifying:

♻️ Proposed simplification
                       hint={
-                          isCustom || mode === 'edit'
-                            ? undefined
-                            : preset && !preset.default_base_url
-                              ? 'Required for this provider'
-                              : !preset?.default_base_url
-                                ? 'Optional for known cloud providers'
-                                : undefined
+                          isCustom || mode === 'edit' || preset?.default_base_url
+                            ? undefined
+                            : preset
+                              ? 'Required for this provider'
+                              : undefined
                       }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
hint={
isCustom || mode === 'edit'
? undefined
: preset && !preset.default_base_url
? 'Required for this provider'
: !preset?.default_base_url
? 'Optional for known cloud providers'
: undefined
}
hint={
isCustom || mode === 'edit' || preset?.default_base_url
? undefined
: preset
? 'Required for this provider'
: undefined
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/providers/ProviderFormDrawer.tsx` around lines 358 - 366, The
nested ternary for the hint prop is confusing and reachable branches are
incorrect; update the hint logic in ProviderFormDrawer (the hint prop that
references isCustom, mode and preset.default_base_url) to an explicit sequence:
if isCustom || mode === 'edit' return undefined; else if preset is falsy return
'Optional for known cloud providers'; else if preset.default_base_url is falsy
return 'Required for this provider'; otherwise return undefined — replace the
nested ternary with this clearer conditional order using the hint prop.

Comment on lines +97 to +101
useEffect(() => {
if (animation === 'instant') return
const interval = setInterval(() => setCycling((v) => !v), 1500)
return () => clearInterval(interval)
}, [animation])
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

cat -n web/src/pages/setup/ThemePreview.tsx

Repository: Aureliolo/synthorg

Length of output: 7792


Reset cycling to ensure the demo card appears immediately when the animation mode changes.

When switching away from instant animation, if the 1500ms cycle happens to have cycling as false, the card disappears until the next toggle, making the preview momentarily blank.

🐛 Suggested fix
 useEffect(() => {
+  setCycling(true)
   if (animation === 'instant') return
   const interval = setInterval(() => setCycling((v) => !v), 1500)
   return () => clearInterval(interval)
 }, [animation])
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
useEffect(() => {
if (animation === 'instant') return
const interval = setInterval(() => setCycling((v) => !v), 1500)
return () => clearInterval(interval)
}, [animation])
useEffect(() => {
setCycling(true)
if (animation === 'instant') return
const interval = setInterval(() => setCycling((v) => !v), 1500)
return () => clearInterval(interval)
}, [animation])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/setup/ThemePreview.tsx` around lines 97 - 101, The useEffect
that starts the interval when "animation" changes can leave "cycling" false
causing a blank preview; inside the effect (the one referencing animation and
creating interval), reset cycling to true before creating the interval so the
demo card shows immediately when switching away from 'instant' (use the existing
setCycling state setter), then create the interval and clear it in the cleanup
as before.

Comment on lines +115 to +124
<motion.div
animate={{ scale: [1, 1.3, 1] }}
transition={{
...transition,
repeat: Infinity,
repeatDelay: 1,
duration: animation === 'instant' ? 0 : 0.6,
}}
className="size-2 rounded-full bg-success"
/>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Use StatusBadge for the active indicator.

This reintroduces an inline status dot. Keep the motion wrapper if you want the pulse, but render the shared status primitive inside it instead of a one-off circle.

As per coding guidelines: "Do NOT recreate status dots inline—use <StatusBadge>."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/setup/ThemePreview.tsx` around lines 115 - 124, Replace the
inline status circle inside the motion.div with the shared StatusBadge
component: keep the motion.div wrapper and its animate/transition logic
(including use of animation and transition), remove the manual classes (size-2
rounded-full bg-success), and render <StatusBadge> inside the motion container
with the matching sizing/variant props (e.g., size="2" or className="size-2" and
variant/status="success") so the shared status primitive is used for the active
indicator.

- Rename ProviderFormDrawer to ProviderFormModal (file + component + 3 import sites)
- Add Dialog.Description for Radix accessibility
- Simplify unreachable nested ternary in base URL hint
- Type PALETTE_CLASS and ANIMATION_TRANSITIONS with ThemeSettings unions
- Type AnimationDemo animation prop as ThemeSettings['animation']
- Fix animation demo interval cleanup with active flag
- Normalize currencyCode to 3-letter uppercase ISO 4217 before Intl.NumberFormat
- Fix test name: Infinity treated as non-finite, not clamped to max
- Tighten formatCurrencyCompact test assertion to check currency marker + abbreviation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@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)
web/src/pages/setup/ProvidersStep.tsx (1)

104-105: 🧹 Nitpick | 🔵 Trivial

Update stale comment referencing old component name.

The comment still references ProviderFormDrawer but the component has been renamed to ProviderFormModal.

♻️ Proposed fix
-  // Overrides for ProviderFormDrawer to use setup wizard store
+  // Overrides for ProviderFormModal to use setup wizard store
   const drawerOverrides: ProviderFormOverrides = useMemo(() => ({
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/setup/ProvidersStep.tsx` around lines 104 - 105, Update the
stale comment that currently mentions ProviderFormDrawer to reflect the new
component name ProviderFormModal; locate the comment immediately above the
declaration of the drawerOverrides constant (type ProviderFormOverrides) in
ProvidersStep (function/component ProvidersStep in ProvidersStep.tsx) and change
the text to reference ProviderFormModal so the comment accurately describes the
overrides being created for that component.
♻️ Duplicate comments (5)
web/src/pages/setup/ThemePreview.tsx (3)

97-108: ⚠️ Potential issue | 🟡 Minor

Reset cycling on animation-mode changes to avoid blank preview states.

When switching from instant to an animated mode, cycling can remain false until the first interval tick, causing a temporary empty preview. Set it to true at effect start.

🐛 Proposed fix
 useEffect(() => {
+  setCycling(true)
   if (animation === 'instant') return
   // Start with the card visible; interval toggles visibility
   let active = true
   const interval = setInterval(() => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/setup/ThemePreview.tsx` around lines 97 - 108, The effect that
toggles preview visibility (the useEffect watching animation) should reset
cycling immediately when switching out of 'instant' mode to avoid a blank
preview; inside that effect (the block that currently creates interval and uses
active/interval) call setCycling(true) at the start whenever animation !==
'instant' so the card is visible until the interval toggles it, and keep the
existing cleanup (active = false and clearInterval(interval)) unchanged; update
the effect surrounding animation, setCycling, and cycling logic accordingly.

122-131: ⚠️ Potential issue | 🟠 Major

Use shared StatusBadge instead of an inline status dot.

The animated indicator reintroduces a custom status dot. Keep the motion wrapper, but render the shared status primitive inside it.

♻️ Proposed fix
+import { StatusBadge } from '@/components/ui/status-badge'
...
             <motion.div
               animate={{ scale: [1, 1.3, 1] }}
               transition={{
                 ...transition,
                 repeat: Infinity,
                 repeatDelay: 1,
                 duration: animation === 'instant' ? 0 : 0.6,
               }}
-              className="size-2 rounded-full bg-success"
-            />
+            >
+              <StatusBadge status="success" className="size-2" />
+            </motion.div>

As per coding guidelines: “Do NOT recreate status dots inline—use <StatusBadge>.”

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/setup/ThemePreview.tsx` around lines 122 - 131, Replace the
inline status dot inside the motion wrapper in ThemePreview.tsx with the shared
StatusBadge component: keep the motion.div (animate, transition, repeat settings
and duration logic using the animation variable) but render <StatusBadge> as its
child instead of the custom <div>; pass the equivalent styling/semantics to
StatusBadge (e.g., success variant and appropriate size/rounded props that match
"size-2 rounded-full bg-success") so the animation still wraps the shared
primitive rather than recreating the dot inline.

70-70: ⚠️ Potential issue | 🟠 Major

Replace invalid token utility classes before merge.

bg-bg-surface (Line 70, Line 120) and text-text-muted (Line 84, Line 136) are not valid semantic utility classes in this codebase’s token scheme; they should be replaced with valid token-mapped classes.

♻️ Proposed fix
- 'flex flex-col gap-2 rounded-lg border border-border bg-bg-surface p-2 transition-all duration-200',
+ 'flex flex-col gap-2 rounded-lg border border-border bg-surface p-2 transition-all duration-200',

- <div className="mt-auto flex justify-center pt-1 text-text-muted">
+ <div className="mt-auto flex justify-center pt-1 text-muted-foreground">

- className="flex items-center gap-2 rounded-md border border-border bg-bg-surface px-3 py-1.5"
+ className="flex items-center gap-2 rounded-md border border-border bg-surface px-3 py-1.5"

- <span className="text-[9px] text-text-muted italic">
+ <span className="text-[9px] text-muted-foreground italic">

As per coding guidelines: “Design tokens: Colors use Tailwind semantic classes … NEVER hardcode hex values.”

Also applies to: 84-84, 120-120, 136-136

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/setup/ThemePreview.tsx` at line 70, In the ThemePreview
component update the invalid utility tokens used in the className
strings—replace occurrences of "bg-bg-surface" and "text-text-muted" with the
project's semantic token-mapped Tailwind classes (the appropriate surface and
muted text tokens used across the codebase) so the JSX className values (e.g.,
the string that currently contains 'flex flex-col gap-2 rounded-lg border
border-border bg-bg-surface p-2 ...' and the similar strings containing
'text-text-muted') use the correct token names; locate these className strings
inside the ThemePreview component and swap the invalid tokens for the correct
semantic tokens consistent with other components.
web/src/utils/format.ts (1)

68-68: ⚠️ Potential issue | 🟡 Minor

Trim currencyCode before ISO validation.

Valid values with whitespace (for example " usd ") currently fail the regex and silently fall back to EUR. Normalize with trim() before validating/upcasing.

Proposed fix
-  const code = /^[A-Za-z]{3}$/.test(currencyCode) ? currencyCode.toUpperCase() : 'EUR'
+  const normalized = currencyCode.trim().toUpperCase()
+  const code = /^[A-Z]{3}$/.test(normalized) ? normalized : 'EUR'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/utils/format.ts` at line 68, Trim currencyCode before validating so
values with surrounding whitespace pass the ISO check: when computing code (the
variable using /^[A-Za-z]{3}$/), run currencyCode.trim() and then test and call
toUpperCase on the trimmed value (referencing the existing currencyCode and code
variables) so that strings like " usd " validate and become "USD" instead of
falling back to "EUR".
web/src/pages/providers/ProviderFormModal.tsx (1)

361-367: 🧹 Nitpick | 🔵 Trivial

The nested ternary logic for the Base URL hint is still complex.

This was flagged in a previous review but remains unaddressed. The condition flow is confusing: when !preset?.default_base_url is checked at line 364, if preset is falsy the expression evaluates to true, showing "Required for this provider" even when no preset is selected.

Consider simplifying to clearer conditional logic:

♻️ Proposed simplification
                       hint={
-                          isCustom || mode === 'edit'
-                            ? undefined
-                            : !preset?.default_base_url
-                              ? 'Required for this provider'
-                              : undefined
+                          isCustom || mode === 'edit' || !preset
+                            ? undefined
+                            : !preset.default_base_url
+                              ? 'Required for this provider'
+                              : undefined
                       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/providers/ProviderFormModal.tsx` around lines 361 - 367, The
nested ternary for the hint prop shows "Required for this provider" when no
preset is selected because it evaluates !preset?.default_base_url as true for a
falsy preset; fix by making the intent explicit: compute a small boolean or
variable (e.g. baseUrlHint) before JSX using the existing symbols isCustom, mode
and preset so that the hint is only "Required for this provider" when NOT
isCustom, NOT mode==='edit', AND preset exists but preset.default_base_url is
falsy; then use that variable in the hint prop instead of the nested ternary to
simplify and clarify the logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/src/__tests__/components/ui/progress-gauge.test.tsx`:
- Around line 90-104: Add a complementary test to cover -Infinity: in the
ProgressGauge test suite add an it block (e.g., it('treats -Infinity as
non-finite and defaults to 0%')) that renders <ProgressGauge value={-Infinity}
/> and asserts screen.getByText('0%') is present; place it alongside the
existing Infinity/NaN cases so the test suite documents that negative infinity
follows the same non-finite guard path.

In `@web/src/__tests__/utils/format.test.ts`:
- Around line 210-214: The test for invalid currency fallback should assert
EUR-specific output rather than merely checking for "100": update the test that
calls formatCurrencyCompact(100, 'INVALID') to verify it falls back to EUR by
either asserting the result contains the EUR currency code or symbol (e.g.,
contains 'EUR' or '€') or by comparing the result to the known-good fallback
like formatCurrencyCompact(100, 'EUR'); adjust the expectation in the it('falls
back gracefully for invalid currency code', ...) block to use one of these
stronger assertions referencing formatCurrencyCompact.

---

Outside diff comments:
In `@web/src/pages/setup/ProvidersStep.tsx`:
- Around line 104-105: Update the stale comment that currently mentions
ProviderFormDrawer to reflect the new component name ProviderFormModal; locate
the comment immediately above the declaration of the drawerOverrides constant
(type ProviderFormOverrides) in ProvidersStep (function/component ProvidersStep
in ProvidersStep.tsx) and change the text to reference ProviderFormModal so the
comment accurately describes the overrides being created for that component.

---

Duplicate comments:
In `@web/src/pages/providers/ProviderFormModal.tsx`:
- Around line 361-367: The nested ternary for the hint prop shows "Required for
this provider" when no preset is selected because it evaluates
!preset?.default_base_url as true for a falsy preset; fix by making the intent
explicit: compute a small boolean or variable (e.g. baseUrlHint) before JSX
using the existing symbols isCustom, mode and preset so that the hint is only
"Required for this provider" when NOT isCustom, NOT mode==='edit', AND preset
exists but preset.default_base_url is falsy; then use that variable in the hint
prop instead of the nested ternary to simplify and clarify the logic.

In `@web/src/pages/setup/ThemePreview.tsx`:
- Around line 97-108: The effect that toggles preview visibility (the useEffect
watching animation) should reset cycling immediately when switching out of
'instant' mode to avoid a blank preview; inside that effect (the block that
currently creates interval and uses active/interval) call setCycling(true) at
the start whenever animation !== 'instant' so the card is visible until the
interval toggles it, and keep the existing cleanup (active = false and
clearInterval(interval)) unchanged; update the effect surrounding animation,
setCycling, and cycling logic accordingly.
- Around line 122-131: Replace the inline status dot inside the motion wrapper
in ThemePreview.tsx with the shared StatusBadge component: keep the motion.div
(animate, transition, repeat settings and duration logic using the animation
variable) but render <StatusBadge> as its child instead of the custom <div>;
pass the equivalent styling/semantics to StatusBadge (e.g., success variant and
appropriate size/rounded props that match "size-2 rounded-full bg-success") so
the animation still wraps the shared primitive rather than recreating the dot
inline.
- Line 70: In the ThemePreview component update the invalid utility tokens used
in the className strings—replace occurrences of "bg-bg-surface" and
"text-text-muted" with the project's semantic token-mapped Tailwind classes (the
appropriate surface and muted text tokens used across the codebase) so the JSX
className values (e.g., the string that currently contains 'flex flex-col gap-2
rounded-lg border border-border bg-bg-surface p-2 ...' and the similar strings
containing 'text-text-muted') use the correct token names; locate these
className strings inside the ThemePreview component and swap the invalid tokens
for the correct semantic tokens consistent with other components.

In `@web/src/utils/format.ts`:
- Line 68: Trim currencyCode before validating so values with surrounding
whitespace pass the ISO check: when computing code (the variable using
/^[A-Za-z]{3}$/), run currencyCode.trim() and then test and call toUpperCase on
the trimmed value (referencing the existing currencyCode and code variables) so
that strings like " usd " validate and become "USD" instead of falling back to
"EUR".
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c03c2084-c0a7-453e-a197-947fd4aee89c

📥 Commits

Reviewing files that changed from the base of the PR and between 2bb74b2 and 2aeadc2.

📒 Files selected for processing (8)
  • web/src/__tests__/components/ui/progress-gauge.test.tsx
  • web/src/__tests__/utils/format.test.ts
  • web/src/pages/ProviderDetailPage.tsx
  • web/src/pages/ProvidersPage.tsx
  • web/src/pages/providers/ProviderFormModal.tsx
  • web/src/pages/setup/ProvidersStep.tsx
  • web/src/pages/setup/ThemePreview.tsx
  • web/src/utils/format.ts
📜 Review details
⏰ 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). (6)
  • GitHub Check: Build Backend
  • GitHub Check: Build Web
  • GitHub Check: Test (Python 3.14)
  • GitHub Check: Dashboard Test
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (5)
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

web/src/**/*.{ts,tsx}: Web Dashboard: ALWAYS reuse existing components from web/src/components/ui/ before creating new ones. Reuse StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton variants, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup, Drawer, InputField, SelectField, SliderField, ToggleField, TaskStatusIndicator, PriorityBadge, ProviderHealthBadge, TokenUsageBar, CodeMirrorEditor, SegmentedControl, ThemeToggle.
Do NOT recreate status dots inline—use <StatusBadge>. Do NOT build card-with-header layouts from scratch—use <SectionCard>. Do NOT create metric displays with text-metric font-bold—use <MetricCard>. Do NOT render initials circles manually—use <Avatar>. Do NOT create complex (>8 line) JSX inside .map()—extract to a shared component.
Web design system enforcement: A PostToolUse hook (scripts/check_web_design_system.py) runs automatically on every Edit/Write to web/src/ files. It catches hardcoded hex colors, rgba values, font-family declarations, missing Storybook stories, duplicate patterns, and complex map blocks. Fix all violations before proceeding.
React 19 + shadcn/ui + Tailwind CSS 4 dashboard: Use React Router, Zustand stores, @tanstack/react-query, @xyflow/react, @dagrejs/dagre, d3-force, @dnd-kit, Recharts, Framer Motion, cmdk, js-yaml, Axios.

Files:

  • web/src/pages/ProvidersPage.tsx
  • web/src/__tests__/components/ui/progress-gauge.test.tsx
  • web/src/pages/ProviderDetailPage.tsx
  • web/src/__tests__/utils/format.test.ts
  • web/src/pages/setup/ProvidersStep.tsx
  • web/src/utils/format.ts
  • web/src/pages/setup/ThemePreview.tsx
  • web/src/pages/providers/ProviderFormModal.tsx
web/src/**/*.{ts,tsx,css}

📄 CodeRabbit inference engine (CLAUDE.md)

Design tokens: Colors use Tailwind semantic classes (text-foreground, bg-card, text-accent, etc.) or CSS variables (var(--so-accent)). NEVER hardcode hex values. Typography uses font-sans or font-mono. Spacing uses density-aware tokens or standard Tailwind. Shadows/borders use token variables.

Files:

  • web/src/pages/ProvidersPage.tsx
  • web/src/__tests__/components/ui/progress-gauge.test.tsx
  • web/src/pages/ProviderDetailPage.tsx
  • web/src/__tests__/utils/format.test.ts
  • web/src/pages/setup/ProvidersStep.tsx
  • web/src/utils/format.ts
  • web/src/pages/setup/ThemePreview.tsx
  • web/src/pages/providers/ProviderFormModal.tsx
**/*.{ts,tsx,json}

📄 CodeRabbit inference engine (CLAUDE.md)

TypeScript 6.0: Remove baseUrl (deprecated); paths entries are relative to tsconfig directory. esModuleInterop always true—cannot be set to false. types defaults to []—must explicitly list (e.g. 'types': ['vitest/globals']). DOM.Iterable merged into DOM. moduleResolution: 'classic' and 'node10' removed—use 'bundler' or 'nodenext'. strict defaults to true. noUncheckedSideEffectImports defaults to true.

Files:

  • web/src/pages/ProvidersPage.tsx
  • web/src/__tests__/components/ui/progress-gauge.test.tsx
  • web/src/pages/ProviderDetailPage.tsx
  • web/src/__tests__/utils/format.test.ts
  • web/src/pages/setup/ProvidersStep.tsx
  • web/src/utils/format.ts
  • web/src/pages/setup/ThemePreview.tsx
  • web/src/pages/providers/ProviderFormModal.tsx
**/*.{py,ts,tsx,go}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{py,ts,tsx,go}: Lint & format: Python via ruff (uv run ruff check + uv run ruff format). TypeScript web via ESLint (zero warnings enforced: npm --prefix web run lint). Go CLI via golangci-lint and go vet. All enforced in pre-commit hooks.
Type checking: Python via mypy strict mode (uv run mypy src/ tests/). TypeScript via npm --prefix web run type-check. Go via go -C cli vet ./....

Files:

  • web/src/pages/ProvidersPage.tsx
  • web/src/__tests__/components/ui/progress-gauge.test.tsx
  • web/src/pages/ProviderDetailPage.tsx
  • web/src/__tests__/utils/format.test.ts
  • web/src/pages/setup/ProvidersStep.tsx
  • web/src/utils/format.ts
  • web/src/pages/setup/ThemePreview.tsx
  • web/src/pages/providers/ProviderFormModal.tsx
web/src/pages/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Web dashboard pages: Lazy-loaded page components (one per route). Page-scoped sub-components in pages/<page-name>/ subdirs. Feature dirs added as pages are built. Stores for auth, WebSocket, toast, analytics, setup wizard, company, agents, budget, tasks, settings, providers, theme.

Files:

  • web/src/pages/ProvidersPage.tsx
  • web/src/pages/ProviderDetailPage.tsx
  • web/src/pages/setup/ProvidersStep.tsx
  • web/src/pages/setup/ThemePreview.tsx
  • web/src/pages/providers/ProviderFormModal.tsx
🧠 Learnings (10)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/providers/**/*.py : Providers: LLM provider abstraction (LiteLLM adapter), auth types (api_key/oauth/custom_header/none), presets (PROVIDER_PRESETS), runtime CRUD (ProviderManagementService with asyncio.Lock serialization), hot-reload via AppState swap.
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/**/*.{ts,tsx} : React 19 + shadcn/ui + Tailwind CSS 4 dashboard: Use React Router, Zustand stores, tanstack/react-query, xyflow/react, dagrejs/dagre, d3-force, dnd-kit, Recharts, Framer Motion, cmdk, js-yaml, Axios.

Applied to files:

  • web/src/pages/ProvidersPage.tsx
  • web/src/pages/ProviderDetailPage.tsx
  • web/src/pages/setup/ThemePreview.tsx
  • web/src/pages/providers/ProviderFormModal.tsx
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to web/src/__tests__/**/*.{ts,js} : Dashboard testing: Vitest unit tests organized by feature under `web/src/__tests__/`. Use fast-check for property-based testing (`fc.assert` + `fc.property`).

Applied to files:

  • web/src/__tests__/components/ui/progress-gauge.test.tsx
  • web/src/__tests__/utils/format.test.ts
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do NOT recreate status dots inline—use `<StatusBadge>`. Do NOT build card-with-header layouts from scratch—use `<SectionCard>`. Do NOT create metric displays with `text-metric font-bold`—use `<MetricCard>`. Do NOT render initials circles manually—use `<Avatar>`. Do NOT create complex (>8 line) JSX inside `.map()`—extract to a shared component.

Applied to files:

  • web/src/utils/format.ts
  • web/src/pages/setup/ThemePreview.tsx
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : Web components: Place new shared components in `web/src/components/ui/` with kebab-case filenames. Create `.stories.tsx` with all states. Export props as TypeScript interface. Use design tokens exclusively—no hardcoded colors, fonts, or spacing. Import `cn` from `@/lib/utils` for conditional class merging.

Applied to files:

  • web/src/pages/setup/ThemePreview.tsx
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/**/*.stories.{ts,tsx} : Storybook 10: ESM-only—no CJS support. Packages like `storybook/addon-essentials`, `storybook/addon-interactions`, `storybook/test`, `storybook/blocks` no longer published. `storybook/addon-docs` is separate. Import from `storybook/test` (not `storybook/test`), `storybook/actions`. Use `defineMain` from `storybook/react-vite/node` and `definePreview` from `storybook/react-vite`. Backgrounds API: use `parameters.backgrounds.options` (object) + `initialGlobals.backgrounds.value`. a11y testing: use `parameters.a11y.test: 'error' | 'todo' | 'off'`. Minimum versions: Node 20.19+, Vite 5+, Vitest 3+, TypeScript 4.9+.

Applied to files:

  • web/src/pages/setup/ThemePreview.tsx
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/**/*.{ts,tsx,css} : Design tokens: Colors use Tailwind semantic classes (`text-foreground`, `bg-card`, `text-accent`, etc.) or CSS variables (`var(--so-accent)`). NEVER hardcode hex values. Typography uses `font-sans` or `font-mono`. Spacing uses density-aware tokens or standard Tailwind. Shadows/borders use token variables.

Applied to files:

  • web/src/pages/setup/ThemePreview.tsx
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/**/*.{ts,tsx} : Web design system enforcement: A PostToolUse hook (`scripts/check_web_design_system.py`) runs automatically on every Edit/Write to `web/src/` files. It catches hardcoded hex colors, rgba values, font-family declarations, missing Storybook stories, duplicate patterns, and complex map blocks. Fix all violations before proceeding.

Applied to files:

  • web/src/pages/setup/ThemePreview.tsx
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/**/*.{ts,tsx} : Web Dashboard: ALWAYS reuse existing components from `web/src/components/ui/` before creating new ones. Reuse StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton variants, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup, Drawer, InputField, SelectField, SliderField, ToggleField, TaskStatusIndicator, PriorityBadge, ProviderHealthBadge, TokenUsageBar, CodeMirrorEditor, SegmentedControl, ThemeToggle.

Applied to files:

  • web/src/pages/setup/ThemePreview.tsx
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/pages/**/*.{ts,tsx} : Web dashboard pages: Lazy-loaded page components (one per route). Page-scoped sub-components in `pages/<page-name>/` subdirs. Feature dirs added as pages are built. Stores for auth, WebSocket, toast, analytics, setup wizard, company, agents, budget, tasks, settings, providers, theme.

Applied to files:

  • web/src/pages/setup/ThemePreview.tsx
🔇 Additional comments (6)
web/src/__tests__/utils/format.test.ts (1)

192-196: Compact-notation assertion improvement looks good.

This is a meaningful semantic check and avoids the previous length-based false positives.

web/src/pages/providers/ProviderFormModal.tsx (2)

246-267: Good accessibility implementation.

The Dialog header correctly includes:

  • Dialog.Title for the accessible name
  • Dialog.Description with sr-only class for screen readers (addresses previous review feedback)
  • Close button with proper aria-label="Close" and focus-visible ring

2-2: No issues found. The Radix UI import path is correct.

The project uses the radix-ui meta-package (version ^1.4.3), which re-exports all primitives including Dialog. The import import { Dialog } from 'radix-ui' is valid and will not cause module resolution failure. Dialog usage throughout the file (Dialog.Root, Dialog.Portal, Dialog.Overlay, Dialog.Content, Dialog.Title, Dialog.Description, Dialog.Close) confirms the import is working as intended.

web/src/pages/ProvidersPage.tsx (1)

9-9: Clean migration from drawer to modal.

The import and usage of ProviderFormModal are correct. The same props (open, onClose, mode="create") work seamlessly with the new modal component.

Minor note: The state variable drawerOpen (line 14) could be renamed to modalOpen for consistency, but this is purely cosmetic.

Also applies to: 56-60

web/src/pages/ProviderDetailPage.tsx (1)

102-108: LGTM - Modal migration for edit mode is correct.

The ProviderFormModal is properly wired for edit mode with mode="edit" and provider prop. The component will pre-fill form fields from the existing provider configuration.

web/src/pages/setup/ProvidersStep.tsx (1)

11-11: Migration to modal component is correct.

The import of ProviderFormModal and ProviderFormOverrides type is correct. The overrides object (lines 105-124) correctly implements all required fields of the ProviderFormOverrides interface, and the modal is rendered with the proper props.

Also applies to: 201-206

- Add -Infinity test case for ProgressGauge non-finite guard
- Fix invalid currency test to assert EUR normalization output
- Update stale ProviderFormDrawer comment to ProviderFormModal
- Extract baseUrlHint variable from nested ternary in ProviderFormModal
- Trim currencyCode before ISO 4217 validation in formatCurrencyCompact

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@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

♻️ Duplicate comments (1)
web/src/utils/format.ts (1)

67-79: ⚠️ Potential issue | 🟠 Major

Line 69 still permits exception-driven formatting for unsupported 3-letter codes.

/^[A-Za-z]{3}$/ accepts values that are not actually supported by Intl.NumberFormat (e.g., "ZZZ"). In chart ticks, that can repeatedly throw and log on every render.

Proposed hardening (validate once, avoid repeated throws)
+const VALID_COMPACT_CURRENCIES = new Set<string>()
+const INVALID_COMPACT_CURRENCIES = new Set<string>()
+
+function normalizeCompactCurrencyCode(currencyCode: string): string {
+  const upper = currencyCode.trim().toUpperCase()
+  if (!/^[A-Z]{3}$/.test(upper)) return 'EUR'
+  if (VALID_COMPACT_CURRENCIES.has(upper)) return upper
+  if (INVALID_COMPACT_CURRENCIES.has(upper)) return 'EUR'
+
+  try {
+    new Intl.NumberFormat('en-US', { style: 'currency', currency: upper })
+    VALID_COMPACT_CURRENCIES.add(upper)
+    return upper
+  } catch {
+    INVALID_COMPACT_CURRENCIES.add(upper)
+    return 'EUR'
+  }
+}
+
 export function formatCurrencyCompact(value: number, currencyCode: string = 'EUR'): string {
   if (!Number.isFinite(value)) return '--'
-  // Normalize to 3-letter uppercase ISO 4217 code; fall back to EUR
-  const trimmed = currencyCode.trim()
-  const code = /^[A-Za-z]{3}$/.test(trimmed) ? trimmed.toUpperCase() : 'EUR'
+  const code = normalizeCompactCurrencyCode(currencyCode)
   try {
     return new Intl.NumberFormat('en-US', {
       style: 'currency',
       currency: code,
       maximumFractionDigits: 0,
       notation: 'compact',
     }).format(value)
Does `Intl.NumberFormat('en-US', { style: 'currency', currency: 'ZZZ' })` throw a RangeError, and what is current browser support for `Intl.supportedValuesOf('currency')`?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/utils/format.ts` around lines 67 - 79, The code currently allows any
3-letter string (trimmed -> code) and relies on try/catch at format time;
instead validate the currency once and avoid per-render exceptions: use
Intl.supportedValuesOf?.('currency') to check trimmed.toUpperCase() and set code
= valid ? trimmedUpper : 'EUR'; if supportedValuesOf is not available, perform a
one-time cached check (e.g., attempt new
Intl.NumberFormat('en-US',{style:'currency',currency:trimmedUpper}) once and
store the result in a Map like validCurrencyCache) and then reuse that cached
boolean to choose the fallback 'EUR' before calling new Intl.NumberFormat to
format value; update references to trimmed and code in utils/format.ts and
remove reliance on try/catch for unsupported currencies.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/src/__tests__/utils/format.test.ts`:
- Around line 186-220: Add a test inside the existing "formatCurrencyCompact"
test suite that verifies currency normalization trims whitespace and uppercases
input: call formatCurrencyCompact(100, ' usd ') and assert it equals
formatCurrencyCompact(100, 'USD') (or matches same output), referencing the
existing describe('formatCurrencyCompact') block and the formatCurrencyCompact
function to ensure the trim+uppercase path is covered.

In `@web/src/pages/providers/ProviderFormModal.tsx`:
- Around line 231-233: The modal's open-change handlers (notably
handleOpenChange) currently allow closing even when a save is in progress;
modify handleOpenChange to ignore close attempts while the form is submitting by
returning early if the submitting flag is true, e.g., if (submitting &&
!nextOpen) return; then keep calling handleClose() only when not submitting;
apply the same guard to any other open/close handlers or header-close logic
associated with this form so the modal cannot be dismissed while a submit is
pending.
- Around line 260-271: Replace the hand-rolled close <button> inside
Dialog.Close with the shared Button component to preserve shared states: keep
Dialog.Close asChild, render <Button> (e.g., <Button variant="ghost"
aria-label="Close">) as the child and place the <X className="size-4" /> inside
it, removing the custom cn className and focus/hover classes; ensure Button
props (variant/size/aria-label) match other modal uses so hover, focus and
disabled behavior remain consistent (references: Dialog.Close, Button, and X).

---

Duplicate comments:
In `@web/src/utils/format.ts`:
- Around line 67-79: The code currently allows any 3-letter string (trimmed ->
code) and relies on try/catch at format time; instead validate the currency once
and avoid per-render exceptions: use Intl.supportedValuesOf?.('currency') to
check trimmed.toUpperCase() and set code = valid ? trimmedUpper : 'EUR'; if
supportedValuesOf is not available, perform a one-time cached check (e.g.,
attempt new Intl.NumberFormat('en-US',{style:'currency',currency:trimmedUpper})
once and store the result in a Map like validCurrencyCache) and then reuse that
cached boolean to choose the fallback 'EUR' before calling new Intl.NumberFormat
to format value; update references to trimmed and code in utils/format.ts and
remove reliance on try/catch for unsupported currencies.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c30a64bf-ba53-4b4c-9e7d-5db74de4f871

📥 Commits

Reviewing files that changed from the base of the PR and between 2aeadc2 and 5e3dab7.

📒 Files selected for processing (5)
  • web/src/__tests__/components/ui/progress-gauge.test.tsx
  • web/src/__tests__/utils/format.test.ts
  • web/src/pages/providers/ProviderFormModal.tsx
  • web/src/pages/setup/ProvidersStep.tsx
  • web/src/utils/format.ts
📜 Review details
⏰ 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). (6)
  • GitHub Check: Test (Python 3.14)
  • GitHub Check: Dashboard Test
  • GitHub Check: Build Web
  • GitHub Check: Build Backend
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (5)
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

web/src/**/*.{ts,tsx}: Web Dashboard: ALWAYS reuse existing components from web/src/components/ui/ before creating new ones. Reuse StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton variants, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup, Drawer, InputField, SelectField, SliderField, ToggleField, TaskStatusIndicator, PriorityBadge, ProviderHealthBadge, TokenUsageBar, CodeMirrorEditor, SegmentedControl, ThemeToggle.
Do NOT recreate status dots inline—use <StatusBadge>. Do NOT build card-with-header layouts from scratch—use <SectionCard>. Do NOT create metric displays with text-metric font-bold—use <MetricCard>. Do NOT render initials circles manually—use <Avatar>. Do NOT create complex (>8 line) JSX inside .map()—extract to a shared component.
Web design system enforcement: A PostToolUse hook (scripts/check_web_design_system.py) runs automatically on every Edit/Write to web/src/ files. It catches hardcoded hex colors, rgba values, font-family declarations, missing Storybook stories, duplicate patterns, and complex map blocks. Fix all violations before proceeding.
React 19 + shadcn/ui + Tailwind CSS 4 dashboard: Use React Router, Zustand stores, @tanstack/react-query, @xyflow/react, @dagrejs/dagre, d3-force, @dnd-kit, Recharts, Framer Motion, cmdk, js-yaml, Axios.

Files:

  • web/src/__tests__/components/ui/progress-gauge.test.tsx
  • web/src/pages/setup/ProvidersStep.tsx
  • web/src/utils/format.ts
  • web/src/pages/providers/ProviderFormModal.tsx
  • web/src/__tests__/utils/format.test.ts
web/src/**/*.{ts,tsx,css}

📄 CodeRabbit inference engine (CLAUDE.md)

Design tokens: Colors use Tailwind semantic classes (text-foreground, bg-card, text-accent, etc.) or CSS variables (var(--so-accent)). NEVER hardcode hex values. Typography uses font-sans or font-mono. Spacing uses density-aware tokens or standard Tailwind. Shadows/borders use token variables.

Files:

  • web/src/__tests__/components/ui/progress-gauge.test.tsx
  • web/src/pages/setup/ProvidersStep.tsx
  • web/src/utils/format.ts
  • web/src/pages/providers/ProviderFormModal.tsx
  • web/src/__tests__/utils/format.test.ts
**/*.{ts,tsx,json}

📄 CodeRabbit inference engine (CLAUDE.md)

TypeScript 6.0: Remove baseUrl (deprecated); paths entries are relative to tsconfig directory. esModuleInterop always true—cannot be set to false. types defaults to []—must explicitly list (e.g. 'types': ['vitest/globals']). DOM.Iterable merged into DOM. moduleResolution: 'classic' and 'node10' removed—use 'bundler' or 'nodenext'. strict defaults to true. noUncheckedSideEffectImports defaults to true.

Files:

  • web/src/__tests__/components/ui/progress-gauge.test.tsx
  • web/src/pages/setup/ProvidersStep.tsx
  • web/src/utils/format.ts
  • web/src/pages/providers/ProviderFormModal.tsx
  • web/src/__tests__/utils/format.test.ts
**/*.{py,ts,tsx,go}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{py,ts,tsx,go}: Lint & format: Python via ruff (uv run ruff check + uv run ruff format). TypeScript web via ESLint (zero warnings enforced: npm --prefix web run lint). Go CLI via golangci-lint and go vet. All enforced in pre-commit hooks.
Type checking: Python via mypy strict mode (uv run mypy src/ tests/). TypeScript via npm --prefix web run type-check. Go via go -C cli vet ./....

Files:

  • web/src/__tests__/components/ui/progress-gauge.test.tsx
  • web/src/pages/setup/ProvidersStep.tsx
  • web/src/utils/format.ts
  • web/src/pages/providers/ProviderFormModal.tsx
  • web/src/__tests__/utils/format.test.ts
web/src/pages/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Web dashboard pages: Lazy-loaded page components (one per route). Page-scoped sub-components in pages/<page-name>/ subdirs. Feature dirs added as pages are built. Stores for auth, WebSocket, toast, analytics, setup wizard, company, agents, budget, tasks, settings, providers, theme.

Files:

  • web/src/pages/setup/ProvidersStep.tsx
  • web/src/pages/providers/ProviderFormModal.tsx
🧠 Learnings (4)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/providers/**/*.py : Providers: LLM provider abstraction (LiteLLM adapter), auth types (api_key/oauth/custom_header/none), presets (PROVIDER_PRESETS), runtime CRUD (ProviderManagementService with asyncio.Lock serialization), hot-reload via AppState swap.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to src/synthorg/providers/**/*.py : Providers: LLM provider abstraction, presets, model auto-discovery, runtime CRUD management, provider families, discovery SSRF allowlist, health tracking. Apply retry + rate limiting automatically via `BaseCompletionProvider`.
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to web/src/__tests__/**/*.{ts,js} : Dashboard testing: Vitest unit tests organized by feature under `web/src/__tests__/`. Use fast-check for property-based testing (`fc.assert` + `fc.property`).

Applied to files:

  • web/src/__tests__/components/ui/progress-gauge.test.tsx
  • web/src/__tests__/utils/format.test.ts
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/**/*.{ts,tsx} : React 19 + shadcn/ui + Tailwind CSS 4 dashboard: Use React Router, Zustand stores, tanstack/react-query, xyflow/react, dagrejs/dagre, d3-force, dnd-kit, Recharts, Framer Motion, cmdk, js-yaml, Axios.

Applied to files:

  • web/src/pages/setup/ProvidersStep.tsx
  • web/src/pages/providers/ProviderFormModal.tsx
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do NOT recreate status dots inline—use `<StatusBadge>`. Do NOT build card-with-header layouts from scratch—use `<SectionCard>`. Do NOT create metric displays with `text-metric font-bold`—use `<MetricCard>`. Do NOT render initials circles manually—use `<Avatar>`. Do NOT create complex (>8 line) JSX inside `.map()`—extract to a shared component.

Applied to files:

  • web/src/utils/format.ts
🔇 Additional comments (3)
web/src/__tests__/components/ui/progress-gauge.test.tsx (1)

90-109: Good edge-case coverage for non-finite inputs.

These additions correctly lock in the non-finite guards (NaN, Infinity, -Infinity) and NaN max fallback behavior with clear expectations.

web/src/__tests__/utils/format.test.ts (1)

5-5: Import update looks correct.

formatCurrencyCompact is properly imported and used by the added test block.

web/src/pages/setup/ProvidersStep.tsx (1)

105-124: Nice reuse of the override path.

This keeps the setup wizard on its own store/actions while reusing the shared provider form, so the create flow stays consistent without forking the modal logic.

Also applies to: 201-206

- Guard handleOpenChange to prevent closing modal while submitting
- Replace hand-rolled close button with shared Button component (ghost/icon)
- Add trim+uppercase currency code test for formatCurrencyCompact

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@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

Caution

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

⚠️ Outside diff range comments (1)
web/src/pages/providers/ProviderFormModal.tsx (1)

82-133: 🧹 Nitpick | 🔵 Trivial

Consider extracting render-phase state sync to a custom hook.

This render-phase state synchronization pattern using refs is valid but increases cognitive complexity. The logic for syncing form state based on mode, provider, open, and selectedPreset changes is spread across multiple conditional blocks.

Consider extracting this to a custom hook like useSyncProviderForm for better encapsulation and testability.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/providers/ProviderFormModal.tsx` around lines 82 - 133, Extract
the render-phase state sync block into a new custom hook named
useSyncProviderForm that accepts the inputs (mode, provider, open,
selectedPreset, preset) and the setter functions (setSelectedPreset, setApiKey,
setSubscriptionToken, setName, setAuthType, setBaseUrl, setLitellmProvider,
setTosAccepted) and performs the same logic currently in ProviderFormModal using
internal refs (prevProviderRef, prevModeRef, prevOpenRef, prevSelectedPresetRef)
to detect changes; replace the in-component refs/ifs with a single call to
useSyncProviderForm(...) so the hook encapsulates the "clear credentials when
switching to edit", "pre-fill in edit mode", and "auto-fill/reset on
selectedPresetChanged" behaviors and still updates the previous refs after
comparisons.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/src/__tests__/utils/format.test.ts`:
- Around line 187-190: The test for formatCurrencyCompact should assert that the
default currency is EUR, not just that the string contains "5"; update the spec
for it('formats a small value with default EUR currency') to check both the
numeric part and an EUR marker by asserting result contains "5" and matches
either the euro symbol or "EUR" (e.g., use a regex like /€|EUR/ in addition to
the existing numeric check against formatCurrencyCompact).

---

Outside diff comments:
In `@web/src/pages/providers/ProviderFormModal.tsx`:
- Around line 82-133: Extract the render-phase state sync block into a new
custom hook named useSyncProviderForm that accepts the inputs (mode, provider,
open, selectedPreset, preset) and the setter functions (setSelectedPreset,
setApiKey, setSubscriptionToken, setName, setAuthType, setBaseUrl,
setLitellmProvider, setTosAccepted) and performs the same logic currently in
ProviderFormModal using internal refs (prevProviderRef, prevModeRef,
prevOpenRef, prevSelectedPresetRef) to detect changes; replace the in-component
refs/ifs with a single call to useSyncProviderForm(...) so the hook encapsulates
the "clear credentials when switching to edit", "pre-fill in edit mode", and
"auto-fill/reset on selectedPresetChanged" behaviors and still updates the
previous refs after comparisons.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3c9c3653-d08a-4791-a794-c7326183e8b0

📥 Commits

Reviewing files that changed from the base of the PR and between 5e3dab7 and 855c130.

📒 Files selected for processing (2)
  • web/src/__tests__/utils/format.test.ts
  • web/src/pages/providers/ProviderFormModal.tsx
📜 Review details
⏰ 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). (6)
  • GitHub Check: Dashboard Test
  • GitHub Check: Test (Python 3.14)
  • GitHub Check: Build Backend
  • GitHub Check: Build Web
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (5)
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

web/src/**/*.{ts,tsx}: Web Dashboard: ALWAYS reuse existing components from web/src/components/ui/ before creating new ones. Reuse StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton variants, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup, Drawer, InputField, SelectField, SliderField, ToggleField, TaskStatusIndicator, PriorityBadge, ProviderHealthBadge, TokenUsageBar, CodeMirrorEditor, SegmentedControl, ThemeToggle.
Do NOT recreate status dots inline—use <StatusBadge>. Do NOT build card-with-header layouts from scratch—use <SectionCard>. Do NOT create metric displays with text-metric font-bold—use <MetricCard>. Do NOT render initials circles manually—use <Avatar>. Do NOT create complex (>8 line) JSX inside .map()—extract to a shared component.
Web design system enforcement: A PostToolUse hook (scripts/check_web_design_system.py) runs automatically on every Edit/Write to web/src/ files. It catches hardcoded hex colors, rgba values, font-family declarations, missing Storybook stories, duplicate patterns, and complex map blocks. Fix all violations before proceeding.
React 19 + shadcn/ui + Tailwind CSS 4 dashboard: Use React Router, Zustand stores, @tanstack/react-query, @xyflow/react, @dagrejs/dagre, d3-force, @dnd-kit, Recharts, Framer Motion, cmdk, js-yaml, Axios.

Files:

  • web/src/__tests__/utils/format.test.ts
  • web/src/pages/providers/ProviderFormModal.tsx
web/src/**/*.{ts,tsx,css}

📄 CodeRabbit inference engine (CLAUDE.md)

Design tokens: Colors use Tailwind semantic classes (text-foreground, bg-card, text-accent, etc.) or CSS variables (var(--so-accent)). NEVER hardcode hex values. Typography uses font-sans or font-mono. Spacing uses density-aware tokens or standard Tailwind. Shadows/borders use token variables.

Files:

  • web/src/__tests__/utils/format.test.ts
  • web/src/pages/providers/ProviderFormModal.tsx
**/*.{ts,tsx,json}

📄 CodeRabbit inference engine (CLAUDE.md)

TypeScript 6.0: Remove baseUrl (deprecated); paths entries are relative to tsconfig directory. esModuleInterop always true—cannot be set to false. types defaults to []—must explicitly list (e.g. 'types': ['vitest/globals']). DOM.Iterable merged into DOM. moduleResolution: 'classic' and 'node10' removed—use 'bundler' or 'nodenext'. strict defaults to true. noUncheckedSideEffectImports defaults to true.

Files:

  • web/src/__tests__/utils/format.test.ts
  • web/src/pages/providers/ProviderFormModal.tsx
**/*.{py,ts,tsx,go}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{py,ts,tsx,go}: Lint & format: Python via ruff (uv run ruff check + uv run ruff format). TypeScript web via ESLint (zero warnings enforced: npm --prefix web run lint). Go CLI via golangci-lint and go vet. All enforced in pre-commit hooks.
Type checking: Python via mypy strict mode (uv run mypy src/ tests/). TypeScript via npm --prefix web run type-check. Go via go -C cli vet ./....

Files:

  • web/src/__tests__/utils/format.test.ts
  • web/src/pages/providers/ProviderFormModal.tsx
web/src/pages/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Web dashboard pages: Lazy-loaded page components (one per route). Page-scoped sub-components in pages/<page-name>/ subdirs. Feature dirs added as pages are built. Stores for auth, WebSocket, toast, analytics, setup wizard, company, agents, budget, tasks, settings, providers, theme.

Files:

  • web/src/pages/providers/ProviderFormModal.tsx
🧠 Learnings (6)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/providers/**/*.py : Providers: LLM provider abstraction (LiteLLM adapter), auth types (api_key/oauth/custom_header/none), presets (PROVIDER_PRESETS), runtime CRUD (ProviderManagementService with asyncio.Lock serialization), hot-reload via AppState swap.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to src/synthorg/providers/**/*.py : Providers: LLM provider abstraction, presets, model auto-discovery, runtime CRUD management, provider families, discovery SSRF allowlist, health tracking. Apply retry + rate limiting automatically via `BaseCompletionProvider`.
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to web/src/__tests__/**/*.{ts,js} : Dashboard testing: Vitest unit tests organized by feature under `web/src/__tests__/`. Use fast-check for property-based testing (`fc.assert` + `fc.property`).

Applied to files:

  • web/src/__tests__/utils/format.test.ts
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : Web components: Place new shared components in `web/src/components/ui/` with kebab-case filenames. Create `.stories.tsx` with all states. Export props as TypeScript interface. Use design tokens exclusively—no hardcoded colors, fonts, or spacing. Import `cn` from `@/lib/utils` for conditional class merging.

Applied to files:

  • web/src/__tests__/utils/format.test.ts
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/**/*.{ts,tsx} : React 19 + shadcn/ui + Tailwind CSS 4 dashboard: Use React Router, Zustand stores, tanstack/react-query, xyflow/react, dagrejs/dagre, d3-force, dnd-kit, Recharts, Framer Motion, cmdk, js-yaml, Axios.

Applied to files:

  • web/src/pages/providers/ProviderFormModal.tsx
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/**/*.{ts,tsx} : Web Dashboard: ALWAYS reuse existing components from `web/src/components/ui/` before creating new ones. Reuse StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton variants, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup, Drawer, InputField, SelectField, SliderField, ToggleField, TaskStatusIndicator, PriorityBadge, ProviderHealthBadge, TokenUsageBar, CodeMirrorEditor, SegmentedControl, ThemeToggle.

Applied to files:

  • web/src/pages/providers/ProviderFormModal.tsx
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do NOT recreate status dots inline—use `<StatusBadge>`. Do NOT build card-with-header layouts from scratch—use `<SectionCard>`. Do NOT create metric displays with `text-metric font-bold`—use `<MetricCard>`. Do NOT render initials circles manually—use `<Avatar>`. Do NOT create complex (>8 line) JSX inside `.map()`—extract to a shared component.

Applied to files:

  • web/src/pages/providers/ProviderFormModal.tsx
🔇 Additional comments (7)
web/src/__tests__/utils/format.test.ts (1)

192-220: Good coverage for compact formatting and currency normalization.

The updated checks for compact USD notation plus invalid-code fallback and trim+uppercase normalization are solid and align with the bugfix intent.

web/src/pages/providers/ProviderFormModal.tsx (6)

135-140: LGTM!

The baseUrlHint derivation is clear and correctly simplified. The logic properly shows "Required for this provider" only when creating with a preset that lacks a default_base_url.


231-234: LGTM!

The handleOpenChange guard correctly prevents the modal from closing while a submission is in progress, addressing the potential duplicate-write concern.


253-266: LGTM!

The dialog header correctly uses Dialog.Description with sr-only for accessibility, and the close button properly uses the shared Button component. Both past review concerns are addressed.


376-388: LGTM!

The submit section correctly disables both Cancel and Submit buttons during submission, and the Submit button includes proper validation guards for required fields.


397-410: LGTM!

Proper reuse of the shared ConfirmDialog component for the subscription ToS confirmation.


2-2: The import import { Dialog } from 'radix-ui' is correct for this project. The codebase consistently uses the consolidated radix-ui v1.4.3 package across multiple files (TaskCreateDialog.tsx, DepartmentCreateDialog.tsx, AgentCreateDialog.tsx, OrgEditPage.tsx, theme-toggle.tsx, and confirm-dialog.tsx), all importing Dialog, Tabs, Popover, and AlertDialog using this exact pattern. No changes needed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 29, 2026 18:56 — with GitHub Actions Inactive
@Aureliolo Aureliolo merged commit 87bf8e6 into main Mar 29, 2026
32 of 34 checks passed
@Aureliolo Aureliolo deleted the fix/improve-provider-setup-in-wizard branch March 29, 2026 19:05
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 29, 2026 19:05 — with GitHub Actions Inactive
Aureliolo added a commit that referenced this pull request Mar 30, 2026
🤖 I have created a release *beep* *boop*
---
#MAJOR CHANGES; We got a somewhat working webui :)

##
[0.5.0](v0.4.9...v0.5.0)
(2026-03-30)


### Features

* add analytics trends and budget forecast API endpoints
([#798](#798))
([16b61f5](16b61f5))
* add department policies to default templates
([#852](#852))
([7a41548](7a41548))
* add remaining activity event types (task_started, tool_used,
delegation, cost_incurred)
([#832](#832))
([4252fac](4252fac))
* agent performance, activity, and history API endpoints
([#811](#811))
([9b75c1d](9b75c1d))
* Agent Profiles and Detail pages (biography, career, performance)
([#874](#874))
([62d7880](62d7880))
* app shell, Storybook, and CI/CD pipeline
([#819](#819))
([d4dde90](d4dde90))
* Approvals page with risk grouping, urgency indicators, batch actions
([#889](#889))
([4e9673d](4e9673d))
* Budget Panel page (P&L dashboard, breakdown charts, forecast)
([#890](#890))
([b63b0f1](b63b0f1))
* build infrastructure layer (API client, auth, WebSocket)
([#815](#815))
([9f01d3e](9f01d3e))
* CLI global options infrastructure, UI modes, exit codes, env vars
([#891](#891))
([fef4fc5](fef4fc5))
* CodeMirror editor and theme preferences toggle
([#905](#905),
[#807](#807))
([#909](#909))
([41fbedc](41fbedc))
* Company page (department/agent management)
([#888](#888))
([cfb88b0](cfb88b0))
* comprehensive hint coverage across all CLI commands
([#900](#900))
([937974e](937974e))
* config system extensions, per-command flags for
init/start/stop/status/logs
([#895](#895))
([32f83fe](32f83fe))
* configurable currency system replacing hardcoded USD
([#854](#854))
([b372551](b372551))
* Dashboard page (metric cards, activity feed, budget burn)
([#861](#861))
([7d519d5](7d519d5))
* department health, provider status, and activity feed endpoints
([#818](#818))
([6d5f196](6d5f196))
* design tokens and core UI components
([#833](#833))
([ed887f2](ed887f2))
* extend approval, meeting, and budget API responses
([#834](#834))
([31472bf](31472bf))
* frontend polish -- real-time UX, accessibility, responsive,
performance ([#790](#790),
[#792](#792),
[#791](#791),
[#793](#793))
([#917](#917))
([f04a537](f04a537))
* implement human roles and access control levels
([#856](#856))
([d6d8a06](d6d8a06))
* implement semantic conflict detection in workspace merge
([#860](#860))
([d97283b](d97283b))
* interaction components and animation patterns
([#853](#853))
([82d4b01](82d4b01))
* Login page + first-run bootstrap + Company page
([#789](#789),
[#888](#888))
([#896](#896))
([8758e8d](8758e8d))
* Meetings page with timeline viz, token bars, contribution formatting
([#788](#788))
([#904](#904))
([b207f46](b207f46))
* Messages page with threading, channel badges, sender indicators
([#787](#787))
([#903](#903))
([28293ad](28293ad))
* Org Chart force-directed view and drag-drop reassignment
([#872](#872),
[#873](#873))
([#912](#912))
([a68a938](a68a938))
* Org Chart page (living nodes, status, CRUD, department health)
([#870](#870))
([0acbdae](0acbdae))
* per-command flags for remaining commands, auto-behavior wiring,
help/discoverability
([#897](#897))
([3f7afa2](3f7afa2))
* Providers page with backend rework -- health, CRUD, subscription auth
([#893](#893))
([9f8dd98](9f8dd98))
* scaffold React + Vite + TypeScript + Tailwind project
([#799](#799))
([bd151aa](bd151aa))
* Settings page with search, dependency indicators, grouped rendering
([#784](#784))
([#902](#902))
([a7b9870](a7b9870))
* Setup Wizard rebuild with template comparison, cost estimator, theme
customization ([#879](#879))
([ae8b50b](ae8b50b))
* setup wizard UX -- template filters, card metadata, provider form
reuse ([#910](#910))
([7f04676](7f04676))
* setup wizard UX overhaul -- mode choice, step reorder, provider fixes
([#907](#907))
([ee964c4](ee964c4))
* structured ModelRequirement in template agent configs
([#795](#795))
([7433548](7433548))
* Task Board page (rich Kanban, filtering, dependency viz)
([#871](#871))
([04a19b0](04a19b0))


### Bug Fixes

* align frontend types with backend and debounce WS refetches
([#916](#916))
([134c11b](134c11b))
* auto-cleanup targets newly pulled images instead of old ones
([#884](#884))
([50e6591](50e6591))
* correct wipe backup-skip flow and harden error handling
([#808](#808))
([c05860f](c05860f))
* improve provider setup in wizard, subscription auth, dashboard bugs
([#914](#914))
([87bf8e6](87bf8e6))
* improve update channel detection and add config get command
([#814](#814))
([6b137f0](6b137f0))
* resolve all ESLint warnings, add zero-warnings enforcement
([#899](#899))
([079b46a](079b46a))
* subscription auth uses api_key, base URL optional for cloud providers
([#915](#915))
([f0098dd](f0098dd))


### Refactoring

* semantic analyzer cleanup -- shared filtering, concurrency, extraction
([#908](#908))
([81372bf](81372bf))


### Documentation

* brand identity and UX design system from
[#765](#765) exploration
([#804](#804))
([389a9f4](389a9f4))
* page structure and information architecture for v0.5.0 dashboard
([#809](#809))
([f8d6d4a](f8d6d4a))
* write UX design guidelines with WCAG-verified color system
([#816](#816))
([4a4594e](4a4594e))


### Tests

* add unit tests for agent hooks and page components
([#875](#875))
([#901](#901))
([1d81546](1d81546))


### CI/CD

* bump actions/deploy-pages from 4.0.5 to 5.0.0 in the major group
([#831](#831))
([01c19de](01c19de))
* bump astral-sh/setup-uv from 7.6.0 to 8.0.0 in
/.github/actions/setup-python-uv in the all group
([#920](#920))
([5f6ba54](5f6ba54))
* bump codecov/codecov-action from 5.5.3 to 6.0.0 in the major group
([#868](#868))
([f22a181](f22a181))
* bump github/codeql-action from 4.34.1 to 4.35.0 in the all group
([#883](#883))
([87a4890](87a4890))
* bump sigstore/cosign-installer from 4.1.0 to 4.1.1 in the
minor-and-patch group
([#830](#830))
([7a69050](7a69050))
* bump the all group with 3 updates
([#923](#923))
([ff27c8e](ff27c8e))
* bump wrangler from 4.76.0 to 4.77.0 in /.github in the minor-and-patch
group ([#822](#822))
([07d43eb](07d43eb))
* bump wrangler from 4.77.0 to 4.78.0 in /.github in the all group
([#882](#882))
([f84118d](f84118d))


### Maintenance

* add design system enforcement hook and component inventory
([#846](#846))
([15abc43](15abc43))
* add dev-only auth bypass for frontend testing
([#885](#885))
([6cdcd8a](6cdcd8a))
* add pre-push rebase check hook
([#855](#855))
([b637a04](b637a04))
* backend hardening -- eviction/size-caps and model validation
([#911](#911))
([81253d9](81253d9))
* bump axios from 1.13.6 to 1.14.0 in /web in the all group across 1
directory ([#922](#922))
([b1b0232](b1b0232))
* bump brace-expansion from 5.0.4 to 5.0.5 in /web
([#862](#862))
([ba4a565](ba4a565))
* bump eslint-plugin-react-refresh from 0.4.26 to 0.5.2 in /web
([#801](#801))
([7574bb5](7574bb5))
* bump faker from 40.11.0 to 40.11.1 in the minor-and-patch group
([#803](#803))
([14d322e](14d322e))
* bump https://github.com/astral-sh/ruff-pre-commit from v0.15.7 to
0.15.8 ([#864](#864))
([f52901e](f52901e))
* bump nginxinc/nginx-unprivileged from `6582a34` to `f99cc61` in
/docker/web in the all group
([#919](#919))
([df85e4f](df85e4f))
* bump nginxinc/nginx-unprivileged from `ccbac1a` to `6582a34` in
/docker/web ([#800](#800))
([f4e9450](f4e9450))
* bump node from `44bcbf4` to `71be405` in /docker/sandbox
([#827](#827))
([91bec67](91bec67))
* bump node from `5209bca` to `cf38e1f` in /docker/web
([#863](#863))
([66d6043](66d6043))
* bump picomatch in /site
([#842](#842))
([5f20bcc](5f20bcc))
* bump recharts 2-&gt;3 and @types/node 22-&gt;25 in /web
([#802](#802))
([a908800](a908800))
* Bump requests from 2.32.5 to 2.33.0
([#843](#843))
([41daf69](41daf69))
* bump smol-toml from 1.6.0 to 1.6.1 in /site
([#826](#826))
([3e5dbe4](3e5dbe4))
* bump the all group with 3 updates
([#921](#921))
([7bace0b](7bace0b))
* bump the minor-and-patch group across 1 directory with 2 updates
([#829](#829))
([93e611f](93e611f))
* bump the minor-and-patch group across 1 directory with 3 updates
([#841](#841))
([7010c8e](7010c8e))
* bump the minor-and-patch group across 1 directory with 3 updates
([#869](#869))
([548cee5](548cee5))
* bump the minor-and-patch group in /site with 2 updates
([#865](#865))
([9558101](9558101))
* bump the minor-and-patch group with 2 updates
([#867](#867))
([4830706](4830706))
* consolidate Dependabot groups to 1 PR per ecosystem
([06d2556](06d2556))
* consolidate Dependabot groups to 1 PR per ecosystem
([#881](#881))
([06d2556](06d2556))
* improve worktree skill with full dep sync and status enhancements
([#906](#906))
([772c625](772c625))
* remove Vue remnants and document framework decision
([#851](#851))
([bf2adf6](bf2adf6))
* update web dependencies and fix brace-expansion CVE
([#880](#880))
([a7a0ed6](a7a0ed6))
* upgrade to Storybook 10 and TypeScript 6
([#845](#845))
([52d95f2](52d95f2))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant