refactor(web): address complexity and logging issues in dashboard#1056
refactor(web): address complexity and logging issues in dashboard#1056
Conversation
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 1 minutes and 7 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Repository UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (60)
WalkthroughAdds a structured logging utility at 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Snapshot WarningsEnsure 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 FilesNone |
There was a problem hiding this comment.
Pull request overview
Refactors the web dashboard to reduce complexity hotspots and replace scattered console.warn/error calls with a shared structured logger, improving maintainability and log consistency across the app.
Changes:
- Introduces
web/src/lib/logger.ts(createLogger(module)) and migrates many call sites to structured/sanitized logging. - Splits high-complexity modules into smaller focused files (providers store actions; settings editor extensions; CodeEditorPanel helpers).
- Adds/updates unit tests for the new logger and refactored settings-editor utilities/extensions.
Reviewed changes
Copilot reviewed 57 out of 57 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| web/src/utils/format.ts | Switches Intl formatting error logs to structured logger. |
| web/src/utils/errors.ts | Replaces warning log for suppressed messages with structured logger. |
| web/src/utils/dashboard.ts | Converts org health warnings to structured logger. |
| web/src/stores/websocket.ts | Migrates WS store error/warn logging to structured logger (keeps targeted sanitization where needed). |
| web/src/stores/theme.ts | Converts localStorage/theme warnings to structured logger. |
| web/src/stores/tasks.ts | Converts WS payload validation error logging to structured logger. |
| web/src/stores/setup-wizard.ts | Converts setup wizard error logging to structured logger. |
| web/src/stores/settings.ts | Converts settings store warnings to structured logger. |
| web/src/stores/providers/types.ts | New: shared ProvidersState + set/get types for split providers modules. |
| web/src/stores/providers/local-model-actions.ts | New: extracted local-model actions (pull/cancel/delete/update). |
| web/src/stores/providers/list-actions.ts | New: extracted list + health fetch actions with request-staleness guard. |
| web/src/stores/providers/detail-actions.ts | New: extracted detail fetch actions with partial-error handling. |
| web/src/stores/providers/crud-actions.ts | New: extracted CRUD/presets/test/discovery actions. |
| web/src/stores/providers.ts | Refactors providers store to delegate to extracted action modules. |
| web/src/stores/messages.ts | Converts WS payload error logging to structured logger. |
| web/src/stores/meetings.ts | Converts stale-request and WS payload logs to structured logger. |
| web/src/stores/company.ts | Converts department health + WS refresh logs to structured logger. |
| web/src/stores/budget.ts | Converts best-effort fetch warnings and WS processing errors to structured logger. |
| web/src/stores/auth.ts | Converts auth bypass/session/profile logs to structured logger. |
| web/src/stores/approvals.ts | Converts WS payload + optimistic flow logs to structured logger. |
| web/src/stores/analytics.ts | Converts fetch and WS processing logs to structured logger. |
| web/src/stores/agents.ts | Converts pagination warning logs to structured logger. |
| web/src/pages/WorkflowEditorPage.tsx | Converts viewport persistence warnings to structured logger. |
| web/src/pages/setup/ProviderProbeResults.tsx | Converts preset-add failure log to structured logger. |
| web/src/pages/setup/AccountStep.tsx | Converts setup-status fetch error logging to structured logger. |
| web/src/pages/SettingsPage.tsx | Converts code-save unexpected error logging to structured logger. |
| web/src/pages/settings/utils.ts | Converts malformed key/save-failure logging to structured logger. |
| web/src/pages/settings/sinks/SinkFormDrawer.tsx | Converts test-config failure logging to structured logger. |
| web/src/pages/settings/SettingField.tsx | Converts parse/validator warnings to structured logger. |
| web/src/pages/settings/editor-linter.ts | New: extracted CodeMirror linter extension + schema helpers (testable). |
| web/src/pages/settings/editor-extensions.ts | Becomes a barrel re-export for diff/linter/autocomplete modules. |
| web/src/pages/settings/editor-diff.ts | New: extracted diff gutter extension and computeLineDiff. |
| web/src/pages/settings/editor-autocomplete.ts | New: extracted schema-aware autocomplete extension. |
| web/src/pages/settings/CodeEditorPanel.tsx | Uses extracted helpers/types and keeps editor wiring in component. |
| web/src/pages/settings/code-editor-utils.ts | New: extracted parsing/serialization/diff helper functions for code editor. |
| web/src/pages/providers/ProviderFormModal.tsx | Converts submit-failure logging to structured logger. |
| web/src/pages/OrgChartPage.tsx | Converts viewport persistence warnings to structured logger. |
| web/src/lib/logger.ts | New: structured logger with prefixing + argument sanitization. |
| web/src/hooks/useWebSocket.ts | Converts setup/connect/cleanup error logs to structured logger. |
| web/src/hooks/useSettingsDirtyState.ts | Converts unexpected save error logging to structured logger. |
| web/src/hooks/usePolling.ts | Converts polling error logging to structured logger and updates prefix expectations. |
| web/src/hooks/useOrgChartData.ts | Converts fetch warnings to structured logger. |
| web/src/hooks/useOptimisticUpdate.ts | Converts optimistic/rollback failure logs to structured logger. |
| web/src/components/ui/error-boundary.tsx | Converts DEV-only boundary logging to structured logger. |
| web/src/components/ui/drawer.tsx | Converts non-production accessibility warning to structured logger. |
| web/src/components/ui/command-palette.tsx | Converts DEV-only localStorage/action failure logging to structured logger. |
| web/src/components/layout/Sidebar.tsx | Converts non-production prop misuse warning to structured logger. |
| web/src/api/endpoints/providers.ts | Converts SSE parse warning to structured logger. |
| web/src/api/client.ts | Converts dynamic auth-store import failure logging to structured logger. |
| web/src/tests/pages/settings/editor-linter.test.ts | New: unit tests for schema info builder + schema validation diagnostics. |
| web/src/tests/pages/settings/editor-diff.test.ts | New: unit tests for computeLineDiff. |
| web/src/tests/pages/settings/editor-autocomplete.test.ts | New: basic smoke tests for autocomplete extension creation. |
| web/src/tests/pages/settings/code-editor-utils.test.ts | New: unit tests for extracted code-editor utilities. |
| web/src/tests/lib/logger.test.ts | New: unit tests for logger prefixing and sanitization behavior. |
| web/src/tests/hooks/usePolling.test.ts | Updates assertion to match structured logger output. |
| web/CLAUDE.md | Updates package structure docs to mention logger and providers split. |
| docs/design/ux-guidelines.md | Adds structured logger factory to reference table. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| /** | ||
| * Pure utility functions for the Settings code editor. | ||
| * | ||
| * Handles serialization, parsing, validation, and diffing of | ||
| * settings entries in JSON/YAML format. | ||
| */ | ||
|
|
||
| import YAML from 'js-yaml' | ||
| import type { SettingEntry } from '@/api/types' | ||
| import type { CodeMirrorEditorProps } from '@/components/ui/code-mirror-editor' | ||
| import { createLogger } from '@/lib/logger' | ||
|
|
||
| const log = createLogger('settings') | ||
|
|
There was a problem hiding this comment.
The module header describes these as "Pure utility functions", but the file has side effects (it creates a logger and logs warnings in entriesToObject). Either remove logging from this module (return errors to the caller) or update the comment to avoid implying purity/side-effect-free behavior.
| * Tests the exported settingsAutocompleteExtension indirectly | ||
| * by verifying the completion schema builder and the JSON/YAML | ||
| * completion sources via minimal CompletionContext mocks. |
There was a problem hiding this comment.
The file-level comment claims the tests verify the completion schema builder and JSON/YAML completion sources via CompletionContext mocks, but the tests currently only assert the returned Extension is truthy. Update the comment to match what's actually tested, or add assertions that exercise the completion sources as described.
| * Tests the exported settingsAutocompleteExtension indirectly | |
| * by verifying the completion schema builder and the JSON/YAML | |
| * completion sources via minimal CompletionContext mocks. | |
| * Verifies that settingsAutocompleteExtension can be constructed for | |
| * representative JSON/YAML formats and entry shapes, and returns a | |
| * truthy CodeMirror Extension for those inputs. |
There was a problem hiding this comment.
Code Review
This pull request introduces a structured logger factory to standardize logging and sanitization across the application, replacing direct console calls. It also refactors the settings editor and providers store into modular components, enhancing maintainability and test coverage. Feedback focuses on optimizing the memory usage of the LCS-based diffing algorithm, removing redundant sorting, and improving YAML parsing robustness regarding indentation and quoted keys. Additionally, a suggestion was made to clean up the providers' health map upon deletion to avoid stale data.
| const serverLines = serverText.split('\n') | ||
| const editedLines = editedText.split('\n') |
There was a problem hiding this comment.
Adding a fast path for identical strings and normalizing line endings will improve performance and prevent false diffs caused by CRLF/LF mismatches between the server and the browser.
| const serverLines = serverText.split('\n') | |
| const editedLines = editedText.split('\n') | |
| if (serverText === editedText) return [] | |
| const serverLines = serverText.replace(/\r\n/g, '\n').split('\n') | |
| const editedLines = editedText.replace(/\r\n/g, '\n').split('\n') |
| // at any position without cascading false "changed" markers). | ||
| const n = serverLines.length | ||
| const m = editedLines.length | ||
| const dp: number[][] = Array.from({ length: n + 1 }, () => new Array<number>(m + 1).fill(0)) |
There was a problem hiding this comment.
The current Int32Array of size (n + 1) * (m + 1) to store the DP table more efficiently.
| const doc = view.state.doc | ||
|
|
||
| // Sort diffs by line to satisfy RangeSetBuilder ordering | ||
| const sorted = [...diffs].sort((a, b) => a.line - b.line) |
| const indent = indentMatch?.[1]?.length ?? 0 | ||
|
|
||
| // Check if we're typing a value after "key: " for enum autocomplete | ||
| const valueMatch = /^\s{2,}(\w[\w_]*)\s*:\s*(\S*)$/.exec(beforeOnLine) |
There was a problem hiding this comment.
The YAML indentation heuristic is too strict. Requiring at least 2 spaces (\s{2,}) will break autocomplete for users who prefer a single space or different indentation styles. Using \s+ is safer as namespaces are guaranteed to have zero indentation.
const valueMatch = /^\s+(\w[\w_]*)\s*:\s*(\S*)$/.exec(beforeOnLine)| const partial = keyTyping | ||
| const from = pos - partial.length | ||
|
|
||
| if (indent >= 2) { |
| const searchFrom = nsMatch ? nsMatch.index + nsMatch[0].length : 0 | ||
| const sub = text.slice(searchFrom) | ||
| // eslint-disable-next-line security/detect-non-literal-regexp -- input is escaped via escapeRegex | ||
| const keyPattern = new RegExp(`^(\\s+)${escapeRegex(key)}\\s*:`, 'm') |
There was a problem hiding this comment.
The YAML key position finder doesn't account for quoted keys (e.g., "key": value). This can cause the linter to fail to highlight the correct position if the user or the formatter uses quotes.
| const keyPattern = new RegExp(`^(\\s+)${escapeRegex(key)}\\s*:`, 'm') | |
| const keyPattern = new RegExp(`^(\\s+)["']?${escapeRegex(key)}["']?\\s*:`, 'm') |
| set((state) => ({ | ||
| providers: state.providers.filter((p) => p.name !== name), | ||
| })) |
There was a problem hiding this comment.
When a provider is deleted, its entry should also be removed from the healthMap to prevent stale data and a small memory leak.
| set((state) => ({ | |
| providers: state.providers.filter((p) => p.name !== name), | |
| })) | |
| set((state) => { | |
| const { [name]: _, ...healthMap } = state.healthMap | |
| return { | |
| providers: state.providers.filter((p) => p.name !== name), | |
| healthMap, | |
| } | |
| }) |
There was a problem hiding this comment.
Actionable comments posted: 17
🤖 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__/lib/logger.test.ts`:
- Around line 46-51: The test currently asserts that createLogger('m') forwards
the original object reference (ctx) to warnSpy, locking in unsafe pass-through;
change the assertion to verify that createLogger sanitizes/serializes the object
rather than forwarding the exact reference: update the test for the "passes
objects through unchanged" case to call createLogger and log.warn (same as now)
but assert against a sanitized/serialized form (or a deep-equal copy) instead of
the original ctx reference and/or assert that the argument is not the same
object instance; refer to createLogger, the warnSpy used in the test, and the
test case name when making the change.
In `@web/src/__tests__/pages/settings/code-editor-utils.test.ts`:
- Around line 161-168: Add an explicit YAML syntax error test for parseText
similar to the JSON syntax test: create an `it` case that calls parseText with
malformed YAML (e.g., an unclosed mapping or bad indentation) and asserts it
throws; reference the existing test helpers `parseText` and `MAX_EDITOR_BYTES`
so you place the new case near the other syntax/error tests (e.g., next to
"rejects invalid JSON syntax") to improve coverage of YAML parsing errors.
In `@web/src/lib/logger.ts`:
- Around line 9-17: The current logging sanitization is incomplete: sanitizeArg
currently returns plain objects and forwards the primary message raw, allowing
log-injection via interpolated strings; update sanitizeArg and the logging call
sites so the main message is passed through sanitizeForLog before any console.*
call and make sanitizeArg perform deep sanitization for objects (recursively
sanitize all string properties) and sanitize Error instances (at least
error.message and error.stack) instead of returning them unchanged; ensure any
structured args used for devtools inspection are replaced with their sanitized
equivalents before being logged by the logger functions.
In `@web/src/pages/settings/code-editor-utils.ts`:
- Around line 21-37: entriesToObject currently writes API-controlled namespace
and key strings into plain objects, allowing prototype-polluting keys like
"__proto__" to modify prototypes; to fix, build the root object and each
namespace object with Object.create(null) instead of {} (e.g., const obj =
Object.create(null); const nsObj = Object.create(null)), and before assigning
any entry use a defensive check to skip dangerous property names (at minimum
'__proto__', 'constructor' and 'prototype') when handling entry.definition.key;
keep the existing JSON.parse/try-catch behavior and assign into the safe ns
object (obj[ns][entry.definition.key] = ...).
- Line 17: Declare CodeFormat explicitly as the union type 'json' | 'yaml'
(instead of relying on CodeMirrorEditorProps['language']), then update
serializeEntries and parseText to validate the format value against that union
and reject/throw a clear error for any unsupported value rather than implicitly
treating non-'json' as YAML; reference the CodeFormat type, the serializeEntries
function, and the parseText function and implement an explicit branch/exhaustive
check (e.g., switch or if/else) that handles 'json' and 'yaml' and errors on
anything else.
In `@web/src/pages/settings/editor-autocomplete.ts`:
- Around line 139-145: The detail template literal in the options mapping
(inside options: keyInfo.map(...)) exceeds the 88-character limit; refactor by
computing the detail string in a short helper or local const (e.g., const detail
= k.enumValues.length ? `${k.type} (${k.enumValues.join(' | ')})` : k.type)
immediately above the returned object and then use detail in the object literal
(detail: detail) so the object property line stays under 88 chars; ensure you
reference the same keyInfo.map callback and keep label/type/info properties
as-is.
- Around line 276-286: The completion override rebuilds the schema on every
keystroke because buildCompletionSchema(entries) is called inside the
CompletionContext callback; memoize the schema so it’s only recomputed when
entries (from getEntries()) or format (from getFormat()) actually change. Move
caching outside the override—e.g., compute and cache schema and chosen source
(jsonCompletionSource or yamlCompletionSource) based on entries identity/length
and format, then have the override simply call the cached source(ctx); ensure
references to buildCompletionSchema, getEntries, getFormat,
jsonCompletionSource, and yamlCompletionSource are used to control invalidation.
In `@web/src/pages/settings/editor-diff.ts`:
- Around line 86-90: The loop that adds removed-line diffs can produce a line
value of 0 when m === 0 (empty editedText); update the pushed line number to
clamp to at least 1 by wrapping the existing Math.min expression with
Math.max(1, ...). Locate the loop that iterates over i (uses serverMatched, n,
m, and pushes into diffs) and change the pushed object’s line calculation to use
Math.max(1, Math.min(i + 1, m)) so removed entries never have line 0.
In `@web/src/stores/meetings.ts`:
- Around line 78-79: Replace raw error object logging with a sanitized string
for all log.warn/log.error calls that pass the `err` object (e.g., the
"Discarding error from stale list request" call and the similar occurrences
later). Instead of passing `err` directly to `log.warn`, compute a safe string
like `const safe = err instanceof Error ? err.message : String(err)` (or call a
shared sanitizeError helper) and log that string (optionally include
non-sensitive fields only, and only include stack in debug). Update the
corresponding calls in meetings.ts to use this safe string/sanitizer for the
`err` parameter.
In `@web/src/stores/providers/crud-actions.ts`:
- Around line 26-37: fetchPresets currently skips fetching if presets are
already loaded which prevents session updates; modify fetchPresets to accept an
optional boolean parameter (e.g., forceRefresh = false) and only return early
when presets exist AND forceRefresh is false, then use listPresets()/set()/get()
as before and ensure callers that need fresh data call fetchPresets(true);
update any call sites that should force refresh or leave default behavior
unchanged.
In `@web/src/stores/providers/list-actions.ts`:
- Around line 18-20: The early return in fetchProviders (the guard checking
get().mutating) prevents post-mutation refreshes called from CRUD flows; remove
this guard or add a forced-refresh option so internal callers (e.g., await
get().fetchProviders() from crud-actions.ts) can bypass it. Update the
fetchProviders signature to accept a boolean like force?: boolean (or similar)
and change the guard to if (get().mutating && !force) return, then update calls
in CRUD actions (create/update/delete) to pass force: true when they need a
guaranteed reload; ensure existing external callers without explicit force
retain previous behavior.
In `@web/src/stores/providers/local-model-actions.ts`:
- Around line 1-10: Add structured logging to this module by importing
createLogger from the logging utility and instantiating it (e.g., const logger =
createLogger('providers')); then in each action's catch block (the functions
that call apiPullModel, apiDeleteModel, apiUpdateModelConfig) call logger.error
or logger.warn with the error and contextual info alongside the existing
useToastStore notifications; ensure you reference the same error message passed
to toasts (use getErrorMessage(err)) so logs and toasts remain consistent.
- Around line 99-119: The updateModelConfig handler (updateModelConfig)
currently updates via apiUpdateModelConfig but doesn't refresh the providers
list; add an await get().fetchProviders() after the successful
apiUpdateModelConfig call (matching deleteModel and pullModel patterns) and keep
the existing selected provider detail refresh (await
get().fetchProviderDetail(name)) so both the providers list and the provider
detail stay in sync after a config change.
In `@web/src/stores/settings.ts`:
- Line 74: The warning embeds raw user/config data directly in the template
string; change the log.warn call that references currencyEntry.value so the
logger receives a static message and the raw value as a separate argument (or
sanitize/escape currencyEntry.value first) instead of interpolating it into the
message — update the call to use log.warn("Invalid currency value: %s, keeping
default", currencyEntry.value) or equivalent so logger argument sanitization can
run (target the log.warn invocation that currently uses `currencyEntry.value`).
In `@web/src/stores/setup-wizard.ts`:
- Around line 59-60: Replace all direct logging of error objects with normalized
safe strings by using the existing getErrorMessage helper; specifically, change
calls like log.error(`${label} failed:`, entry.reason) (and other occurrences
that pass raw err/reason) to call log.error(`${label} failed:
${getErrorMessage(entry.reason || entry.err)}`) so logs never receive
unsanitized objects. Search for log.error usages in setup-wizard.ts (e.g.,
references around entry.reason/entry.err) and update each to use
getErrorMessage(...) and interpolate the returned string into the log message.
In `@web/src/utils/format.ts`:
- Line 59: The log call in format.ts currently interpolates the dynamic
currencyCode into the message string; change the call to pass currencyCode as a
separate argument to log.error so it goes through structured-logging
sanitization (i.e., replace the template string usage in the Intl.NumberFormat
failure handler and call log.error with a static message and currencyCode as an
additional parameter), ensuring the surrounding code that references
currencyCode and the log.error invocation in the formatter function is updated
accordingly.
🪄 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: 66770e98-2d9c-4630-936c-d64bb867f746
📒 Files selected for processing (57)
docs/design/ux-guidelines.mdweb/CLAUDE.mdweb/src/__tests__/hooks/usePolling.test.tsweb/src/__tests__/lib/logger.test.tsweb/src/__tests__/pages/settings/code-editor-utils.test.tsweb/src/__tests__/pages/settings/editor-autocomplete.test.tsweb/src/__tests__/pages/settings/editor-diff.test.tsweb/src/__tests__/pages/settings/editor-linter.test.tsweb/src/api/client.tsweb/src/api/endpoints/providers.tsweb/src/components/layout/Sidebar.tsxweb/src/components/ui/command-palette.tsxweb/src/components/ui/drawer.tsxweb/src/components/ui/error-boundary.tsxweb/src/hooks/useOptimisticUpdate.tsweb/src/hooks/useOrgChartData.tsweb/src/hooks/usePolling.tsweb/src/hooks/useSettingsDirtyState.tsweb/src/hooks/useWebSocket.tsweb/src/lib/logger.tsweb/src/pages/OrgChartPage.tsxweb/src/pages/SettingsPage.tsxweb/src/pages/WorkflowEditorPage.tsxweb/src/pages/providers/ProviderFormModal.tsxweb/src/pages/settings/CodeEditorPanel.tsxweb/src/pages/settings/SettingField.tsxweb/src/pages/settings/code-editor-utils.tsweb/src/pages/settings/editor-autocomplete.tsweb/src/pages/settings/editor-diff.tsweb/src/pages/settings/editor-extensions.tsweb/src/pages/settings/editor-linter.tsweb/src/pages/settings/sinks/SinkFormDrawer.tsxweb/src/pages/settings/utils.tsweb/src/pages/setup/AccountStep.tsxweb/src/pages/setup/ProviderProbeResults.tsxweb/src/stores/agents.tsweb/src/stores/analytics.tsweb/src/stores/approvals.tsweb/src/stores/auth.tsweb/src/stores/budget.tsweb/src/stores/company.tsweb/src/stores/meetings.tsweb/src/stores/messages.tsweb/src/stores/providers.tsweb/src/stores/providers/crud-actions.tsweb/src/stores/providers/detail-actions.tsweb/src/stores/providers/list-actions.tsweb/src/stores/providers/local-model-actions.tsweb/src/stores/providers/types.tsweb/src/stores/settings.tsweb/src/stores/setup-wizard.tsweb/src/stores/tasks.tsweb/src/stores/theme.tsweb/src/stores/websocket.tsweb/src/utils/dashboard.tsweb/src/utils/errors.tsweb/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: Agent
- GitHub Check: Build Backend
- GitHub Check: Build Web
- GitHub Check: Build Sandbox
- GitHub Check: Dashboard Test
- GitHub Check: Dependency Review
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (9)
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/**/*.{ts,tsx}: Use Tailwind semantic classes (text-foreground,bg-card,text-accent, etc.) or CSS variables (var(--so-*)) for colors. NEVER hardcode hex values or rgba() in.tsx/.tsfiles
Usefont-sansorfont-monofor typography (maps to Geist tokens). NEVER setfontFamilydirectly in styles
Use density-aware spacing tokens (p-card,gap-section-gap,gap-grid-gap) or standard Tailwind spacing. NEVER hardcode pixel values for layout spacing
Use token variables (var(--so-shadow-card-hover),border-border,border-bright) for shadows and borders
Do NOT usergba()with hardcoded values -- use design token variables
Do NOT hardcode Framer Motion transition durations -- use@/lib/motionpresets
Use/// <reference types="vite/client" />to enable side-effect imports for CSS in Storybook 10 with TypeScript 6
Ensure all TypeScript code passes type checking vianpm --prefix web run type-check
Bundle size must be auditable vianpm --prefix web run analyze(generates stats.html treemap)
Achieve Lighthouse performance audit target of 90+ vianpm --prefix web run lighthouse
web/src/**/*.{ts,tsx}: ALWAYS reuse existing components fromweb/src/components/ui/before creating new ones
Web Dashboard Design System (MANDATORY): Seeweb/CLAUDE.mdfor the full component inventory, design token rules, and post-training references (TS6, Storybook 10).
Files:
web/src/hooks/useSettingsDirtyState.tsweb/src/hooks/useOptimisticUpdate.tsweb/src/api/client.tsweb/src/hooks/useOrgChartData.tsweb/src/components/ui/error-boundary.tsxweb/src/pages/providers/ProviderFormModal.tsxweb/src/pages/OrgChartPage.tsxweb/src/pages/settings/utils.tsweb/src/components/ui/command-palette.tsxweb/src/__tests__/hooks/usePolling.test.tsweb/src/components/layout/Sidebar.tsxweb/src/components/ui/drawer.tsxweb/src/api/endpoints/providers.tsweb/src/pages/setup/AccountStep.tsxweb/src/pages/WorkflowEditorPage.tsxweb/src/utils/errors.tsweb/src/pages/settings/SettingField.tsxweb/src/stores/agents.tsweb/src/stores/company.tsweb/src/hooks/useWebSocket.tsweb/src/utils/dashboard.tsweb/src/utils/format.tsweb/src/pages/SettingsPage.tsxweb/src/__tests__/pages/settings/editor-autocomplete.test.tsweb/src/stores/settings.tsweb/src/stores/tasks.tsweb/src/pages/settings/CodeEditorPanel.tsxweb/src/stores/approvals.tsweb/src/stores/analytics.tsweb/src/pages/setup/ProviderProbeResults.tsxweb/src/stores/auth.tsweb/src/stores/meetings.tsweb/src/__tests__/pages/settings/editor-linter.test.tsweb/src/__tests__/pages/settings/editor-diff.test.tsweb/src/stores/budget.tsweb/src/__tests__/lib/logger.test.tsweb/src/lib/logger.tsweb/src/stores/setup-wizard.tsweb/src/stores/providers.tsweb/src/stores/websocket.tsweb/src/hooks/usePolling.tsweb/src/__tests__/pages/settings/code-editor-utils.test.tsweb/src/pages/settings/editor-autocomplete.tsweb/src/stores/providers/list-actions.tsweb/src/stores/providers/local-model-actions.tsweb/src/stores/providers/detail-actions.tsweb/src/stores/theme.tsweb/src/pages/settings/sinks/SinkFormDrawer.tsxweb/src/pages/settings/editor-diff.tsweb/src/stores/providers/types.tsweb/src/pages/settings/editor-extensions.tsweb/src/pages/settings/code-editor-utils.tsweb/src/stores/providers/crud-actions.tsweb/src/pages/settings/editor-linter.tsweb/src/stores/messages.ts
web/src/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
Run ESLint with zero warnings enforced via
npm --prefix web run lintLine length: 88 characters (ruff) for JavaScript/TypeScript files
Files:
web/src/hooks/useSettingsDirtyState.tsweb/src/hooks/useOptimisticUpdate.tsweb/src/api/client.tsweb/src/hooks/useOrgChartData.tsweb/src/components/ui/error-boundary.tsxweb/src/pages/providers/ProviderFormModal.tsxweb/src/pages/OrgChartPage.tsxweb/src/pages/settings/utils.tsweb/src/components/ui/command-palette.tsxweb/src/__tests__/hooks/usePolling.test.tsweb/src/components/layout/Sidebar.tsxweb/src/components/ui/drawer.tsxweb/src/api/endpoints/providers.tsweb/src/pages/setup/AccountStep.tsxweb/src/pages/WorkflowEditorPage.tsxweb/src/utils/errors.tsweb/src/pages/settings/SettingField.tsxweb/src/stores/agents.tsweb/src/stores/company.tsweb/src/hooks/useWebSocket.tsweb/src/utils/dashboard.tsweb/src/utils/format.tsweb/src/pages/SettingsPage.tsxweb/src/__tests__/pages/settings/editor-autocomplete.test.tsweb/src/stores/settings.tsweb/src/stores/tasks.tsweb/src/pages/settings/CodeEditorPanel.tsxweb/src/stores/approvals.tsweb/src/stores/analytics.tsweb/src/pages/setup/ProviderProbeResults.tsxweb/src/stores/auth.tsweb/src/stores/meetings.tsweb/src/__tests__/pages/settings/editor-linter.test.tsweb/src/__tests__/pages/settings/editor-diff.test.tsweb/src/stores/budget.tsweb/src/__tests__/lib/logger.test.tsweb/src/lib/logger.tsweb/src/stores/setup-wizard.tsweb/src/stores/providers.tsweb/src/stores/websocket.tsweb/src/hooks/usePolling.tsweb/src/__tests__/pages/settings/code-editor-utils.test.tsweb/src/pages/settings/editor-autocomplete.tsweb/src/stores/providers/list-actions.tsweb/src/stores/providers/local-model-actions.tsweb/src/stores/providers/detail-actions.tsweb/src/stores/theme.tsweb/src/pages/settings/sinks/SinkFormDrawer.tsxweb/src/pages/settings/editor-diff.tsweb/src/stores/providers/types.tsweb/src/pages/settings/editor-extensions.tsweb/src/pages/settings/code-editor-utils.tsweb/src/stores/providers/crud-actions.tsweb/src/pages/settings/editor-linter.tsweb/src/stores/messages.ts
web/src/**/*.{ts,tsx,css}
📄 CodeRabbit inference engine (CLAUDE.md)
NEVER hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions — use design tokens and
@/lib/motionpresets
Files:
web/src/hooks/useSettingsDirtyState.tsweb/src/hooks/useOptimisticUpdate.tsweb/src/api/client.tsweb/src/hooks/useOrgChartData.tsweb/src/components/ui/error-boundary.tsxweb/src/pages/providers/ProviderFormModal.tsxweb/src/pages/OrgChartPage.tsxweb/src/pages/settings/utils.tsweb/src/components/ui/command-palette.tsxweb/src/__tests__/hooks/usePolling.test.tsweb/src/components/layout/Sidebar.tsxweb/src/components/ui/drawer.tsxweb/src/api/endpoints/providers.tsweb/src/pages/setup/AccountStep.tsxweb/src/pages/WorkflowEditorPage.tsxweb/src/utils/errors.tsweb/src/pages/settings/SettingField.tsxweb/src/stores/agents.tsweb/src/stores/company.tsweb/src/hooks/useWebSocket.tsweb/src/utils/dashboard.tsweb/src/utils/format.tsweb/src/pages/SettingsPage.tsxweb/src/__tests__/pages/settings/editor-autocomplete.test.tsweb/src/stores/settings.tsweb/src/stores/tasks.tsweb/src/pages/settings/CodeEditorPanel.tsxweb/src/stores/approvals.tsweb/src/stores/analytics.tsweb/src/pages/setup/ProviderProbeResults.tsxweb/src/stores/auth.tsweb/src/stores/meetings.tsweb/src/__tests__/pages/settings/editor-linter.test.tsweb/src/__tests__/pages/settings/editor-diff.test.tsweb/src/stores/budget.tsweb/src/__tests__/lib/logger.test.tsweb/src/lib/logger.tsweb/src/stores/setup-wizard.tsweb/src/stores/providers.tsweb/src/stores/websocket.tsweb/src/hooks/usePolling.tsweb/src/__tests__/pages/settings/code-editor-utils.test.tsweb/src/pages/settings/editor-autocomplete.tsweb/src/stores/providers/list-actions.tsweb/src/stores/providers/local-model-actions.tsweb/src/stores/providers/detail-actions.tsweb/src/stores/theme.tsweb/src/pages/settings/sinks/SinkFormDrawer.tsxweb/src/pages/settings/editor-diff.tsweb/src/stores/providers/types.tsweb/src/pages/settings/editor-extensions.tsweb/src/pages/settings/code-editor-utils.tsweb/src/stores/providers/crud-actions.tsweb/src/pages/settings/editor-linter.tsweb/src/stores/messages.ts
web/src/components/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/components/**/*.{ts,tsx}: ALWAYS reuse existing components fromweb/src/components/ui/before creating new ones
Importcnfrom@/lib/utilsfor conditional class merging in components
Do NOT recreate status dots inline -- use<StatusBadge>component
Do NOT build card-with-header layouts from scratch -- use<SectionCard>component
Do NOT create metric displays withtext-metric font-bold-- use<MetricCard>component
Do NOT render initials circles manually -- use<Avatar>component
Do NOT create complex (>8 line) JSX inside.map()-- extract to a shared component
Files:
web/src/components/ui/error-boundary.tsxweb/src/components/ui/command-palette.tsxweb/src/components/layout/Sidebar.tsxweb/src/components/ui/drawer.tsx
web/src/components/ui/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/components/ui/**/*.{ts,tsx}: Place new shared components inweb/src/components/ui/with descriptive kebab-case filenames
Export props as a TypeScript interface for each component
Use design tokens exclusively in new components -- no hardcoded colors, fonts, or spacing
Files:
web/src/components/ui/error-boundary.tsxweb/src/components/ui/command-palette.tsxweb/src/components/ui/drawer.tsx
web/src/pages/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/pages/**/*.{ts,tsx}: Organize page-scoped sub-components inpages/<page-name>/subdirectories rather than inline in page files
Use lazy-loaded page components inweb/src/pages/with React Router and route guards for auth/setup
Use@AnimatedPresencewrapper for page transitions in React Router (keyed by route)
Files:
web/src/pages/providers/ProviderFormModal.tsxweb/src/pages/OrgChartPage.tsxweb/src/pages/settings/utils.tsweb/src/pages/setup/AccountStep.tsxweb/src/pages/WorkflowEditorPage.tsxweb/src/pages/settings/SettingField.tsxweb/src/pages/SettingsPage.tsxweb/src/pages/settings/CodeEditorPanel.tsxweb/src/pages/setup/ProviderProbeResults.tsxweb/src/pages/settings/editor-autocomplete.tsweb/src/pages/settings/sinks/SinkFormDrawer.tsxweb/src/pages/settings/editor-diff.tsweb/src/pages/settings/editor-extensions.tsweb/src/pages/settings/code-editor-utils.tsweb/src/pages/settings/editor-linter.ts
web/src/__tests__/**/*.{test,spec}.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/__tests__/**/*.{test,spec}.{ts,tsx}: All unit tests must pass vianpm --prefix web run testwith coverage scoped to files changed vs origin/main
Mirror test directory structure to source code inweb/src/__tests__/(matchessrc/structure for Vitest unit + property tests)
Files:
web/src/__tests__/hooks/usePolling.test.tsweb/src/__tests__/pages/settings/editor-autocomplete.test.tsweb/src/__tests__/pages/settings/editor-linter.test.tsweb/src/__tests__/pages/settings/editor-diff.test.tsweb/src/__tests__/lib/logger.test.tsweb/src/__tests__/pages/settings/code-editor-utils.test.ts
web/src/components/layout/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/components/layout/**/*.{ts,tsx}: MountToastContaineronce in AppLayout for the toast notification system
MountCommandPaletteonce in AppLayout and register commands viauseCommandPalettehook
Files:
web/src/components/layout/Sidebar.tsx
web/src/stores/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
Use
web/src/stores/for Zustand store definitions (auth, WebSocket, toast, analytics, and per-domain stores)
Files:
web/src/stores/agents.tsweb/src/stores/company.tsweb/src/stores/settings.tsweb/src/stores/tasks.tsweb/src/stores/approvals.tsweb/src/stores/analytics.tsweb/src/stores/auth.tsweb/src/stores/meetings.tsweb/src/stores/budget.tsweb/src/stores/setup-wizard.tsweb/src/stores/providers.tsweb/src/stores/websocket.tsweb/src/stores/providers/list-actions.tsweb/src/stores/providers/local-model-actions.tsweb/src/stores/providers/detail-actions.tsweb/src/stores/theme.tsweb/src/stores/providers/types.tsweb/src/stores/providers/crud-actions.tsweb/src/stores/messages.ts
🧠 Learnings (62)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-03T21:18:15.006Z
Learning: Applies to web/src/**/*.{ts,tsx} : Web Dashboard Design System (MANDATORY): See `web/CLAUDE.md` for the full component inventory, design token rules, and post-training references (TS6, Storybook 10).
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use Zustand stores for state management in the web dashboard; each domain has its own store module (auth, WebSocket, toast, analytics, setup, company, agents, budget, tasks, settings, providers, theme, per-domain stores)
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T12:44:29.466Z
Learning: Applies to web/src/stores/**/*.{ts,tsx} : Use Zustand stores in web dashboard for state management (auth, WebSocket, toast, analytics, domain shells)
📚 Learning: 2026-03-27T12:44:29.466Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T12:44:29.466Z
Learning: Applies to web/src/stores/**/*.{ts,tsx} : Use Zustand stores in web dashboard for state management (auth, WebSocket, toast, analytics, domain shells)
Applied to files:
web/src/api/client.tsweb/CLAUDE.mdweb/src/hooks/useWebSocket.tsweb/src/utils/dashboard.tsweb/src/pages/SettingsPage.tsxweb/src/stores/settings.tsweb/src/stores/tasks.tsweb/src/stores/approvals.tsweb/src/stores/analytics.tsweb/src/stores/auth.tsweb/src/stores/meetings.tsweb/src/stores/providers.tsweb/src/stores/websocket.tsweb/src/stores/providers/list-actions.tsweb/src/stores/providers/local-model-actions.tsweb/src/stores/providers/detail-actions.tsweb/src/stores/theme.tsweb/src/stores/providers/types.tsweb/src/stores/providers/crud-actions.ts
📚 Learning: 2026-04-03T18:36:51.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/stores/**/*.{ts,tsx} : Use `web/src/stores/` for Zustand store definitions (auth, WebSocket, toast, analytics, and per-domain stores)
Applied to files:
web/src/api/client.tsweb/CLAUDE.mdweb/src/stores/settings.tsweb/src/stores/tasks.tsweb/src/stores/approvals.tsweb/src/stores/analytics.tsweb/src/stores/auth.tsweb/src/stores/meetings.tsweb/src/stores/providers.tsweb/src/stores/websocket.tsweb/src/stores/providers/list-actions.tsweb/src/stores/providers/local-model-actions.tsweb/src/stores/providers/detail-actions.tsweb/src/stores/theme.tsweb/src/stores/providers/types.tsweb/src/stores/providers/crud-actions.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:
web/src/pages/providers/ProviderFormModal.tsxweb/src/stores/providers.tsweb/src/stores/providers/types.ts
📚 Learning: 2026-03-30T10:41:40.176Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Update the relevant `docs/design/` page to reflect new reality when approved deviations from spec occur
Applied to files:
docs/design/ux-guidelines.md
📚 Learning: 2026-03-31T14:31:11.894Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:31:11.894Z
Learning: Applies to docs/design/*.md : Update the relevant `docs/design/` page when approved deviations occur to reflect the new reality
Applied to files:
docs/design/ux-guidelines.md
📚 Learning: 2026-04-03T21:18:15.006Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-03T21:18:15.006Z
Learning: Applies to web/src/**/*.{ts,tsx} : Web Dashboard Design System (MANDATORY): See `web/CLAUDE.md` for the full component inventory, design token rules, and post-training references (TS6, Storybook 10).
Applied to files:
docs/design/ux-guidelines.mdweb/src/components/layout/Sidebar.tsxweb/src/components/ui/drawer.tsxweb/CLAUDE.mdweb/src/utils/dashboard.tsweb/src/pages/settings/code-editor-utils.ts
📚 Learning: 2026-04-01T20:43:51.878Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T20:43:51.878Z
Learning: Applies to web/src/**/*.{ts,tsx} : Always reuse existing components from `web/src/components/ui/` before creating new ones. Never hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions -- use design tokens and `@/lib/motion` presets.
Applied to files:
docs/design/ux-guidelines.md
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/**/*.{ts,tsx,css} : Never hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions in web code — use design tokens and `@/lib/motion` presets
Applied to files:
docs/design/ux-guidelines.md
📚 Learning: 2026-04-03T21:18:15.006Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-03T21:18:15.006Z
Learning: Applies to web/src/**/*.{ts,tsx,css} : NEVER hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions — use design tokens and `@/lib/motion` presets
Applied to files:
docs/design/ux-guidelines.md
📚 Learning: 2026-03-31T14:31:11.894Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:31:11.894Z
Learning: Applies to web/src/**/*.{ts,tsx} : NEVER hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions — use design tokens and `@/lib/motion` presets
Applied to files:
docs/design/ux-guidelines.md
📚 Learning: 2026-04-03T18:36:51.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/styles/**/*.{ts,tsx,css} : Use design tokens in `web/src/styles/` (--so-* CSS custom properties) as single source of truth and bridge to Tailwind theme
Applied to files:
docs/design/ux-guidelines.md
📚 Learning: 2026-04-03T18:36:51.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : Use design tokens exclusively in new components -- no hardcoded colors, fonts, or spacing
Applied to files:
docs/design/ux-guidelines.md
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/components/ui/**/*.tsx : Use design tokens exclusively in new components -- no hardcoded colors, fonts, or spacing
Applied to files:
docs/design/ux-guidelines.md
📚 Learning: 2026-03-30T10:20:08.544Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:20:08.544Z
Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : When creating new shared web components, place in web/src/components/ui/ with kebab-case filename, create .stories.tsx alongside with all states (default, hover, loading, error, empty), export props as TypeScript interface, use design tokens exclusively with no hardcoded colors/fonts/spacing, and import cn from `@/lib/utils` for conditional class merging
Applied to files:
docs/design/ux-guidelines.mdweb/CLAUDE.md
📚 Learning: 2026-03-27T22:32:26.927Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T22:32:26.927Z
Learning: Applies to web/src/components/ui/*.{tsx,ts} : For new shared React components: place in web/src/components/ui/ with kebab-case filename, create .stories.tsx with all states, export props as TypeScript interface, use design tokens exclusively
Applied to files:
docs/design/ux-guidelines.mdweb/CLAUDE.mdweb/src/pages/settings/sinks/SinkFormDrawer.tsxweb/src/stores/providers/types.ts
📚 Learning: 2026-03-31T14:31:11.894Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:31:11.894Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use React 19, TypeScript 6.0+, and design system tokens from shadcn/ui + Tailwind CSS 4 + Radix UI in web dashboard
Applied to files:
web/src/pages/OrgChartPage.tsxweb/src/utils/dashboard.ts
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/**/*.{tsx,ts} : ALWAYS reuse existing components from `web/src/components/ui/` before creating new ones (StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup, Drawer, InputField, SelectField, SliderField, ToggleField, TaskStatusIndicator, PriorityBadge, ProviderHealthBadge, TokenUsageBar, CodeMirrorEditor, SegmentedControl, ThemeToggle, LiveRegion, MobileUnsupportedOverlay, LazyCodeMirrorEditor, TagInput, MetadataGrid, ProjectStatusBadge, ContentTypeBadge)
Applied to files:
web/src/pages/OrgChartPage.tsx
📚 Learning: 2026-03-30T10:20:08.544Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:20:08.544Z
Learning: Applies to web/src/**/*.{ts,tsx} : Always reuse existing components from web/src/components/ui/ (StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast/ToastContainer, Skeleton variants, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup/StaggerItem, Drawer, form fields, TaskStatusIndicator, PriorityBadge, ProviderHealthBadge, TokenUsageBar, CodeMirrorEditor, SegmentedControl, ThemeToggle, LiveRegion, MobileUnsupportedOverlay, LazyCodeMirrorEditor) before creating new components
Applied to files:
web/src/pages/OrgChartPage.tsx
📚 Learning: 2026-03-27T12:44:29.466Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T12:44:29.466Z
Learning: Applies to web/src/**/*.{ts,tsx} : Always reuse existing components from `web/src/components/ui/` (StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup/StaggerItem) before creating new ones
Applied to files:
web/src/pages/OrgChartPage.tsx
📚 Learning: 2026-04-03T18:36:51.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/components/**/*.{ts,tsx} : Import `cn` from `@/lib/utils` for conditional class merging in components
Applied to files:
web/src/pages/OrgChartPage.tsxweb/src/components/ui/drawer.tsx
📚 Learning: 2026-04-03T18:36:51.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/components/layout/**/*.{ts,tsx} : Mount `CommandPalette` once in AppLayout and register commands via `useCommandPalette` hook
Applied to files:
web/src/components/ui/command-palette.tsx
📚 Learning: 2026-04-03T18:36:51.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/__tests__/**/*.{test,spec}.{ts,tsx} : All unit tests must pass via `npm --prefix web run test` with coverage scoped to files changed vs origin/main
Applied to files:
web/src/__tests__/hooks/usePolling.test.tsweb/src/__tests__/pages/settings/editor-autocomplete.test.tsweb/src/__tests__/pages/settings/editor-linter.test.tsweb/src/__tests__/pages/settings/editor-diff.test.tsweb/src/__tests__/lib/logger.test.tsweb/src/__tests__/pages/settings/code-editor-utils.test.ts
📚 Learning: 2026-03-30T10:41:40.176Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use `@/components/ui/animated-presence` (Framer Motion AnimatePresence keyed by route) for page transitions
Applied to files:
web/src/components/ui/drawer.tsx
📚 Learning: 2026-04-03T18:36:51.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do NOT hardcode Framer Motion transition durations -- use `@/lib/motion` presets
Applied to files:
web/src/components/ui/drawer.tsx
📚 Learning: 2026-04-03T18:36:51.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/pages/**/*.{ts,tsx} : Use `AnimatedPresence` wrapper for page transitions in React Router (keyed by route)
Applied to files:
web/src/components/ui/drawer.tsx
📚 Learning: 2026-03-31T14:28:28.895Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:28:28.895Z
Learning: Web dashboard: see `web/CLAUDE.md` for commands, design system, and component inventory
Applied to files:
web/CLAUDE.md
📚 Learning: 2026-03-30T10:41:40.176Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use Zustand stores for state management in the web dashboard; each domain has its own store module (auth, WebSocket, toast, analytics, setup, company, agents, budget, tasks, settings, providers, theme, per-domain stores)
Applied to files:
web/CLAUDE.mdweb/src/stores/company.tsweb/src/utils/dashboard.tsweb/src/pages/SettingsPage.tsxweb/src/stores/settings.tsweb/src/stores/tasks.tsweb/src/stores/approvals.tsweb/src/stores/analytics.tsweb/src/stores/auth.tsweb/src/stores/setup-wizard.tsweb/src/stores/providers.tsweb/src/stores/websocket.tsweb/src/stores/providers/list-actions.tsweb/src/stores/providers/local-model-actions.tsweb/src/stores/providers/detail-actions.tsweb/src/stores/theme.tsweb/src/stores/providers/types.tsweb/src/stores/providers/crud-actions.ts
📚 Learning: 2026-04-01T18:57:46.655Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T18:57:46.655Z
Learning: Applies to web/package.json : Web dashboard Node.js 22+, TypeScript 6.0+, dependencies in `web/package.json`
Applied to files:
web/CLAUDE.mdweb/src/utils/dashboard.ts
📚 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/**/*.py : Package structure: src/synthorg/ organized as: api/ (REST+WebSocket, Litestar), auth/ (auth subpackage), backup/ (scheduled/manual backups), budget/ (cost tracking, CFO), cli/ (superseded by Go CLI), communication/ (message bus, meetings), config/ (YAML loading), core/ (domain models, resilience config), engine/ (orchestration, task state, coordination, approval gates, stagnation detection, context budget, compaction), hr/ (hiring, performance, promotion), memory/ (pluggable backend, Mem0, retrieval, consolidation), persistence/ (operational data, SQLite, settings), observability/ (logging, correlation, sinks), providers/ (LLM abstraction, LiteLLM, auth types, presets, runtime CRUD), settings/ (runtime-editable, typed definitions, encryption, config bridge), security/ (SecOps, rule engine, output scanning, progressive trust, autonomy levels), templates/ (company templates, personalities), tools/ (registry, built-in tools, git, sandbox, code_runner, MCP...
Applied to files:
web/CLAUDE.md
📚 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/CLAUDE.md
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to site/**/*.{astro,tsx,ts} : Landing page in `site/` (Astro + React islands via `astrojs/react`), includes `/get/` CLI install page, contact form, interactive dashboard preview, SEO
Applied to files:
web/CLAUDE.md
📚 Learning: 2026-03-30T10:41:40.176Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Applies to web/src/**/*.stories.tsx : Storybook 10: import from `storybook/test` (not `storybook/test`), `storybook/actions` (not `storybook/addon-actions`)
Applied to files:
web/CLAUDE.mdweb/src/pages/settings/sinks/SinkFormDrawer.tsx
📚 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 docs/** : Docs source in docs/ (Markdown, built with Zensical); design spec in docs/design/ (7 pages: index, agents, organization, communication, engine, memory, operations)
Applied to files:
web/CLAUDE.md
📚 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 DEBUG logging for object creation, internal flow, and entry/exit of key functions.
Applied to files:
web/src/stores/company.ts
📚 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 : All error paths must log at WARNING or ERROR with context before raising.
Applied to files:
web/src/stores/company.ts
📚 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 : Every module with business logic must import get_logger from ai_company.observability and define logger = get_logger(__name__); never use import logging or logging.getLogger() or print() in application code
Applied to files:
web/src/stores/company.ts
📚 Learning: 2026-04-03T07:43:01.127Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-03T07:43:01.127Z
Learning: Applies to web/src/**/*.{ts,tsx} : ESLint enforces zero warnings on web dashboard code via pre-commit (conditional on `web/src/**/*.{ts,tsx}`)
Applied to files:
web/src/utils/dashboard.ts
📚 Learning: 2026-03-30T10:20:08.544Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:20:08.544Z
Learning: Applies to web/src/**/*.{ts,tsx} : Web dashboard shadows/borders: use token variables (var(--so-shadow-card-hover), border-border, border-bright); never hardcode shadow or border values
Applied to files:
web/src/utils/dashboard.ts
📚 Learning: 2026-04-03T18:36:51.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/components/**/*.{ts,tsx} : Do NOT create metric displays with `text-metric font-bold` -- use `<MetricCard>` component
Applied to files:
web/src/utils/dashboard.ts
📚 Learning: 2026-04-03T18:36:51.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/components/layout/**/*.{ts,tsx} : Mount `ToastContainer` once in AppLayout for the toast notification system
Applied to files:
web/src/pages/SettingsPage.tsx
📚 Learning: 2026-04-03T18:36:51.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/__tests__/**/*.{test,spec}.{ts,tsx} : Mirror test directory structure to source code in `web/src/__tests__/` (matches `src/` structure for Vitest unit + property tests)
Applied to files:
web/src/__tests__/pages/settings/editor-autocomplete.test.tsweb/src/__tests__/pages/settings/editor-linter.test.tsweb/src/__tests__/pages/settings/editor-diff.test.tsweb/src/__tests__/lib/logger.test.tsweb/src/__tests__/pages/settings/code-editor-utils.test.ts
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/__tests__/**/*.{test,spec}.{ts,tsx} : Vitest unit tests must use coverage scoped to files changed vs origin/main branch
Applied to files:
web/src/__tests__/pages/settings/editor-autocomplete.test.tsweb/src/__tests__/pages/settings/editor-linter.test.tsweb/src/__tests__/pages/settings/editor-diff.test.tsweb/src/__tests__/lib/logger.test.tsweb/src/__tests__/pages/settings/code-editor-utils.test.ts
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/**/*.stories.tsx : Storybook 10: Use `parameters.a11y.test: 'error' | 'todo' | 'off'` for a11y testing configuration (replaces old `.element` and `.manual`)
Applied to files:
web/src/__tests__/pages/settings/editor-autocomplete.test.tsweb/src/__tests__/pages/settings/editor-linter.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 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__/pages/settings/editor-autocomplete.test.tsweb/src/__tests__/pages/settings/editor-linter.test.tsweb/src/__tests__/pages/settings/editor-diff.test.tsweb/src/__tests__/lib/logger.test.tsweb/src/__tests__/pages/settings/code-editor-utils.test.ts
📚 Learning: 2026-03-30T10:41:40.176Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Applies to web/src/__tests__/**/*.test.{ts,tsx} : Use property-based testing with fast-check in React tests (`fc.assert` + `fc.property`)
Applied to files:
web/src/__tests__/pages/settings/editor-autocomplete.test.tsweb/src/__tests__/pages/settings/editor-linter.test.tsweb/src/__tests__/pages/settings/editor-diff.test.ts
📚 Learning: 2026-04-03T18:36:51.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/**/*.stories.tsx : Use `parameters.a11y.test: 'error' | 'todo' | 'off'` for a11y testing in Storybook 10 to enforce WCAG compliance (replaces old `.element` and `.manual`)
Applied to files:
web/src/__tests__/pages/settings/editor-autocomplete.test.tsweb/src/__tests__/pages/settings/editor-linter.test.ts
📚 Learning: 2026-03-30T10:20:08.544Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:20:08.544Z
Learning: Applies to web/**/*.test.{ts,tsx} : Web dashboard: Use React Hypothesis (fast-check) for property-based testing with fc.assert + fc.property
Applied to files:
web/src/__tests__/pages/settings/editor-autocomplete.test.tsweb/src/__tests__/pages/settings/editor-linter.test.tsweb/src/__tests__/pages/settings/editor-diff.test.tsweb/src/__tests__/lib/logger.test.ts
📚 Learning: 2026-04-03T18:36:51.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/**/*.stories.tsx : Import from `storybook/test` (not `storybook/test`) and `storybook/actions` (not `storybook/addon-actions`) in Storybook 10
Applied to files:
web/src/__tests__/pages/settings/editor-autocomplete.test.tsweb/src/__tests__/pages/settings/editor-diff.test.tsweb/src/pages/settings/sinks/SinkFormDrawer.tsx
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/**/*.stories.tsx : Storybook 10: Import from `storybook/test` instead of `storybook/test`
Applied to files:
web/src/__tests__/pages/settings/editor-autocomplete.test.tsweb/src/__tests__/pages/settings/editor-linter.test.tsweb/src/__tests__/pages/settings/editor-diff.test.ts
📚 Learning: 2026-04-03T18:36:51.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/**/*.{ts,tsx} : Ensure all TypeScript code passes type checking via `npm --prefix web run type-check`
Applied to files:
web/src/__tests__/pages/settings/editor-linter.test.ts
📚 Learning: 2026-04-03T18:36:51.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/**/*.e2e.{ts,tsx} : Maintain visual regression test baselines via Playwright (`npm --prefix web run e2e` and `npm --prefix web run e2e:update`)
Applied to files:
web/src/__tests__/pages/settings/editor-diff.test.ts
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget tracking includes pre-flight/in-flight checks, auto-downgrade, billing periods, cost tiers, quota/subscription. CFO includes anomaly detection, efficiency analysis, downgrade recommendations.
Applied to files:
web/src/stores/budget.ts
📚 Learning: 2026-03-19T07:13:44.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:13:44.964Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget package (budget/): cost tracking, budget enforcement (pre-flight/in-flight checks, auto-downgrade), billing periods, cost tiers, quota/subscription tracking, CFO cost optimization (anomaly detection, efficiency analysis, downgrade recommendations, approval decisions), spending reports, budget errors (BudgetExhaustedError, DailyLimitExceededError, QuotaExhaustedError)
Applied to files:
web/src/stores/budget.ts
📚 Learning: 2026-03-30T10:20:08.544Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:20:08.544Z
Learning: Applies to web/**/*.stories.{ts,tsx} : Storybook 10: Use storybook/test (not storybook/test) and storybook/actions (not storybook/addon-actions) import paths
Applied to files:
web/src/stores/providers.ts
📚 Learning: 2026-04-03T21:18:15.006Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-03T21:18:15.006Z
Learning: Applies to web/src/**/*.{ts,tsx,js,jsx} : Line length: 88 characters (ruff) for JavaScript/TypeScript files
Applied to files:
web/src/pages/settings/editor-diff.tsweb/src/pages/settings/editor-extensions.tsweb/src/pages/settings/code-editor-utils.ts
📚 Learning: 2026-04-03T18:36:51.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/**/tsconfig.json : Explicitly list required types in tsconfig.json `types` field (e.g. `"types": ["vitest/globals"]`) -- TypeScript 6 no longer auto-discovers `types/*`
Applied to files:
web/src/stores/providers/types.ts
📚 Learning: 2026-04-03T18:36:51.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/components/ui/**/*.stories.tsx : Create a `.stories.tsx` file alongside each new component with all states (default, hover, loading, error, empty)
Applied to files:
web/src/stores/providers/types.ts
📚 Learning: 2026-04-03T18:36:51.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : Export props as a TypeScript interface for each component
Applied to files:
web/src/stores/providers/types.ts
📚 Learning: 2026-03-30T10:41:40.176Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : Create new shared components in `web/src/components/ui/` with `.stories.tsx` Storybook file covering all states (default, hover, loading, error, empty)
Applied to files:
web/src/stores/providers/types.ts
📚 Learning: 2026-04-03T18:36:51.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/**/*.{ts,tsx} : Bundle size must be auditable via `npm --prefix web run analyze` (generates stats.html treemap)
Applied to files:
web/src/pages/settings/code-editor-utils.ts
📚 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/settings/**/*.py : Settings package (settings/): runtime-editable settings persistence (DB > env > YAML > code defaults), typed definitions (9 namespaces), Fernet encryption for sensitive values, config bridge (JSON serialization for Pydantic/collections), ConfigResolver (typed accessors), validation, registry, change notifications via message bus, SettingsSubscriber protocol, SettingsChangeDispatcher (polls `#settings` channel, routes to subscribers, restart_required filtering)
Applied to files:
web/src/pages/settings/code-editor-utils.ts
🪛 ast-grep (0.42.0)
web/src/pages/settings/editor-linter.ts
[warning] 59-59: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp("${escapeRegex(namespace)}"\\s*:)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html
(regexp-from-variable)
[warning] 70-70: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp("${escapeRegex(namespace)}"\\s*:\\s*\\{)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html
(regexp-from-variable)
[warning] 74-74: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp("${escapeRegex(key)}"\\s*:)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html
(regexp-from-variable)
[warning] 97-97: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(^${escapeRegex(namespace)}\\s*:, 'm')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html
(regexp-from-variable)
[warning] 107-107: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(^${escapeRegex(namespace)}\\s*:, 'm')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html
(regexp-from-variable)
[warning] 112-112: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(^(\\s+)${escapeRegex(key)}\\s*:, 'm')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html
(regexp-from-variable)
🪛 GitHub Check: CodeQL
web/src/lib/logger.ts
[warning] 32-32: Log injection
Log entry depends on a user-provided value.
| it('passes objects through unchanged', () => { | ||
| const log = createLogger('m') | ||
| const ctx = { key: 'value', count: 42 } | ||
| log.warn('context', ctx) | ||
| expect(warnSpy).toHaveBeenCalledWith('[m]', 'context', ctx) | ||
| }) |
There was a problem hiding this comment.
This test locks in unsafe object pass-through in production logging.
The assertion at Line 50 enshrines unsanitized object forwarding, which undermines the stated sanitization goal and makes log hardening a breaking change.
🔧 Suggested test direction
- it('passes objects through unchanged', () => {
+ it('sanitizes object arguments before forwarding', () => {
const log = createLogger('m')
const ctx = { key: 'value', count: 42 }
log.warn('context', ctx)
- expect(warnSpy).toHaveBeenCalledWith('[m]', 'context', ctx)
+ const forwarded = warnSpy.mock.calls[0]?.[2]
+ expect(typeof forwarded).toBe('string')
+ expect(forwarded).toContain('[object Object]')
})🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/__tests__/lib/logger.test.ts` around lines 46 - 51, The test
currently asserts that createLogger('m') forwards the original object reference
(ctx) to warnSpy, locking in unsafe pass-through; change the assertion to verify
that createLogger sanitizes/serializes the object rather than forwarding the
exact reference: update the test for the "passes objects through unchanged" case
to call createLogger and log.warn (same as now) but assert against a
sanitized/serialized form (or a deep-equal copy) instead of the original ctx
reference and/or assert that the argument is not the same object instance; refer
to createLogger, the warnSpy used in the test, and the test case name when
making the change.
| describe('settingsAutocompleteExtension', () => { | ||
| it('returns an Extension when given valid inputs', () => { | ||
| const entries = [makeEntry('api', 'retries')] | ||
| const ext: Extension = settingsAutocompleteExtension( | ||
| () => 'json', | ||
| () => entries, | ||
| ) | ||
| // The extension should be truthy (autocompletion returns an array of extensions) | ||
| expect(ext).toBeTruthy() | ||
| }) | ||
|
|
||
| it('returns an Extension for yaml format', () => { | ||
| const entries = [makeEntry('api', 'retries')] | ||
| const ext: Extension = settingsAutocompleteExtension( | ||
| () => 'yaml', | ||
| () => entries, | ||
| ) | ||
| expect(ext).toBeTruthy() | ||
| }) | ||
|
|
||
| it('returns an Extension with empty entries', () => { | ||
| const ext: Extension = settingsAutocompleteExtension( | ||
| () => 'json', | ||
| () => [], | ||
| ) | ||
| expect(ext).toBeTruthy() | ||
| }) | ||
|
|
||
| it('accepts entries with enum values', () => { | ||
| const entries = [ | ||
| makeEntry('api', 'log_level', { | ||
| type: 'enum', | ||
| enumValues: ['debug', 'info', 'warning', 'error'], | ||
| }), | ||
| ] | ||
| const ext: Extension = settingsAutocompleteExtension( | ||
| () => 'json', | ||
| () => entries, | ||
| ) | ||
| expect(ext).toBeTruthy() | ||
| }) | ||
|
|
||
| it('accepts multiple namespaces', () => { | ||
| const entries = [ | ||
| makeEntry('api', 'retries'), | ||
| makeEntry('api', 'timeout'), | ||
| makeEntry('budget', 'cap'), | ||
| makeEntry('security', 'level'), | ||
| ] | ||
| const ext: Extension = settingsAutocompleteExtension( | ||
| () => 'json', | ||
| () => entries, | ||
| ) | ||
| expect(ext).toBeTruthy() | ||
| }) |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Assert autocomplete behavior, not just construction.
Every case only checks that the factory returns something truthy. A no-op implementation that always returns [] would still pass, so namespace, key, and enum regressions can slip through. Please drive representative JSON/YAML inputs through the extension and assert the actual suggestions that come back.
| function sanitizeArg(value: unknown): unknown { | ||
| if (typeof value === 'string') return sanitizeForLog(value) | ||
| if (value instanceof Error) return sanitizeForLog(value) | ||
| // Structured objects pass through for devtools inspection | ||
| if (value !== null && typeof value === 'object') return value | ||
| // Sanitize other primitives (numbers pass through String() harmlessly, | ||
| // but null/undefined/boolean/symbol get consistent treatment) | ||
| return sanitizeForLog(value) | ||
| } |
There was a problem hiding this comment.
Sanitize the primary message and structured args before they reach console.*.
message is forwarded raw, and sanitizeArg() returns plain objects unchanged. That means interpolated attacker-controlled text still bypasses sanitizeForLog — for example, web/src/stores/providers/list-actions.ts now places provider names in the unsanitized message slot — so the log-injection hardening is incomplete here.
Suggested hardening
-function sanitizeArg(value: unknown): unknown {
- if (typeof value === 'string') return sanitizeForLog(value)
- if (value instanceof Error) return sanitizeForLog(value)
- // Structured objects pass through for devtools inspection
- if (value !== null && typeof value === 'object') return value
- // Sanitize other primitives (numbers pass through String() harmlessly,
- // but null/undefined/boolean/symbol get consistent treatment)
- return sanitizeForLog(value)
+function sanitizeArg(
+ value: unknown,
+ seen = new WeakSet<object>(),
+): unknown {
+ if (typeof value === 'string' || value instanceof Error) {
+ return sanitizeForLog(value)
+ }
+ if (value !== null && typeof value === 'object') {
+ if (seen.has(value)) return '[circular]'
+ seen.add(value)
+ if (Array.isArray(value)) {
+ return value.map((item) => sanitizeArg(item, seen))
+ }
+ return Object.fromEntries(
+ Object.entries(value as Record<string, unknown>).map(([key, nested]) => [
+ key,
+ sanitizeArg(nested, seen),
+ ]),
+ )
+ }
+ return sanitizeForLog(value)
}
@@
warn(message: string, ...args: unknown[]) {
- console.warn(prefix, message, ...args.map(sanitizeArg))
+ console.warn(
+ prefix,
+ sanitizeForLog(message),
+ ...args.map((arg) => sanitizeArg(arg)),
+ )
},
error(message: string, ...args: unknown[]) {
- console.error(prefix, message, ...args.map(sanitizeArg))
+ console.error(
+ prefix,
+ sanitizeForLog(message),
+ ...args.map((arg) => sanitizeArg(arg)),
+ )
},Also applies to: 25-33
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/lib/logger.ts` around lines 9 - 17, The current logging sanitization
is incomplete: sanitizeArg currently returns plain objects and forwards the
primary message raw, allowing log-injection via interpolated strings; update
sanitizeArg and the logging call sites so the main message is passed through
sanitizeForLog before any console.* call and make sanitizeArg perform deep
sanitization for objects (recursively sanitize all string properties) and
sanitize Error instances (at least error.message and error.stack) instead of
returning them unchanged; ensure any structured args used for devtools
inspection are replaced with their sanitized equivalents before being logged by
the logger functions.
There was a problem hiding this comment.
Actionable comments posted: 10
♻️ Duplicate comments (1)
web/src/lib/logger.ts (1)
35-40:⚠️ Potential issue | 🟠 MajorLog injection risk:
messageparameter bypasses sanitization.The
messagestring is forwarded directly toconsole.warn/console.errorwithout sanitization. If callers interpolate user-controlled values into the message (e.g.,log.error(\Provider "${providerName}" failed`)), those values bypasssanitizeForLog`. This is flagged by CodeQL at line 39.Consider sanitizing the message parameter to complete the hardening:
🔒 Proposed fix to sanitize message
warn(message: string, ...args: unknown[]) { - console.warn(prefix, message, ...args.map(sanitizeArg)) + console.warn(prefix, sanitizeForLog(message), ...args.map(sanitizeArg)) }, error(message: string, ...args: unknown[]) { - console.error(prefix, message, ...args.map(sanitizeArg)) + console.error(prefix, sanitizeForLog(message), ...args.map(sanitizeArg)) },🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/lib/logger.ts` around lines 35 - 40, The warn and error methods currently pass the message string unsanitized to console, creating a log-injection risk; update the implementations of warn and error to sanitize the message (using the existing sanitizeForLog or sanitizeArg helper) before calling console.warn/console.error so both the message and ...args are sanitized (e.g., apply sanitizeForLog to message and keep ...args.map(sanitizeArg)), ensuring all inputs logged by warn and error are sanitized; refer to the warn and error functions and the sanitizeArg/sanitizeForLog helpers to locate the change.
🤖 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__/lib/logger.test.ts`:
- Around line 77-84: The test currently assumes import.meta.env.DEV is true;
make that explicit by stubbing the DEV flag before creating the logger: in the
test, define import.meta.env with DEV true using a configurable
Object.defineProperty on import.meta (or set a temporary global stub) prior to
calling createLogger('m'), then restore or delete the stub after the assertion;
reference createLogger and the existing debug spy (vi.spyOn(console, 'debug'))
so the test no longer relies on the environment default.
In `@web/src/pages/settings/editor-autocomplete.ts`:
- Around line 165-265: The yamlCompletionSource function is too long and
duplicates the namespace lookup loop; extract a new helper function
findYamlNamespace(text: string, lineFrom: number): string | null that
encapsulates the logic which scans lines above a given position for an
unindented namespace key, then replace both duplicated loops inside
yamlCompletionSource with calls to findYamlNamespace (use the existing variables
text and lineObj.from when calling), and keep all existing behavior (return null
when not found).
- Around line 65-161: jsonCompletionSource is too long (~93 lines); split it
into small helpers to keep the top-level function under 50 lines while retaining
identical behavior and return shapes. Extract the backward-scan/namespace
detection into a helper (e.g., detectCurrentNamespace(text: string, pos:
number): string | null), extract the value-position regex logic into
findValueMatch(before: string): {key: string, partial: string} | null, extract
the key-position regex logic into findKeyMatch(before: string): {partial:
string} | null, and extract the option-builders into buildEnumOptions(setting,
currentNamespace, pos, partial), buildKeyOptions(keyInfo, pos, partial) and
buildNamespaceOptions(schema, pos). Have jsonCompletionSource call these helpers
in the same order (context detection → enum completion → key completion →
namespace completion) and return the same CompletionResult objects so
behavior/tests remain unchanged.
In `@web/src/pages/settings/editor-diff.ts`:
- Around line 153-156: Replace the hardcoded marginRight: '2px' in the
'.cm-diff-gutter' style with the centralized spacing token or CSS variable used
across the dashboard (e.g., the spacing token export or a --space-XX CSS var) so
spacing is not hardcoded; update the style in
web/src/pages/settings/editor-diff.ts by importing/using the appropriate token
name and assigning that token/CSS variable to marginRight instead of the literal
'2px' (target the '.cm-diff-gutter' key and its marginRight property).
- Around line 39-60: computeLineDiff is building a full LCS matrix on every
editor change and is invoked from dispatchDiff in CodeEditorPanel; to fix,
normalize inputs before any equality checks (apply the CRLF -> LF replace on
both serverText and editedText at the top) and then early-return when texts are
equal; next, add a safe matrix-size guard in computeLineDiff (e.g., if (
(n+1)*(m+1) > SOME_THRESHOLD ) ) to skip the O(n*m) LCS and instead fall back to
a cheaper heuristic diff routine or line-by-line comparison; update
dispatchDiff/CodeEditorPanel to use the normalized strings and ensure it calls
the cheap fallback when the matrix-size guard trips so we avoid creating huge dp
arrays on large documents.
- Around line 80-93: The code currently pushes separate 'added' and 'removed'
markers for pure replacements and later a gutter dedupe hides them, so change
detection never surfaces; modify the two unmatched-loops that use editedMatched,
serverMatched, diffs, m, n and the Math.max(1, Math.min(i + 1, m)) mapping so
that when an unmatched server index i and an unmatched edited index j correspond
to the same target line you emit a single { line: L, kind: 'changed' } and mark
both indexes as handled (remove from editedMatched/serverMatched or track
handled), otherwise fall back to pushing 'added' or 'removed' as before; this
ensures replacements are emitted as 'changed' before the later gutter dedupe.
In `@web/src/pages/settings/editor-linter.ts`:
- Around line 240-257: The linter currently only rejects non-object top-level
values but must mirror parseText’s structural checks: before schema validation,
enforce the 64 KiB size limit on text (<= 65536 bytes) and validate that the
parsed top-level is an object whose values (namespaces) are each objects (not
arrays or primitives); if any check fails, push the same diagnostics into
diagnostics and return early. Update the block that assigns parsed (and uses
format/text/diagnostics) to perform these checks the same way parseText does so
inputs like foo: [] or oversized documents fail during linting as well as on
save.
In `@web/src/stores/analytics.ts`:
- Line 76: The logger call currently interpolates dept.name into the message;
change the call in the log.warn invocation so the message string does not
include dept.name but instead pass dept.name as a separate argument (alongside
the error) to ensure the logger can sanitize it; update the log.warn call that
references dept.name and err accordingly so the format string contains no direct
interpolation and dept.name and err are provided as parameters.
In `@web/src/stores/providers/crud-actions.ts`:
- Around line 109-124: After successfully deleting a provider in deleteProvider,
if the deleted provider equals the currently selectedProvider clear the
per-provider detail and test-result state: call clearDetail() (the existing
function that resets the detail slice) and remove/clear any provider-scoped
result fields (e.g. lastTestResult or similar keys in the store) so the UI won't
keep showing a deleted provider; implement this by checking
state.selectedProvider?.name === name inside the success branch (where providers
and healthMap are pruned) and then invoke clearDetail() and reset the
provider-scoped result fields in the same set(...) update.
In `@web/src/stores/providers/list-actions.ts`:
- Around line 38-46: Replace raw error objects passed to logging with sanitized
messages using getErrorMessage; specifically, in the provider health fetch loop
update the log.warn call that currently logs result.reason to pass
getErrorMessage(result.reason), and in the outer catch update the log.error call
that currently logs err to pass getErrorMessage(err) (while preserving the
existing context strings and the checks/sets around healthMap, listLoading,
requestId/_listRequestId).
---
Duplicate comments:
In `@web/src/lib/logger.ts`:
- Around line 35-40: The warn and error methods currently pass the message
string unsanitized to console, creating a log-injection risk; update the
implementations of warn and error to sanitize the message (using the existing
sanitizeForLog or sanitizeArg helper) before calling console.warn/console.error
so both the message and ...args are sanitized (e.g., apply sanitizeForLog to
message and keep ...args.map(sanitizeArg)), ensuring all inputs logged by warn
and error are sanitized; refer to the warn and error functions and the
sanitizeArg/sanitizeForLog helpers to locate the change.
🪄 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: 8e133552-0dd6-4cea-84e4-a02c51450aa0
📒 Files selected for processing (22)
web/CLAUDE.mdweb/src/__tests__/lib/logger.test.tsweb/src/__tests__/pages/settings/code-editor-utils.test.tsweb/src/__tests__/pages/settings/editor-autocomplete.test.tsweb/src/__tests__/stores/meetings.test.tsweb/src/components/ui/error-boundary.tsxweb/src/lib/logger.tsweb/src/pages/settings/CodeEditorPanel.tsxweb/src/pages/settings/code-editor-utils.tsweb/src/pages/settings/editor-autocomplete.tsweb/src/pages/settings/editor-diff.tsweb/src/pages/settings/editor-linter.tsweb/src/stores/analytics.tsweb/src/stores/meetings.tsweb/src/stores/providers.tsweb/src/stores/providers/crud-actions.tsweb/src/stores/providers/list-actions.tsweb/src/stores/providers/local-model-actions.tsweb/src/stores/settings.tsweb/src/stores/setup-wizard.tsweb/src/stores/tasks.tsweb/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: Dashboard Test
- GitHub Check: Build Backend
- GitHub Check: Build Web
- GitHub Check: Build Sandbox
- GitHub Check: Dependency Review
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (8)
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/**/*.{ts,tsx}: Use Tailwind semantic classes (text-foreground,bg-card,text-accent, etc.) or CSS variables (var(--so-*)) for colors. NEVER hardcode hex values or rgba() in.tsx/.tsfiles
Usefont-sansorfont-monofor typography (maps to Geist tokens). NEVER setfontFamilydirectly in styles
Use density-aware spacing tokens (p-card,gap-section-gap,gap-grid-gap) or standard Tailwind spacing. NEVER hardcode pixel values for layout spacing
Use token variables (var(--so-shadow-card-hover),border-border,border-bright) for shadows and borders
Do NOT usergba()with hardcoded values -- use design token variables
Do NOT hardcode Framer Motion transition durations -- use@/lib/motionpresets
Use/// <reference types="vite/client" />to enable side-effect imports for CSS in Storybook 10 with TypeScript 6
Ensure all TypeScript code passes type checking vianpm --prefix web run type-check
Bundle size must be auditable vianpm --prefix web run analyze(generates stats.html treemap)
Achieve Lighthouse performance audit target of 90+ vianpm --prefix web run lighthouse
web/src/**/*.{ts,tsx}: ALWAYS reuse existing components fromweb/src/components/ui/before creating new ones
Web Dashboard Design System (MANDATORY): Seeweb/CLAUDE.mdfor the full component inventory, design token rules, and post-training references (TS6, Storybook 10).
Files:
web/src/utils/format.tsweb/src/stores/tasks.tsweb/src/stores/settings.tsweb/src/components/ui/error-boundary.tsxweb/src/__tests__/stores/meetings.test.tsweb/src/stores/analytics.tsweb/src/stores/meetings.tsweb/src/__tests__/pages/settings/editor-autocomplete.test.tsweb/src/__tests__/lib/logger.test.tsweb/src/stores/setup-wizard.tsweb/src/pages/settings/CodeEditorPanel.tsxweb/src/lib/logger.tsweb/src/pages/settings/editor-autocomplete.tsweb/src/stores/providers/list-actions.tsweb/src/stores/providers/local-model-actions.tsweb/src/pages/settings/editor-diff.tsweb/src/pages/settings/editor-linter.tsweb/src/pages/settings/code-editor-utils.tsweb/src/stores/providers.tsweb/src/stores/providers/crud-actions.tsweb/src/__tests__/pages/settings/code-editor-utils.test.ts
web/src/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
Run ESLint with zero warnings enforced via
npm --prefix web run lintLine length: 88 characters (ruff) for JavaScript/TypeScript files
Files:
web/src/utils/format.tsweb/src/stores/tasks.tsweb/src/stores/settings.tsweb/src/components/ui/error-boundary.tsxweb/src/__tests__/stores/meetings.test.tsweb/src/stores/analytics.tsweb/src/stores/meetings.tsweb/src/__tests__/pages/settings/editor-autocomplete.test.tsweb/src/__tests__/lib/logger.test.tsweb/src/stores/setup-wizard.tsweb/src/pages/settings/CodeEditorPanel.tsxweb/src/lib/logger.tsweb/src/pages/settings/editor-autocomplete.tsweb/src/stores/providers/list-actions.tsweb/src/stores/providers/local-model-actions.tsweb/src/pages/settings/editor-diff.tsweb/src/pages/settings/editor-linter.tsweb/src/pages/settings/code-editor-utils.tsweb/src/stores/providers.tsweb/src/stores/providers/crud-actions.tsweb/src/__tests__/pages/settings/code-editor-utils.test.ts
web/src/**/*.{ts,tsx,css}
📄 CodeRabbit inference engine (CLAUDE.md)
NEVER hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions — use design tokens and
@/lib/motionpresets
Files:
web/src/utils/format.tsweb/src/stores/tasks.tsweb/src/stores/settings.tsweb/src/components/ui/error-boundary.tsxweb/src/__tests__/stores/meetings.test.tsweb/src/stores/analytics.tsweb/src/stores/meetings.tsweb/src/__tests__/pages/settings/editor-autocomplete.test.tsweb/src/__tests__/lib/logger.test.tsweb/src/stores/setup-wizard.tsweb/src/pages/settings/CodeEditorPanel.tsxweb/src/lib/logger.tsweb/src/pages/settings/editor-autocomplete.tsweb/src/stores/providers/list-actions.tsweb/src/stores/providers/local-model-actions.tsweb/src/pages/settings/editor-diff.tsweb/src/pages/settings/editor-linter.tsweb/src/pages/settings/code-editor-utils.tsweb/src/stores/providers.tsweb/src/stores/providers/crud-actions.tsweb/src/__tests__/pages/settings/code-editor-utils.test.ts
web/src/stores/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
Use
web/src/stores/for Zustand store definitions (auth, WebSocket, toast, analytics, and per-domain stores)
Files:
web/src/stores/tasks.tsweb/src/stores/settings.tsweb/src/stores/analytics.tsweb/src/stores/meetings.tsweb/src/stores/setup-wizard.tsweb/src/stores/providers/list-actions.tsweb/src/stores/providers/local-model-actions.tsweb/src/stores/providers.tsweb/src/stores/providers/crud-actions.ts
web/src/components/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/components/**/*.{ts,tsx}: ALWAYS reuse existing components fromweb/src/components/ui/before creating new ones
Importcnfrom@/lib/utilsfor conditional class merging in components
Do NOT recreate status dots inline -- use<StatusBadge>component
Do NOT build card-with-header layouts from scratch -- use<SectionCard>component
Do NOT create metric displays withtext-metric font-bold-- use<MetricCard>component
Do NOT render initials circles manually -- use<Avatar>component
Do NOT create complex (>8 line) JSX inside.map()-- extract to a shared component
Files:
web/src/components/ui/error-boundary.tsx
web/src/components/ui/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/components/ui/**/*.{ts,tsx}: Place new shared components inweb/src/components/ui/with descriptive kebab-case filenames
Export props as a TypeScript interface for each component
Use design tokens exclusively in new components -- no hardcoded colors, fonts, or spacing
Files:
web/src/components/ui/error-boundary.tsx
web/src/__tests__/**/*.{test,spec}.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/__tests__/**/*.{test,spec}.{ts,tsx}: All unit tests must pass vianpm --prefix web run testwith coverage scoped to files changed vs origin/main
Mirror test directory structure to source code inweb/src/__tests__/(matchessrc/structure for Vitest unit + property tests)
Files:
web/src/__tests__/stores/meetings.test.tsweb/src/__tests__/pages/settings/editor-autocomplete.test.tsweb/src/__tests__/lib/logger.test.tsweb/src/__tests__/pages/settings/code-editor-utils.test.ts
web/src/pages/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/pages/**/*.{ts,tsx}: Organize page-scoped sub-components inpages/<page-name>/subdirectories rather than inline in page files
Use lazy-loaded page components inweb/src/pages/with React Router and route guards for auth/setup
Use@AnimatedPresencewrapper for page transitions in React Router (keyed by route)
Files:
web/src/pages/settings/CodeEditorPanel.tsxweb/src/pages/settings/editor-autocomplete.tsweb/src/pages/settings/editor-diff.tsweb/src/pages/settings/editor-linter.tsweb/src/pages/settings/code-editor-utils.ts
🧠 Learnings (44)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-03T21:18:15.006Z
Learning: Applies to web/src/**/*.{ts,tsx} : Web Dashboard Design System (MANDATORY): See `web/CLAUDE.md` for the full component inventory, design token rules, and post-training references (TS6, Storybook 10).
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use Zustand stores for state management in the web dashboard; each domain has its own store module (auth, WebSocket, toast, analytics, setup, company, agents, budget, tasks, settings, providers, theme, per-domain stores)
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/pages/**/*.{ts,tsx} : Organize page-scoped sub-components in `pages/<page-name>/` subdirectories rather than inline in page files
📚 Learning: 2026-04-03T18:36:51.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/stores/**/*.{ts,tsx} : Use `web/src/stores/` for Zustand store definitions (auth, WebSocket, toast, analytics, and per-domain stores)
Applied to files:
web/src/stores/tasks.tsweb/src/stores/analytics.tsweb/src/stores/meetings.tsweb/CLAUDE.mdweb/src/stores/providers/list-actions.tsweb/src/stores/providers.tsweb/src/stores/providers/crud-actions.ts
📚 Learning: 2026-03-30T10:41:40.176Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use Zustand stores for state management in the web dashboard; each domain has its own store module (auth, WebSocket, toast, analytics, setup, company, agents, budget, tasks, settings, providers, theme, per-domain stores)
Applied to files:
web/src/stores/tasks.tsweb/src/stores/meetings.tsweb/CLAUDE.mdweb/src/stores/providers/list-actions.tsweb/src/stores/providers.tsweb/src/stores/providers/crud-actions.ts
📚 Learning: 2026-03-27T12:44:29.466Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T12:44:29.466Z
Learning: Applies to web/src/stores/**/*.{ts,tsx} : Use Zustand stores in web dashboard for state management (auth, WebSocket, toast, analytics, domain shells)
Applied to files:
web/src/stores/tasks.tsweb/src/stores/meetings.tsweb/CLAUDE.mdweb/src/stores/providers/list-actions.tsweb/src/stores/providers.tsweb/src/stores/providers/crud-actions.ts
📚 Learning: 2026-04-03T18:36:51.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/__tests__/**/*.{test,spec}.{ts,tsx} : All unit tests must pass via `npm --prefix web run test` with coverage scoped to files changed vs origin/main
Applied to files:
web/src/__tests__/stores/meetings.test.tsweb/src/__tests__/pages/settings/editor-autocomplete.test.tsweb/src/__tests__/lib/logger.test.tsweb/src/__tests__/pages/settings/code-editor-utils.test.ts
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/__tests__/**/*.{test,spec}.{ts,tsx} : Vitest unit tests must use coverage scoped to files changed vs origin/main branch
Applied to files:
web/src/__tests__/stores/meetings.test.tsweb/src/__tests__/pages/settings/editor-autocomplete.test.tsweb/src/__tests__/lib/logger.test.tsweb/src/__tests__/pages/settings/code-editor-utils.test.ts
📚 Learning: 2026-03-30T10:20:08.544Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:20:08.544Z
Learning: Applies to web/**/*.test.{ts,tsx} : Web dashboard: Use React Hypothesis (fast-check) for property-based testing with fc.assert + fc.property
Applied to files:
web/src/__tests__/stores/meetings.test.tsweb/src/__tests__/pages/settings/editor-autocomplete.test.tsweb/src/__tests__/pages/settings/code-editor-utils.test.ts
📚 Learning: 2026-04-03T18:36:51.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/__tests__/**/*.{test,spec}.{ts,tsx} : Mirror test directory structure to source code in `web/src/__tests__/` (matches `src/` structure for Vitest unit + property tests)
Applied to files:
web/src/__tests__/pages/settings/editor-autocomplete.test.tsweb/src/__tests__/lib/logger.test.tsweb/src/__tests__/pages/settings/code-editor-utils.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 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__/pages/settings/editor-autocomplete.test.tsweb/src/__tests__/lib/logger.test.tsweb/src/__tests__/pages/settings/code-editor-utils.test.ts
📚 Learning: 2026-03-30T10:41:40.176Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Applies to web/src/__tests__/**/*.test.{ts,tsx} : Use property-based testing with fast-check in React tests (`fc.assert` + `fc.property`)
Applied to files:
web/src/__tests__/pages/settings/editor-autocomplete.test.tsweb/src/__tests__/pages/settings/code-editor-utils.test.ts
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/**/*.stories.tsx : Storybook 10: Use `parameters.a11y.test: 'error' | 'todo' | 'off'` for a11y testing configuration (replaces old `.element` and `.manual`)
Applied to files:
web/src/__tests__/pages/settings/editor-autocomplete.test.tsweb/src/__tests__/pages/settings/code-editor-utils.test.ts
📚 Learning: 2026-04-03T18:36:51.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/**/*.stories.tsx : Use `parameters.a11y.test: 'error' | 'todo' | 'off'` for a11y testing in Storybook 10 to enforce WCAG compliance (replaces old `.element` and `.manual`)
Applied to files:
web/src/__tests__/pages/settings/editor-autocomplete.test.ts
📚 Learning: 2026-03-30T10:41:40.176Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Applies to web/src/**/*.stories.tsx : Storybook 10: import from `storybook/test` (not `storybook/test`), `storybook/actions` (not `storybook/addon-actions`)
Applied to files:
web/src/__tests__/pages/settings/editor-autocomplete.test.tsweb/src/__tests__/pages/settings/code-editor-utils.test.ts
📚 Learning: 2026-04-03T18:36:51.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/**/*.stories.tsx : Import from `storybook/test` (not `storybook/test`) and `storybook/actions` (not `storybook/addon-actions`) in Storybook 10
Applied to files:
web/src/__tests__/pages/settings/editor-autocomplete.test.tsweb/src/__tests__/pages/settings/code-editor-utils.test.ts
📚 Learning: 2026-04-03T21:18:15.006Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-03T21:18:15.006Z
Learning: Applies to web/src/**/*.{ts,tsx} : Web Dashboard Design System (MANDATORY): See `web/CLAUDE.md` for the full component inventory, design token rules, and post-training references (TS6, Storybook 10).
Applied to files:
web/CLAUDE.md
📚 Learning: 2026-03-31T14:28:28.895Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:28:28.895Z
Learning: Web dashboard: see `web/CLAUDE.md` for commands, design system, and component inventory
Applied to files:
web/CLAUDE.md
📚 Learning: 2026-04-01T20:43:51.878Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T20:43:51.878Z
Learning: Applies to web/src/**/*.{ts,tsx} : Always reuse existing components from `web/src/components/ui/` before creating new ones. Never hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions -- use design tokens and `@/lib/motion` presets.
Applied to files:
web/CLAUDE.mdweb/src/pages/settings/CodeEditorPanel.tsx
📚 Learning: 2026-03-30T10:20:08.544Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:20:08.544Z
Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : When creating new shared web components, place in web/src/components/ui/ with kebab-case filename, create .stories.tsx alongside with all states (default, hover, loading, error, empty), export props as TypeScript interface, use design tokens exclusively with no hardcoded colors/fonts/spacing, and import cn from `@/lib/utils` for conditional class merging
Applied to files:
web/CLAUDE.md
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/**/*.{tsx,ts} : ALWAYS reuse existing components from `web/src/components/ui/` before creating new ones (StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup, Drawer, InputField, SelectField, SliderField, ToggleField, TaskStatusIndicator, PriorityBadge, ProviderHealthBadge, TokenUsageBar, CodeMirrorEditor, SegmentedControl, ThemeToggle, LiveRegion, MobileUnsupportedOverlay, LazyCodeMirrorEditor, TagInput, MetadataGrid, ProjectStatusBadge, ContentTypeBadge)
Applied to files:
web/CLAUDE.mdweb/src/pages/settings/CodeEditorPanel.tsx
📚 Learning: 2026-03-30T10:41:40.176Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Applies to web/src/**/*.{ts,tsx} : ALWAYS reuse existing components from `web/src/components/ui/` before creating new ones; refer to design system inventory (StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, etc.)
Applied to files:
web/CLAUDE.md
📚 Learning: 2026-03-30T10:20:08.544Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:20:08.544Z
Learning: Applies to web/src/**/*.{ts,tsx} : Always reuse existing components from web/src/components/ui/ (StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast/ToastContainer, Skeleton variants, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup/StaggerItem, Drawer, form fields, TaskStatusIndicator, PriorityBadge, ProviderHealthBadge, TokenUsageBar, CodeMirrorEditor, SegmentedControl, ThemeToggle, LiveRegion, MobileUnsupportedOverlay, LazyCodeMirrorEditor) before creating new components
Applied to files:
web/CLAUDE.mdweb/src/pages/settings/CodeEditorPanel.tsx
📚 Learning: 2026-03-27T22:32:26.927Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T22:32:26.927Z
Learning: Applies to web/src/components/ui/*.{tsx,ts} : For new shared React components: place in web/src/components/ui/ with kebab-case filename, create .stories.tsx with all states, export props as TypeScript interface, use design tokens exclusively
Applied to files:
web/CLAUDE.md
📚 Learning: 2026-04-03T21:18:15.006Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-03T21:18:15.006Z
Learning: Applies to web/src/**/*.{ts,tsx,css} : NEVER hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions — use design tokens and `@/lib/motion` presets
Applied to files:
web/CLAUDE.md
📚 Learning: 2026-03-31T14:31:11.894Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:31:11.894Z
Learning: Applies to web/src/**/*.{ts,tsx} : NEVER hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions — use design tokens and `@/lib/motion` presets
Applied to files:
web/CLAUDE.md
📚 Learning: 2026-04-03T21:18:15.006Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-03T21:18:15.006Z
Learning: Applies to web/src/**/*.{ts,tsx,js,jsx} : Line length: 88 characters (ruff) for JavaScript/TypeScript files
Applied to files:
web/src/pages/settings/CodeEditorPanel.tsxweb/src/pages/settings/editor-autocomplete.tsweb/src/pages/settings/code-editor-utils.ts
📚 Learning: 2026-04-03T18:36:51.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use `/// <reference types="vite/client" />` to enable side-effect imports for CSS in Storybook 10 with TypeScript 6
Applied to files:
web/src/pages/settings/CodeEditorPanel.tsx
📚 Learning: 2026-03-27T22:32:26.927Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T22:32:26.927Z
Learning: Applies to web/src/**/*.{tsx,ts} : Use token variables (var(--so-shadow-card-hover), border-border, border-bright) for shadows/borders; never hardcode values
Applied to files:
web/src/pages/settings/CodeEditorPanel.tsx
📚 Learning: 2026-03-31T14:31:11.894Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:31:11.894Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use React 19, TypeScript 6.0+, and design system tokens from shadcn/ui + Tailwind CSS 4 + Radix UI in web dashboard
Applied to files:
web/src/pages/settings/CodeEditorPanel.tsx
📚 Learning: 2026-04-03T18:36:51.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : Use design tokens exclusively in new components -- no hardcoded colors, fonts, or spacing
Applied to files:
web/src/pages/settings/CodeEditorPanel.tsx
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/components/ui/**/*.tsx : Use design tokens exclusively in new components -- no hardcoded colors, fonts, or spacing
Applied to files:
web/src/pages/settings/CodeEditorPanel.tsx
📚 Learning: 2026-04-03T18:36:51.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/components/**/*.{ts,tsx} : Import `cn` from `@/lib/utils` for conditional class merging in components
Applied to files:
web/src/pages/settings/CodeEditorPanel.tsx
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/**/*.{tsx,ts} : Use density-aware tokens (`p-card`, `gap-section-gap`, `gap-grid-gap`) or standard Tailwind spacing. NEVER hardcode pixel values for layout spacing
Applied to files:
web/src/pages/settings/CodeEditorPanel.tsx
📚 Learning: 2026-04-01T06:12:03.047Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T06:12:03.047Z
Learning: Applies to {**/*.py,web/src/**/*.{ts,tsx}} : Line length: 88 characters (enforced by ruff)
Applied to files:
web/src/pages/settings/editor-autocomplete.ts
📚 Learning: 2026-04-01T06:12:03.047Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T06:12:03.047Z
Learning: Applies to {**/*.py,web/src/**/*.{ts,tsx}} : Keep functions under 50 lines and files under 800 lines
Applied to files:
web/src/pages/settings/editor-autocomplete.ts
📚 Learning: 2026-03-28T09:24:37.044Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T09:24:37.044Z
Learning: Applies to **/*.{py,ts,tsx,go} : Functions should be under 50 lines; files should be under 800 lines
Applied to files:
web/src/pages/settings/editor-autocomplete.ts
📚 Learning: 2026-03-21T11:08:01.542Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T11:08:01.542Z
Learning: Applies to **/*.{py,md} : Line length: 88 characters (ruff).
Applied to files:
web/src/pages/settings/editor-autocomplete.ts
📚 Learning: 2026-04-03T18:36:51.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/components/**/*.{ts,tsx} : Do NOT create complex (>8 line) JSX inside `.map()` -- extract to a shared component
Applied to files:
web/src/pages/settings/editor-autocomplete.ts
📚 Learning: 2026-03-17T06:43:14.114Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:43:14.114Z
Learning: Applies to src/synthorg/**/*.py : 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.
Applied to files:
web/src/pages/settings/code-editor-utils.ts
📚 Learning: 2026-04-03T21:18:15.006Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-03T21:18:15.006Z
Learning: Pure data models, enums, and re-exports do NOT need logging
Applied to files:
web/src/pages/settings/code-editor-utils.ts
📚 Learning: 2026-04-01T14:22:06.315Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T14:22:06.315Z
Learning: Applies to {**/*.py,web/src/**/*.{ts,tsx}} : Validate at system boundaries (user input, external APIs, config files)
Applied to files:
web/src/pages/settings/code-editor-utils.ts
📚 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/settings/**/*.py : Settings package (settings/): runtime-editable settings persistence (DB > env > YAML > code defaults), typed definitions (9 namespaces), Fernet encryption for sensitive values, config bridge (JSON serialization for Pydantic/collections), ConfigResolver (typed accessors), validation, registry, change notifications via message bus, SettingsSubscriber protocol, SettingsChangeDispatcher (polls `#settings` channel, routes to subscribers, restart_required filtering)
Applied to files:
web/src/pages/settings/code-editor-utils.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:
web/src/stores/providers.ts
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/**/*.stories.tsx : Storybook 10: Import from `storybook/test` instead of `storybook/test`
Applied to files:
web/src/__tests__/pages/settings/code-editor-utils.test.ts
📚 Learning: 2026-04-02T08:47:46.313Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T08:47:46.313Z
Learning: Pre-commit hooks enforce: trailing-whitespace, end-of-file-fixer, check-yaml, check-toml, check-json, check-merge-conflict, check-added-large-files, no-commit-to-branch (main), ruff check+format, gitleaks, hadolint, golangci-lint + go vet (CLI, conditional), no-em-dashes, no-redundant-timeout, eslint-web (web dashboard, zero warnings, conditional)
Applied to files:
web/src/__tests__/pages/settings/code-editor-utils.test.ts
🪛 ast-grep (0.42.0)
web/src/pages/settings/editor-linter.ts
[warning] 59-59: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp("${escapeRegex(namespace)}"\\s*:)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html
(regexp-from-variable)
[warning] 70-70: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp("${escapeRegex(namespace)}"\\s*:\\s*\\{)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html
(regexp-from-variable)
[warning] 74-74: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp("${escapeRegex(key)}"\\s*:)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html
(regexp-from-variable)
[warning] 97-97: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(^${escapeRegex(namespace)}\\s*:, 'm')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html
(regexp-from-variable)
[warning] 107-107: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(^${escapeRegex(namespace)}\\s*:, 'm')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html
(regexp-from-variable)
[warning] 112-112: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(^(\\s+)["']?${escapeRegex(key)}["']?\\s*:, 'm')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html
(regexp-from-variable)
🪛 GitHub Check: CodeQL
web/src/lib/logger.ts
[warning] 39-39: Log injection
Log entry depends on a user-provided value.
🔇 Additional comments (26)
web/src/__tests__/pages/settings/editor-autocomplete.test.ts (2)
45-99: Tests only verify extension construction, not autocomplete behavior.All test cases assert only that the extension is truthy. A trivial implementation returning an empty extension would pass. Consider adding tests that invoke the completion source with mock
CompletionContextand verify the actual suggestions returned for namespace, key, and enum value positions.
13-43: Well-structured test helper.The
makeEntryhelper correctly constructs completeSettingEntryobjects matching theSettingDefinitioninterface, with sensible defaults and override capability. This provides good test data flexibility.web/src/pages/settings/editor-autocomplete.ts (4)
30-55: Schema building logic is sound.The function efficiently aggregates entries into a lookup-friendly structure. The non-null assertion on line 43 is safe due to the preceding
setcall on line 42.
269-281: Module-level cache relies on reference equality.The cache invalidates only when the
entriesarray reference changes. If entries are mutated in place (same array, modified contents), the stale schema will be used. This is typically safe with Zustand (which returns new arrays on state changes), but worth noting for future maintainers.
289-308: Extension factory is well-implemented.The factory correctly uses the cached schema via
getOrBuildSchema, addresses the previous keystroke-rebuild concern, and cleanly selects the appropriate completion source based on format.
139-147: Line length issue addressed.The
detailproperty now uses a multi-line ternary, resolving the previous 88-character limit violation.web/src/utils/format.ts (1)
82-82: Avoid interpolating currency code in the log message string.Line 82 still embeds dynamic
codeinside the message template; pass it as a separate arg so sanitizer handling stays consistent.Proposed fix
- log.error(`Intl.NumberFormat compact failed for currency "${code}":`, error) + log.error('Intl.NumberFormat compact failed for currency:', code, error)web/CLAUDE.md (1)
39-47: Logging guidance update is clear and actionable.The new rules are consistent with the structured-logger rollout and help prevent unsafe logging drift.
web/src/components/ui/error-boundary.tsx (1)
4-7: Structured logger migration in ErrorBoundary looks good.
componentDidCatchand reset-failure paths now consistently go through module-scoped logging.Also applies to: 47-55
web/src/stores/tasks.ts (1)
125-129: Malformed WS payload logging is safely structured.Line 125 now logs a static message and sanitizes
idbefore attaching context.web/src/__tests__/stores/meetings.test.ts (1)
237-281: Warn-spy updates correctly track the new logging level.These cases now validate the invalid-payload path through
console.warnas expected.web/src/stores/settings.ts (1)
70-80: Settings store logging migration is clean.The changed warn paths keep behavior intact and avoid embedding dynamic values directly in message strings.
Also applies to: 187-187, 212-212
web/src/pages/settings/CodeEditorPanel.tsx (1)
8-8: Nice extraction and tokenized layout update.The helper imports and
gap-grid-gapusage reduce panel complexity while keeping design-system spacing conventions.Also applies to: 16-24, 261-261
web/src/__tests__/lib/logger.test.ts (1)
46-51: Object pass-through test reflects intentional design, but security trade-off noted.This test accurately reflects the current
createLoggerimplementation which intentionally passes objects through unchanged for devtools inspection (perlogger.tslines 14-15). However, as flagged in previous reviews, this means nested string values within objects bypass sanitization.If objects containing user-controlled strings are logged, those strings won't be sanitized. Consider whether the devtools benefit outweighs the risk, or whether deep sanitization should be added to
sanitizeArg.web/src/lib/logger.ts (1)
11-19: Object pass-through leaves nested strings unsanitized.Plain objects are returned unchanged (line 15), meaning any string properties within those objects bypass
sanitizeForLog. If an object like{ userId: maliciousString }is logged, the malicious content reaches the console unsanitized.This is an intentional trade-off for devtools inspection (per the comment), but it does weaken the security posture. A recursive sanitization approach was suggested in prior reviews.
web/src/stores/meetings.ts (2)
76-82: LGTM! Error logging now properly sanitized.The stale-request error handling correctly uses
getErrorMessage(err)to extract a safe string before logging, addressing the previous review concern about raw error object pass-through.
142-147: Consistent sanitization pattern for WS payload logging.The
sanitizeForLog(candidate.meeting_id)usage matches the parallel pattern intasks.ts(context snippet 1, lines 125-129), maintaining consistency across stores for logging potentially attacker-controlled IDs.web/src/stores/setup-wizard.ts (1)
41-63: LGTM! Consistent error sanitization in probe helper.The
runProbeAllhelper now properly usesgetErrorMessage(entry.reason)for failed probes, and all otherlog.errorcalls throughout the file follow the same pattern. This addresses the previous review concern about inconsistent raw object logging.web/src/stores/providers.ts (1)
39-43: Clean store decomposition with action delegation.The refactored store properly separates concerns by delegating actions to focused modules while maintaining state defaults in the main file. The spread order is correct since context snippets confirm no action name collisions between the factories:
list-actions:fetchProviders,setSearchQuery,setHealthFilter,setSortBy,setSortDirectiondetail-actions:fetchProviderDetail,clearDetailcrud-actions:fetchPresets,createProvider,createFromPreset,updateProvider,deleteProvider,testConnection,discoverModels,clearTestResultlocal-model-actions:pullModel,cancelPull,deleteModel,updateModelConfigweb/src/stores/providers/list-actions.ts (1)
18-48: Stale request guard removed per previous review.The
mutatingguard that previously blocked post-CRUD refreshes has been removed.fetchProvidersnow correctly allows refresh calls from CRUD actions, addressing the critical issue flagged in prior reviews.web/src/__tests__/pages/settings/code-editor-utils.test.ts (2)
1-35: LGTM! Well-structured test helper and comprehensive coverage.The
makeEntryhelper factory reduces boilerplate effectively, and the test suite provides good coverage of the extracted utilities including security-relevant edge cases like YAML tag injection prevention.
184-188: Good security test for YAML tag injection.Testing that
CORE_SCHEMArejects dangerous tags like!!js/functionvalidates an important security boundary. This ensures user-provided YAML configuration cannot execute arbitrary code.web/src/stores/providers/local-model-actions.ts (2)
7-12: LGTM! Structured logger added per previous review.The module now imports
createLoggerand useslog.errorwithgetErrorMessage(err)consistently across all catch blocks, addressing the prior review concern about missing structured logging.
104-126: LGTM!updateModelConfignow refreshes providers list.The
fetchProviders()call at line 111 was added to match the pattern indeleteModelandpullModel, addressing the prior review concern about stale provider list state after config updates.web/src/pages/settings/code-editor-utils.ts (1)
29-60: Nice hardening of the editor boundary helpers.Using null-prototype objects, explicit format branches, the byte cap, and namespace-object validation makes the JSON/YAML path much safer without changing the caller API.
Also applies to: 117-145
web/src/pages/settings/editor-linter.ts (1)
177-216: 🛠️ Refactor suggestion | 🟠 MajorMove the lint theme off inline
fontFamilyand raw px spacing.This theme hardcodes
fontFamily,padding,borderLeft, andmaxHeightvalues. The settings pages are supposed to consume dashboard typography/spacing tokens instead of inline font families and pixel sizing.As per coding guidelines "Use font-sans or font-mono for typography (maps to Geist tokens). NEVER set fontFamily directly in styles" and "Use density-aware spacing tokens (
p-card,gap-section-gap,gap-grid-gap) or standard Tailwind spacing. NEVER hardcode pixel values for layout spacing".⛔ Skipped due to learnings
Learnt from: CR Repo: Aureliolo/synthorg PR: 0 File: web/CLAUDE.md:0-0 Timestamp: 2026-04-02T12:21:16.739Z Learning: Applies to web/src/**/*.{tsx,ts} : Use density-aware tokens (`p-card`, `gap-section-gap`, `gap-grid-gap`) or standard Tailwind spacing. NEVER hardcode pixel values for layout spacingLearnt from: CR Repo: Aureliolo/synthorg PR: 0 File: CLAUDE.md:0-0 Timestamp: 2026-03-27T22:32:26.927Z Learning: Applies to web/src/**/*.{tsx,ts} : Use density-aware tokens (p-card, gap-section-gap, gap-grid-gap) or standard Tailwind spacing; never hardcode pixel values for layout spacingLearnt from: CR Repo: Aureliolo/synthorg PR: 0 File: web/CLAUDE.md:0-0 Timestamp: 2026-04-03T18:36:51.730Z Learning: Applies to web/src/**/*.{ts,tsx} : Use `font-sans` or `font-mono` for typography (maps to Geist tokens). NEVER set `fontFamily` directly in stylesLearnt from: CR Repo: Aureliolo/synthorg PR: 0 File: web/CLAUDE.md:0-0 Timestamp: 2026-04-03T18:36:51.730Z Learning: Applies to web/src/**/*.{ts,tsx} : Use density-aware spacing tokens (`p-card`, `gap-section-gap`, `gap-grid-gap`) or standard Tailwind spacing. NEVER hardcode pixel values for layout spacingLearnt from: CR Repo: Aureliolo/synthorg PR: 0 File: CLAUDE.md:0-0 Timestamp: 2026-03-31T14:31:11.894Z Learning: Applies to web/src/**/*.{ts,tsx} : NEVER hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions — use design tokens and `@/lib/motion` presetsLearnt from: CR Repo: Aureliolo/synthorg PR: 0 File: CLAUDE.md:0-0 Timestamp: 2026-04-02T12:21:16.739Z Learning: Applies to web/src/**/*.{ts,tsx,css} : Never hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions in web code — use design tokens and `@/lib/motion` presetsLearnt from: CR Repo: Aureliolo/synthorg PR: 0 File: CLAUDE.md:0-0 Timestamp: 2026-04-03T21:18:15.006Z Learning: Applies to web/src/**/*.{ts,tsx,css} : NEVER hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions — use design tokens and `@/lib/motion` presetsLearnt from: CR Repo: Aureliolo/synthorg PR: 0 File: web/CLAUDE.md:0-0 Timestamp: 2026-04-02T12:21:16.739Z Learning: Applies to web/src/components/ui/**/*.tsx : Use design tokens exclusively in new components -- no hardcoded colors, fonts, or spacingLearnt from: CR Repo: Aureliolo/synthorg PR: 0 File: web/CLAUDE.md:0-0 Timestamp: 2026-04-03T18:36:51.730Z Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : Use design tokens exclusively in new components -- no hardcoded colors, fonts, or spacingLearnt from: CR Repo: Aureliolo/synthorg PR: 0 File: CLAUDE.md:0-0 Timestamp: 2026-04-01T20:43:51.878Z Learning: Applies to web/src/**/*.{ts,tsx} : Always reuse existing components from `web/src/components/ui/` before creating new ones. Never hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions -- use design tokens and `@/lib/motion` presets.
| it('emits debug only in DEV mode', () => { | ||
| const debugSpy = vi.spyOn(console, 'debug').mockImplementation(() => {}) | ||
| const log = createLogger('m') | ||
| log.debug('test') | ||
| // In test env, import.meta.env.DEV is true | ||
| expect(debugSpy).toHaveBeenCalledWith('[m]', 'test') | ||
| debugSpy.mockRestore() | ||
| }) |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
DEV mode test relies on environment assumption.
The test comment states "In test env, import.meta.env.DEV is true" but this depends on Vitest configuration. If the test environment changes, this test could fail unexpectedly.
Consider making the DEV check explicit
it('emits debug only in DEV mode', () => {
const debugSpy = vi.spyOn(console, 'debug').mockImplementation(() => {})
const log = createLogger('m')
log.debug('test')
- // In test env, import.meta.env.DEV is true
- expect(debugSpy).toHaveBeenCalledWith('[m]', 'test')
+ if (import.meta.env.DEV) {
+ expect(debugSpy).toHaveBeenCalledWith('[m]', 'test')
+ } else {
+ expect(debugSpy).not.toHaveBeenCalled()
+ }
debugSpy.mockRestore()
})📝 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.
| it('emits debug only in DEV mode', () => { | |
| const debugSpy = vi.spyOn(console, 'debug').mockImplementation(() => {}) | |
| const log = createLogger('m') | |
| log.debug('test') | |
| // In test env, import.meta.env.DEV is true | |
| expect(debugSpy).toHaveBeenCalledWith('[m]', 'test') | |
| debugSpy.mockRestore() | |
| }) | |
| it('emits debug only in DEV mode', () => { | |
| const debugSpy = vi.spyOn(console, 'debug').mockImplementation(() => {}) | |
| const log = createLogger('m') | |
| log.debug('test') | |
| if (import.meta.env.DEV) { | |
| expect(debugSpy).toHaveBeenCalledWith('[m]', 'test') | |
| } else { | |
| expect(debugSpy).not.toHaveBeenCalled() | |
| } | |
| debugSpy.mockRestore() | |
| }) |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/__tests__/lib/logger.test.ts` around lines 77 - 84, The test
currently assumes import.meta.env.DEV is true; make that explicit by stubbing
the DEV flag before creating the logger: in the test, define import.meta.env
with DEV true using a configurable Object.defineProperty on import.meta (or set
a temporary global stub) prior to calling createLogger('m'), then restore or
delete the stub after the assertion; reference createLogger and the existing
debug spy (vi.spyOn(console, 'debug')) so the test no longer relies on the
environment default.
| function jsonCompletionSource( | ||
| schema: CompletionSchemaInfo, | ||
| ): (ctx: CompletionContext) => CompletionResult | null { | ||
| return (ctx: CompletionContext) => { | ||
| const text = ctx.state.doc.toString() | ||
| const pos = ctx.pos | ||
|
|
||
| // Get the text before cursor for context analysis | ||
| const before = text.slice(0, pos) | ||
|
|
||
| // Check if we're typing a key (after { or , and before :) | ||
| // Determine nesting depth to know if we're at namespace or key level | ||
| let braceDepth = 0 | ||
| let currentNamespace: string | null = null | ||
|
|
||
| // Walk backwards to determine context. | ||
| // braceDepth counts unmatched '{' seen while scanning backward. | ||
| // The first unmatched '{' is the innermost enclosing object. | ||
| for (let i = pos - 1; i >= 0; i--) { | ||
| const ch = text[i] | ||
| if (ch === '{') { | ||
| braceDepth++ | ||
| if (braceDepth === 1) { | ||
| // First enclosing '{' -- check if it belongs to a namespace | ||
| const preceding = text.slice(0, i).trimEnd() | ||
| const nsMatch = /"(\w+)"\s*:\s*$/.exec(preceding) | ||
| if (nsMatch) { | ||
| // We're inside a namespace object (e.g. "api": { | }) | ||
| currentNamespace = nsMatch[1] ?? null | ||
| } | ||
| // If no nsMatch, we're at the root object level | ||
| break | ||
| } | ||
| } else if (ch === '}') { | ||
| braceDepth-- | ||
| } | ||
| } | ||
|
|
||
| // Check if we're in a string value position (after "key": ) | ||
| // Look for pattern: "someKey": "| (cursor in a value string) | ||
| const valueMatch = /"(\w+)"\s*:\s*"([^"]*?)$/.exec(before) | ||
| if (valueMatch && currentNamespace) { | ||
| const settingKey = valueMatch[1] ?? '' | ||
| const partial = valueMatch[2] ?? '' | ||
| const keyInfo = schema.keys.get(currentNamespace) | ||
| const setting = keyInfo?.find((k) => k.key === settingKey) | ||
| if (setting && setting.enumValues.length > 0) { | ||
| const from = pos - partial.length | ||
| return { | ||
| from, | ||
| options: setting.enumValues.map((val) => ({ | ||
| label: val, | ||
| type: 'enum', | ||
| detail: `${currentNamespace}/${settingKey}`, | ||
| })), | ||
| } | ||
| } | ||
| return null | ||
| } | ||
|
|
||
| // Check if we're typing a key name (inside quotes at key position) | ||
| // Pattern: after { or , or newline, possibly whitespace, then "partial | ||
| const keyMatch = /(?:^|[{,])\s*"(\w*)$/.exec(before) | ||
| if (!keyMatch) return null | ||
|
|
||
| const partial = keyMatch[1] ?? '' | ||
| const from = pos - partial.length | ||
|
|
||
| if (currentNamespace) { | ||
| // Inside a namespace -- suggest setting keys | ||
| const keyInfo = schema.keys.get(currentNamespace) | ||
| if (!keyInfo) return null | ||
| return { | ||
| from, | ||
| options: keyInfo.map((k) => ({ | ||
| label: k.key, | ||
| type: 'property', | ||
| detail: k.enumValues.length > 0 | ||
| ? `${k.type} (${k.enumValues.join(' | ')})` | ||
| : k.type, | ||
| info: k.description, | ||
| })), | ||
| } | ||
| } | ||
|
|
||
| // At root level -- suggest namespaces | ||
| return { | ||
| from, | ||
| options: schema.namespaces.map((ns) => ({ | ||
| label: ns, | ||
| type: 'keyword', | ||
| detail: 'namespace', | ||
| info: `Settings namespace: ${ns}`, | ||
| })), | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Function exceeds 50-line limit; consider decomposition.
jsonCompletionSource spans ~93 lines. Per coding guidelines, functions should be under 50 lines. The logic has clear phases (context detection, enum completion, key completion, namespace completion) that could be extracted into focused helpers.
♻️ Suggested decomposition
+interface JsonContext {
+ currentNamespace: string | null
+}
+
+function findJsonContext(text: string, pos: number): JsonContext {
+ let braceDepth = 0
+ let currentNamespace: string | null = null
+ for (let i = pos - 1; i >= 0; i--) {
+ const ch = text[i]
+ if (ch === '{') {
+ braceDepth++
+ if (braceDepth === 1) {
+ const preceding = text.slice(0, i).trimEnd()
+ const nsMatch = /"(\w+)"\s*:\s*$/.exec(preceding)
+ if (nsMatch) currentNamespace = nsMatch[1] ?? null
+ break
+ }
+ } else if (ch === '}') {
+ braceDepth--
+ }
+ }
+ return { currentNamespace }
+}
+
function jsonCompletionSource(
schema: CompletionSchemaInfo,
): (ctx: CompletionContext) => CompletionResult | null {
return (ctx: CompletionContext) => {
const text = ctx.state.doc.toString()
const pos = ctx.pos
const before = text.slice(0, pos)
- // ... inline context detection ...
+ const { currentNamespace } = findJsonContext(text, pos)
+ // ... rest of completion logicAs per coding guidelines: "Functions should be under 50 lines and files should be under 800 lines".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/pages/settings/editor-autocomplete.ts` around lines 65 - 161,
jsonCompletionSource is too long (~93 lines); split it into small helpers to
keep the top-level function under 50 lines while retaining identical behavior
and return shapes. Extract the backward-scan/namespace detection into a helper
(e.g., detectCurrentNamespace(text: string, pos: number): string | null),
extract the value-position regex logic into findValueMatch(before: string):
{key: string, partial: string} | null, extract the key-position regex logic into
findKeyMatch(before: string): {partial: string} | null, and extract the
option-builders into buildEnumOptions(setting, currentNamespace, pos, partial),
buildKeyOptions(keyInfo, pos, partial) and buildNamespaceOptions(schema, pos).
Have jsonCompletionSource call these helpers in the same order (context
detection → enum completion → key completion → namespace completion) and return
the same CompletionResult objects so behavior/tests remain unchanged.
| function yamlCompletionSource( | ||
| schema: CompletionSchemaInfo, | ||
| ): (ctx: CompletionContext) => CompletionResult | null { | ||
| return (ctx: CompletionContext) => { | ||
| const text = ctx.state.doc.toString() | ||
| const pos = ctx.pos | ||
|
|
||
| // Get the current line and text before cursor | ||
| const lineObj = ctx.state.doc.lineAt(pos) | ||
| const lineText = lineObj.text | ||
| const colPos = pos - lineObj.from | ||
| const beforeOnLine = lineText.slice(0, colPos) | ||
|
|
||
| // Determine indentation level | ||
| const indentMatch = /^(\s*)/.exec(lineText) | ||
| const indent = indentMatch?.[1]?.length ?? 0 | ||
|
|
||
| // Check if we're typing a value after "key: " for enum autocomplete | ||
| const valueMatch = /^\s+(\w[\w_]*)\s*:\s*(\S*)$/.exec(beforeOnLine) | ||
| if (valueMatch && indent > 0) { | ||
| const settingKey = valueMatch[1] ?? '' | ||
| const partial = valueMatch[2] ?? '' | ||
|
|
||
| // Find the namespace by looking at the previous unindented key | ||
| const linesAbove = text.slice(0, lineObj.from).split('\n') | ||
| let ns: string | null = null | ||
| for (let i = linesAbove.length - 1; i >= 0; i--) { | ||
| const nsMatch = /^(\w[\w_]*)\s*:/.exec(linesAbove[i] ?? '') | ||
| if (nsMatch) { | ||
| ns = nsMatch[1] ?? null | ||
| break | ||
| } | ||
| } | ||
|
|
||
| if (ns) { | ||
| const keyInfo = schema.keys.get(ns) | ||
| const setting = keyInfo?.find((k) => k.key === settingKey) | ||
| if (setting && setting.enumValues.length > 0) { | ||
| const from = pos - partial.length | ||
| return { | ||
| from, | ||
| options: setting.enumValues.map((val) => ({ | ||
| label: val, | ||
| type: 'enum', | ||
| detail: `${ns}/${settingKey}`, | ||
| })), | ||
| } | ||
| } | ||
| } | ||
| return null | ||
| } | ||
|
|
||
| // Check if we're typing a key | ||
| const keyTyping = beforeOnLine.trimStart() | ||
| // Only complete if we haven't typed a colon yet | ||
| if (keyTyping.includes(':')) return null | ||
|
|
||
| const partial = keyTyping | ||
| const from = pos - partial.length | ||
|
|
||
| if (indent > 0) { | ||
| // Indented -- inside a namespace, suggest setting keys | ||
| const linesAbove = text.slice(0, lineObj.from).split('\n') | ||
| let ns: string | null = null | ||
| for (let i = linesAbove.length - 1; i >= 0; i--) { | ||
| const nsMatch = /^(\w[\w_]*)\s*:/.exec(linesAbove[i] ?? '') | ||
| if (nsMatch) { | ||
| ns = nsMatch[1] ?? null | ||
| break | ||
| } | ||
| } | ||
| if (!ns) return null | ||
| const keyInfo = schema.keys.get(ns) | ||
| if (!keyInfo) return null | ||
| return { | ||
| from, | ||
| options: keyInfo.map((k) => ({ | ||
| label: k.key, | ||
| type: 'property', | ||
| detail: k.enumValues.length > 0 | ||
| ? `${k.type} (${k.enumValues.join(' | ')})` | ||
| : k.type, | ||
| info: k.description, | ||
| apply: `${k.key}: `, | ||
| })), | ||
| } | ||
| } | ||
|
|
||
| // Top level -- suggest namespaces | ||
| return { | ||
| from, | ||
| options: schema.namespaces.map((ns) => ({ | ||
| label: ns, | ||
| type: 'keyword', | ||
| detail: 'namespace', | ||
| info: `Settings namespace: ${ns}`, | ||
| apply: `${ns}:\n `, | ||
| })), | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Function exceeds 50-line limit; duplicate namespace lookup logic.
yamlCompletionSource is ~97 lines and duplicates the namespace-finding loop (lines 189-197 and 227-235). Extract a findYamlNamespace(text: string, lineFrom: number): string | null helper to reduce both length and duplication.
♻️ Extract namespace lookup
+function findYamlNamespace(text: string, lineFrom: number): string | null {
+ const linesAbove = text.slice(0, lineFrom).split('\n')
+ for (let i = linesAbove.length - 1; i >= 0; i--) {
+ const nsMatch = /^(\w[\w_]*)\s*:/.exec(linesAbove[i] ?? '')
+ if (nsMatch) return nsMatch[1] ?? null
+ }
+ return null
+}
+
function yamlCompletionSource(
// ...
- const linesAbove = text.slice(0, lineObj.from).split('\n')
- let ns: string | null = null
- for (let i = linesAbove.length - 1; i >= 0; i--) {
- const nsMatch = /^(\w[\w_]*)\s*:/.exec(linesAbove[i] ?? '')
- if (nsMatch) {
- ns = nsMatch[1] ?? null
- break
- }
- }
+ const ns = findYamlNamespace(text, lineObj.from)As per coding guidelines: "Functions should be under 50 lines".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/pages/settings/editor-autocomplete.ts` around lines 165 - 265, The
yamlCompletionSource function is too long and duplicates the namespace lookup
loop; extract a new helper function findYamlNamespace(text: string, lineFrom:
number): string | null that encapsulates the logic which scans lines above a
given position for an unindented namespace key, then replace both duplicated
loops inside yamlCompletionSource with calls to findYamlNamespace (use the
existing variables text and lineObj.from when calling), and keep all existing
behavior (return null when not found).
| export function computeLineDiff( | ||
| serverText: string, | ||
| editedText: string, | ||
| ): LineDiff[] { | ||
| if (serverText === editedText) return [] | ||
| const serverLines = serverText.replace(/\r\n/g, '\n').split('\n') | ||
| const editedLines = editedText.replace(/\r\n/g, '\n').split('\n') | ||
| const diffs: LineDiff[] = [] | ||
|
|
||
| // LCS-based diff: find longest common subsequence to identify | ||
| // true additions, removals, and changes (handles insertions/deletions | ||
| // at any position without cascading false "changed" markers). | ||
| const n = serverLines.length | ||
| const m = editedLines.length | ||
| const dp: number[][] = Array.from({ length: n + 1 }, () => new Array<number>(m + 1).fill(0)) | ||
| for (let i = 1; i <= n; i++) { | ||
| for (let j = 1; j <= m; j++) { | ||
| dp[i]![j] = serverLines[i - 1] === editedLines[j - 1] | ||
| ? dp[i - 1]![j - 1]! + 1 | ||
| : Math.max(dp[i - 1]![j]!, dp[i]![j - 1]!) | ||
| } | ||
| } |
There was a problem hiding this comment.
Bound the LCS hot path before it runs on each editor change.
web/src/pages/settings/CodeEditorPanel.tsx Lines 202-207 call dispatchDiff() on every text update. Building the full (n + 1) × (m + 1) matrix here turns a 2k-line document into ~4M cells per keypress, and the current fast path misses CRLF/LF-equivalent inputs because it runs before normalization. Normalize first, then short-circuit and fall back to a cheaper heuristic once the matrix exceeds a safe threshold.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/pages/settings/editor-diff.ts` around lines 39 - 60, computeLineDiff
is building a full LCS matrix on every editor change and is invoked from
dispatchDiff in CodeEditorPanel; to fix, normalize inputs before any equality
checks (apply the CRLF -> LF replace on both serverText and editedText at the
top) and then early-return when texts are equal; next, add a safe matrix-size
guard in computeLineDiff (e.g., if ( (n+1)*(m+1) > SOME_THRESHOLD ) ) to skip
the O(n*m) LCS and instead fall back to a cheaper heuristic diff routine or
line-by-line comparison; update dispatchDiff/CodeEditorPanel to use the
normalized strings and ensure it calls the cheap fallback when the matrix-size
guard trips so we avoid creating huge dp arrays on large documents.
| // Unmatched edited lines are additions | ||
| for (let j = 0; j < m; j++) { | ||
| if (!editedMatched.has(j)) { | ||
| diffs.push({ line: j + 1, kind: 'added' }) | ||
| } | ||
| } | ||
| // Unmatched server lines are removals (shown at nearest edited position) | ||
| for (let i = 0; i < n; i++) { | ||
| if (!serverMatched.has(i)) { | ||
| diffs.push({ line: Math.max(1, Math.min(i + 1, m)), kind: 'removed' }) | ||
| } | ||
| } | ||
|
|
||
| return diffs.sort((a, b) => a.line - b.line) |
There was a problem hiding this comment.
Emit changed for replacements before the gutter dedupes them away.
A pure line replacement currently pushes both added and removed for the same line. Lines 192-200 later keep only one marker per line, so changed is never surfaced and modified lines are mislabeled.
♻️ Possible fix
// Unmatched server lines are removals (shown at nearest edited position)
for (let i = 0; i < n; i++) {
if (!serverMatched.has(i)) {
diffs.push({ line: Math.max(1, Math.min(i + 1, m)), kind: 'removed' })
}
}
- return diffs.sort((a, b) => a.line - b.line)
+ const collapsed = new Map<number, LineDiffKind>()
+ for (const diff of diffs) {
+ const existing = collapsed.get(diff.line)
+ collapsed.set(
+ diff.line,
+ existing && existing !== diff.kind ? 'changed' : diff.kind,
+ )
+ }
+ return Array.from(collapsed, ([line, kind]) => ({ line, kind }))
+ .sort((a, b) => a.line - b.line)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/pages/settings/editor-diff.ts` around lines 80 - 93, The code
currently pushes separate 'added' and 'removed' markers for pure replacements
and later a gutter dedupe hides them, so change detection never surfaces; modify
the two unmatched-loops that use editedMatched, serverMatched, diffs, m, n and
the Math.max(1, Math.min(i + 1, m)) mapping so that when an unmatched server
index i and an unmatched edited index j correspond to the same target line you
emit a single { line: L, kind: 'changed' } and mark both indexes as handled
(remove from editedMatched/serverMatched or track handled), otherwise fall back
to pushing 'added' or 'removed' as before; this ensures replacements are emitted
as 'changed' before the later gutter dedupe.
| '.cm-diff-gutter': { | ||
| width: '6px', | ||
| marginRight: '2px', | ||
| }, |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Use a tokenized spacing value for the gutter gap.
marginRight: '2px' hardcodes layout spacing in a web/src TypeScript file. Please route that through a dashboard spacing token/CSS variable so density changes stay centralized.
As per coding guidelines, web/src/**/*.{ts,tsx,css}: NEVER hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions — use design tokens and @/lib/motion presets.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/pages/settings/editor-diff.ts` around lines 153 - 156, Replace the
hardcoded marginRight: '2px' in the '.cm-diff-gutter' style with the centralized
spacing token or CSS variable used across the dashboard (e.g., the spacing token
export or a --space-XX CSS var) so spacing is not hardcoded; update the style in
web/src/pages/settings/editor-diff.ts by importing/using the appropriate token
name and assigning that token/CSS variable to marginRight instead of the literal
'2px' (target the '.cm-diff-gutter' key and its marginRight property).
| // Phase 1: Syntax validation | ||
| let parsed: Record<string, Record<string, unknown>> | ||
| try { | ||
| const raw: unknown = format === 'json' | ||
| ? JSON.parse(text) | ||
| : YAML.load(text, { schema: YAML.CORE_SCHEMA }) | ||
|
|
||
| if (!raw || typeof raw !== 'object' || Array.isArray(raw)) { | ||
| diagnostics.push({ | ||
| from: 0, | ||
| to: Math.min(text.length, 50), | ||
| severity: 'error', | ||
| message: `${format.toUpperCase()} must be an object at the top level`, | ||
| }) | ||
| return diagnostics | ||
| } | ||
|
|
||
| parsed = raw as Record<string, Record<string, unknown>> |
There was a problem hiding this comment.
Keep lint-time validation in sync with the save parser.
After syntax parsing succeeds, this path only rejects non-object top-level values. parseText in web/src/pages/settings/code-editor-utils.ts:117-145 also enforces the 64 KiB limit and requires every namespace value to be an object, so inputs like foo: [] or oversized documents can lint clean here and then fail only on save. Reuse the same structural checks before running schema validation.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/pages/settings/editor-linter.ts` around lines 240 - 257, The linter
currently only rejects non-object top-level values but must mirror parseText’s
structural checks: before schema validation, enforce the 64 KiB size limit on
text (<= 65536 bytes) and validate that the parsed top-level is an object whose
values (namespaces) are each objects (not arrays or primitives); if any check
fails, push the same diagnostics into diagnostics and return early. Update the
block that assigns parsed (and uses format/text/diagnostics) to perform these
checks the same way parseText does so inputs like foo: [] or oversized documents
fail during linting as well as on save.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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/layout/StatusBar.test.tsx`:
- Around line 36-39: The mocks in this test use getOverviewMetrics and
getForecast with mockResolvedValue(null) which violates the non-nullable
contracts; update these mocks to return contract-accurate resolved values (i.e.,
a valid OverviewMetrics object for getOverviewMetrics and a valid
ForecastResponse object for getForecast) so Promise.allSettled treats them as
real success paths—implement or reuse small factory functions (e.g.,
getMockOverviewMetrics()/getMockForecastResponse()) or inline minimal shape
objects matching the types and replace mockResolvedValue(null) with
mockResolvedValue(<contract-accurate object>).
🪄 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: 9754fc1b-7994-445d-ac57-cea149d338eb
📒 Files selected for processing (1)
web/src/__tests__/components/layout/StatusBar.test.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: 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 (web/CLAUDE.md)
web/src/**/*.{ts,tsx}: Use Tailwind semantic classes (text-foreground,bg-card,text-accent, etc.) or CSS variables (var(--so-*)) for colors. NEVER hardcode hex values or rgba() in.tsx/.tsfiles
Usefont-sansorfont-monofor typography (maps to Geist tokens). NEVER setfontFamilydirectly in styles
Use density-aware spacing tokens (p-card,gap-section-gap,gap-grid-gap) or standard Tailwind spacing. NEVER hardcode pixel values for layout spacing
Use token variables (var(--so-shadow-card-hover),border-border,border-bright) for shadows and borders
Do NOT usergba()with hardcoded values -- use design token variables
Do NOT hardcode Framer Motion transition durations -- use@/lib/motionpresets
Use/// <reference types="vite/client" />to enable side-effect imports for CSS in Storybook 10 with TypeScript 6
Ensure all TypeScript code passes type checking vianpm --prefix web run type-check
Bundle size must be auditable vianpm --prefix web run analyze(generates stats.html treemap)
Achieve Lighthouse performance audit target of 90+ vianpm --prefix web run lighthouse
web/src/**/*.{ts,tsx}: ALWAYS reuse existing components fromweb/src/components/ui/before creating new ones
Web Dashboard Design System (MANDATORY): Seeweb/CLAUDE.mdfor the full component inventory, design token rules, and post-training references (TS6, Storybook 10).
Files:
web/src/__tests__/components/layout/StatusBar.test.tsx
web/src/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
Run ESLint with zero warnings enforced via
npm --prefix web run lintLine length: 88 characters (ruff) for JavaScript/TypeScript files
Files:
web/src/__tests__/components/layout/StatusBar.test.tsx
web/src/__tests__/**/*.{test,spec}.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/__tests__/**/*.{test,spec}.{ts,tsx}: All unit tests must pass vianpm --prefix web run testwith coverage scoped to files changed vs origin/main
Mirror test directory structure to source code inweb/src/__tests__/(matchessrc/structure for Vitest unit + property tests)
Files:
web/src/__tests__/components/layout/StatusBar.test.tsx
web/src/**/*.{ts,tsx,css}
📄 CodeRabbit inference engine (CLAUDE.md)
NEVER hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions — use design tokens and
@/lib/motionpresets
Files:
web/src/__tests__/components/layout/StatusBar.test.tsx
🧠 Learnings (10)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-03T21:18:15.006Z
Learning: Applies to web/src/**/*.{ts,tsx} : Web Dashboard Design System (MANDATORY): See `web/CLAUDE.md` for the full component inventory, design token rules, and post-training references (TS6, Storybook 10).
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use Zustand stores for state management in the web dashboard; each domain has its own store module (auth, WebSocket, toast, analytics, setup, company, agents, budget, tasks, settings, providers, theme, per-domain stores)
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/**/*.{ts,tsx} : Achieve Lighthouse performance audit target of 90+ via `npm --prefix web run lighthouse`
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/pages/**/*.{ts,tsx} : Organize page-scoped sub-components in `pages/<page-name>/` subdirectories rather than inline in page files
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T12:44:29.466Z
Learning: Applies to web/src/stores/**/*.{ts,tsx} : Use Zustand stores in web dashboard for state management (auth, WebSocket, toast, analytics, domain shells)
📚 Learning: 2026-04-03T18:36:51.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/**/*.stories.tsx : Import from `storybook/test` (not `storybook/test`) and `storybook/actions` (not `storybook/addon-actions`) in Storybook 10
Applied to files:
web/src/__tests__/components/layout/StatusBar.test.tsx
📚 Learning: 2026-03-30T10:41:40.176Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Applies to web/src/**/*.stories.tsx : Storybook 10: import from `storybook/test` (not `storybook/test`), `storybook/actions` (not `storybook/addon-actions`)
Applied to files:
web/src/__tests__/components/layout/StatusBar.test.tsx
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/**/*.stories.tsx : Storybook 10: Import from `storybook/test` instead of `storybook/test`
Applied to files:
web/src/__tests__/components/layout/StatusBar.test.tsx
📚 Learning: 2026-04-03T18:36:51.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/__tests__/**/*.{test,spec}.{ts,tsx} : All unit tests must pass via `npm --prefix web run test` with coverage scoped to files changed vs origin/main
Applied to files:
web/src/__tests__/components/layout/StatusBar.test.tsx
📚 Learning: 2026-03-30T10:20:08.544Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:20:08.544Z
Learning: Applies to web/**/*.stories.{ts,tsx} : Storybook 10: Use storybook/test (not storybook/test) and storybook/actions (not storybook/addon-actions) import paths
Applied to files:
web/src/__tests__/components/layout/StatusBar.test.tsx
📚 Learning: 2026-03-30T10:20:08.544Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:20:08.544Z
Learning: Applies to web/**/*.test.{ts,tsx} : Web dashboard: Use React Hypothesis (fast-check) for property-based testing with fc.assert + fc.property
Applied to files:
web/src/__tests__/components/layout/StatusBar.test.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/layout/StatusBar.test.tsx
📚 Learning: 2026-03-30T10:41:40.176Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Applies to web/src/__tests__/**/*.test.{ts,tsx} : Use property-based testing with fast-check in React tests (`fc.assert` + `fc.property`)
Applied to files:
web/src/__tests__/components/layout/StatusBar.test.tsx
📚 Learning: 2026-04-03T18:36:51.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/components/ui/**/*.stories.tsx : Create a `.stories.tsx` file alongside each new component with all states (default, hover, loading, error, empty)
Applied to files:
web/src/__tests__/components/layout/StatusBar.test.tsx
| vi.mock('@/api/endpoints/analytics', () => ({ | ||
| getOverviewMetrics: vi.fn().mockResolvedValue(null), | ||
| getForecast: vi.fn().mockResolvedValue(null), | ||
| })) |
There was a problem hiding this comment.
Use contract-accurate analytics mocks (avoid resolved null)
On Line 37 and Line 38, the mocks resolve null for APIs whose contract is non-nullable (Promise<OverviewMetrics> / Promise<ForecastResponse>). This can hide real error-path behavior because Promise.allSettled treats those as fulfilled values instead of rejections.
Suggested fix
vi.mock('@/api/endpoints/analytics', () => ({
- getOverviewMetrics: vi.fn().mockResolvedValue(null),
- getForecast: vi.fn().mockResolvedValue(null),
+ // Use rejection for failure-path tests; use valid fixtures for success-path tests.
+ getOverviewMetrics: vi.fn().mockRejectedValue(new Error('mocked analytics failure')),
+ getForecast: vi.fn().mockRejectedValue(new Error('mocked analytics failure')),
}))🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/__tests__/components/layout/StatusBar.test.tsx` around lines 36 - 39,
The mocks in this test use getOverviewMetrics and getForecast with
mockResolvedValue(null) which violates the non-nullable contracts; update these
mocks to return contract-accurate resolved values (i.e., a valid OverviewMetrics
object for getOverviewMetrics and a valid ForecastResponse object for
getForecast) so Promise.allSettled treats them as real success paths—implement
or reuse small factory functions (e.g.,
getMockOverviewMetrics()/getMockForecastResponse()) or inline minimal shape
objects matching the types and replace mockResolvedValue(null) with
mockResolvedValue(<contract-accurate object>).
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
web/src/stores/providers/crud-actions.ts (1)
109-122:⚠️ Potential issue | 🟠 MajorCall
clearDetail()when deleting the active provider to prevent stale detail re-hydration.At Line 119, local field resets clear visible state but do not invalidate the detail
request guard indetail-actions.ts(_detailRequestName). An in-flight
fetchProviderDetail(name)can still commit stale data after deletion. Use
get().clearDetail()(and clear test result) when the deleted provider is active.Suggested fix
deleteProvider: async (name: string) => { set({ mutating: true }) try { await apiDeleteProvider(name) + const wasSelected = get().selectedProvider?.name === name // Remove from local state after successful deletion set((state) => ({ providers: state.providers.filter((p) => p.name !== name), healthMap: Object.fromEntries( Object.entries(state.healthMap).filter(([k]) => k !== name), ), - ...(state.selectedProvider?.name === name - ? { selectedProvider: null, selectedProviderModels: [], selectedProviderHealth: null, detailError: null } - : {}), })) + if (wasSelected) { + get().clearDetail() + get().clearTestResult() + } useToastStore.getState().add({ variant: 'success', title: `Provider "${name}" deleted`, })🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/stores/providers/crud-actions.ts` around lines 109 - 122, The deleteProvider flow should call the store's clearDetail helper to avoid stale detail re-hydration: after a successful apiDeleteProvider(name) and when state.selectedProvider?.name === name, invoke get().clearDetail() (and clear any selectedProviderTestResult/test result state) before or while you set selectedProvider to null so any in-flight fetchProviderDetail results are ignored; update deleteProvider to call get().clearDetail() (referencing deleteProvider, clearDetail, get(), and fetchProviderDetail/_detailRequestName in detail-actions.ts) when removing the active provider.
🤖 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/stores/providers/list-actions.ts`:
- Around line 14-15: The module-global counter _listRequestId must be made
instance-scoped: move the declaration and initialization of _listRequestId into
the createListActions function so each store instance gets its own counter
starting at 0, and update all uses (the stale-check comparisons and increments
that currently reference _listRequestId) to reference the new local variable
inside createListActions (ensure any inner closures like fetch/list request
handlers capture the instance-local counter).
- Line 39: The log.warn call in list-actions.ts exceeds the 88-character limit;
split the statement across multiple lines or build the message into a variable
before logging so it fits the max line length. Specifically, break up the
log.warn invocation that uses 'Failed to fetch health for provider:', names[i],
and getErrorMessage(result.reason) (or compose a const msg = ... using
getErrorMessage(result.reason) and then call log.warn(msg, names[i])) so the
final lines are under 88 characters while preserving the same arguments and
semantics.
---
Duplicate comments:
In `@web/src/stores/providers/crud-actions.ts`:
- Around line 109-122: The deleteProvider flow should call the store's
clearDetail helper to avoid stale detail re-hydration: after a successful
apiDeleteProvider(name) and when state.selectedProvider?.name === name, invoke
get().clearDetail() (and clear any selectedProviderTestResult/test result state)
before or while you set selectedProvider to null so any in-flight
fetchProviderDetail results are ignored; update deleteProvider to call
get().clearDetail() (referencing deleteProvider, clearDetail, get(), and
fetchProviderDetail/_detailRequestName in detail-actions.ts) when removing the
active provider.
🪄 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: 1e73388f-d18b-4846-9638-74d691480627
📒 Files selected for processing (3)
web/src/stores/analytics.tsweb/src/stores/providers/crud-actions.tsweb/src/stores/providers/list-actions.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: Dashboard Test
- GitHub Check: Build Web
- GitHub Check: Build Backend
- GitHub Check: Build Sandbox
- GitHub Check: Dependency Review
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (4)
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/**/*.{ts,tsx}: Use Tailwind semantic classes (text-foreground,bg-card,text-accent, etc.) or CSS variables (var(--so-*)) for colors. NEVER hardcode hex values or rgba() in.tsx/.tsfiles
Usefont-sansorfont-monofor typography (maps to Geist tokens). NEVER setfontFamilydirectly in styles
Use density-aware spacing tokens (p-card,gap-section-gap,gap-grid-gap) or standard Tailwind spacing. NEVER hardcode pixel values for layout spacing
Use token variables (var(--so-shadow-card-hover),border-border,border-bright) for shadows and borders
Do NOT usergba()with hardcoded values -- use design token variables
Do NOT hardcode Framer Motion transition durations -- use@/lib/motionpresets
Use/// <reference types="vite/client" />to enable side-effect imports for CSS in Storybook 10 with TypeScript 6
Ensure all TypeScript code passes type checking vianpm --prefix web run type-check
Bundle size must be auditable vianpm --prefix web run analyze(generates stats.html treemap)
Achieve Lighthouse performance audit target of 90+ vianpm --prefix web run lighthouse
web/src/**/*.{ts,tsx}: ALWAYS reuse existing components fromweb/src/components/ui/before creating new ones
Web Dashboard Design System (MANDATORY): Seeweb/CLAUDE.mdfor the full component inventory, design token rules, and post-training references (TS6, Storybook 10).
Files:
web/src/stores/analytics.tsweb/src/stores/providers/list-actions.tsweb/src/stores/providers/crud-actions.ts
web/src/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
Run ESLint with zero warnings enforced via
npm --prefix web run lintLine length: 88 characters (ruff) for JavaScript/TypeScript files
Files:
web/src/stores/analytics.tsweb/src/stores/providers/list-actions.tsweb/src/stores/providers/crud-actions.ts
web/src/stores/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
Use
web/src/stores/for Zustand store definitions (auth, WebSocket, toast, analytics, and per-domain stores)
Files:
web/src/stores/analytics.tsweb/src/stores/providers/list-actions.tsweb/src/stores/providers/crud-actions.ts
web/src/**/*.{ts,tsx,css}
📄 CodeRabbit inference engine (CLAUDE.md)
NEVER hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions — use design tokens and
@/lib/motionpresets
Files:
web/src/stores/analytics.tsweb/src/stores/providers/list-actions.tsweb/src/stores/providers/crud-actions.ts
🧠 Learnings (4)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-03T21:18:15.006Z
Learning: Applies to web/src/**/*.{ts,tsx} : Web Dashboard Design System (MANDATORY): See `web/CLAUDE.md` for the full component inventory, design token rules, and post-training references (TS6, Storybook 10).
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use Zustand stores for state management in the web dashboard; each domain has its own store module (auth, WebSocket, toast, analytics, setup, company, agents, budget, tasks, settings, providers, theme, per-domain stores)
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/**/*.{ts,tsx} : Achieve Lighthouse performance audit target of 90+ via `npm --prefix web run lighthouse`
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T12:44:29.466Z
Learning: Applies to web/src/stores/**/*.{ts,tsx} : Use Zustand stores in web dashboard for state management (auth, WebSocket, toast, analytics, domain shells)
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/stores/**/*.{ts,tsx} : Use `web/src/stores/` for Zustand store definitions (auth, WebSocket, toast, analytics, and per-domain stores)
📚 Learning: 2026-04-03T18:36:51.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/stores/**/*.{ts,tsx} : Use `web/src/stores/` for Zustand store definitions (auth, WebSocket, toast, analytics, and per-domain stores)
Applied to files:
web/src/stores/analytics.tsweb/src/stores/providers/list-actions.tsweb/src/stores/providers/crud-actions.ts
📚 Learning: 2026-03-30T10:41:40.176Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use Zustand stores for state management in the web dashboard; each domain has its own store module (auth, WebSocket, toast, analytics, setup, company, agents, budget, tasks, settings, providers, theme, per-domain stores)
Applied to files:
web/src/stores/analytics.tsweb/src/stores/providers/list-actions.tsweb/src/stores/providers/crud-actions.ts
📚 Learning: 2026-03-27T12:44:29.466Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T12:44:29.466Z
Learning: Applies to web/src/stores/**/*.{ts,tsx} : Use Zustand stores in web dashboard for state management (auth, WebSocket, toast, analytics, domain shells)
Applied to files:
web/src/stores/providers/list-actions.tsweb/src/stores/providers/crud-actions.ts
🔇 Additional comments (6)
web/src/stores/analytics.ts (4)
8-8: LGTM — module-scoped structured logger.The logger is correctly instantiated with the
'analytics'module name, consistent with the PR's structured logging approach.Also applies to: 18-19
76-76: Past review finding addressed —dept.nameis now a separate argument.The department name is correctly passed as a distinct argument rather than interpolated into the message string, allowing the logger's
sanitizeForLogto consistently sanitize it.
109-111: Good improvement — polling failures are now observable.Previously this catch block silently swallowed errors. Now it captures the error and logs a warning with the extracted message, which aids debugging without disrupting the polling loop.
125-125: LGTM — WebSocket event failures logged at error level.Using
log.errorhere is appropriate since a failure to process an incoming WebSocket event indicates a more serious issue than a transient network failure.web/src/stores/providers/crud-actions.ts (1)
149-154: Typed error fallback intestConnectionis solid.The fallback object matches
TestConnectionResponsenullability and keeps the
state shape stable for the UI.web/src/stores/providers/list-actions.ts (1)
22-33: Good async hygiene: stale-response guards + sanitized errors are consistent.The request-id checks and
getErrorMessage(...)usage here reduce race-induced state corruption and log noise.Also applies to: 43-47
| let _listRequestId = 0 | ||
|
|
There was a problem hiding this comment.
Scope request sequencing state per store instance.
_listRequestId is module-global, so multiple store instances can invalidate each other’s in-flight requests. Move the counter inside createListActions so stale checks are instance-local.
💡 Proposed fix
-let _listRequestId = 0
-
export function createListActions(set: ProvidersSet) {
+ let listRequestId = 0
return {
fetchProviders: async () => {
- const requestId = ++_listRequestId
+ const requestId = ++listRequestId
set({ listLoading: true, listError: null })
try {
const record = await listProviders()
- if (requestId !== _listRequestId) return
+ if (requestId !== listRequestId) return
const providers = normalizeProviders(record)
set({ providers })
@@
- if (requestId !== _listRequestId) return
+ if (requestId !== listRequestId) return
@@
- if (requestId !== _listRequestId) return
+ if (requestId !== listRequestId) returnAlso applies to: 19-23, 32-32, 44-44
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/stores/providers/list-actions.ts` around lines 14 - 15, The
module-global counter _listRequestId must be made instance-scoped: move the
declaration and initialization of _listRequestId into the createListActions
function so each store instance gets its own counter starting at 0, and update
all uses (the stale-check comparisons and increments that currently reference
_listRequestId) to reference the new local variable inside createListActions
(ensure any inner closures like fetch/list request handlers capture the
instance-local counter).
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (1)
web/src/stores/providers/list-actions.ts (1)
14-15:⚠️ Potential issue | 🟠 MajorMake the list request token instance-local.
_listRequestIdis still module-global, so a second Providers store instance can cancel the first instance’s in-flightfetchProviders()calls. Scope the counter insidecreateListActions()so stale checks stay local to one Zustand store.💡 Minimal fix
-let _listRequestId = 0 - export function createListActions(set: ProvidersSet) { + let listRequestId = 0 return { fetchProviders: async () => { - const requestId = ++_listRequestId + const requestId = ++listRequestId set({ listLoading: true, listError: null }) try { const record = await listProviders() - if (requestId !== _listRequestId) return + if (requestId !== listRequestId) return const providers = normalizeProviders(record) set({ providers }) @@ - if (requestId !== _listRequestId) return + if (requestId !== listRequestId) return @@ - if (requestId !== _listRequestId) return + if (requestId !== listRequestId) return log.error('Failed to fetch providers:', getErrorMessage(err)) set({ listLoading: false, listError: getErrorMessage(err) }) }Also applies to: 18-23, 32-32, 49-49
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/stores/providers/list-actions.ts` around lines 14 - 15, _listRequestId is currently a module-global variable causing cross-instance interference; move the request counter into createListActions() so it becomes instance-local. Replace the module-level _listRequestId with a local let listRequestId = 0 declared at the top of createListActions(), update fetchProviders() and any helper closures inside createListActions() to increment and compare that local listRequestId for stale-request checks (instead of the module variable), and remove or rename the module-scoped _listRequestId to avoid collisions.
🤖 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/stores/providers/crud-actions.ts`:
- Around line 39-40: The mutating flag is unsafe for concurrent operations:
replace the single boolean mutating with a counter and wrap all mutation
functions (createProvider, createFromPreset, updateProvider, deleteProvider) to
increment at start and decrement in finally using helper functions
(beginMutation / endMutation) so overlapping requests don't clear loading state
prematurely; update state accessors to treat mutating > 0 as "in flight" and
apply the same pattern to the other listed sites (lines referenced around
createProvider, createFromPreset, updateProvider, deleteProvider).
- Around line 91-95: The refresh can race and let an earlier (slower)
fetchProviderDetail overwrite a fresher one; fix by introducing a per-request
token/timestamp check: before calling fetchProviderDetail(name) from the CRUD
paths (the calls around get().fetchProviders() and the other occurrence),
generate a unique requestId and store it on the providers store (e.g.,
currentDetailRequestId), pass that requestId into fetchProviderDetail (or have
fetchProviderDetail capture the currentDetailRequestId at start) and then have
fetchProviderDetail compare the requestId/timestamp before committing the detail
to state so only the latest request can write; apply the same pattern to the
other refresh call (lines 170-172).
- Around line 33-35: The catch block logs the raw unknown error (log.warn) which
can leak non-error objects; normalize the error using getErrorMessage before
logging and when passing to state. Update the catch in the presets fetch path so
log.warn uses getErrorMessage(err) (or a local const like const msg =
getErrorMessage(err)) and continue to call set({ presetsLoading: false,
presetsError: msg }) to ensure consistent, safe error normalization; reference
the existing log.warn call and the set({ presetsLoading..., presetsError:
getErrorMessage(err) }) usage to locate and replace the raw err usage.
---
Duplicate comments:
In `@web/src/stores/providers/list-actions.ts`:
- Around line 14-15: _listRequestId is currently a module-global variable
causing cross-instance interference; move the request counter into
createListActions() so it becomes instance-local. Replace the module-level
_listRequestId with a local let listRequestId = 0 declared at the top of
createListActions(), update fetchProviders() and any helper closures inside
createListActions() to increment and compare that local listRequestId for
stale-request checks (instead of the module variable), and remove or rename the
module-scoped _listRequestId to avoid collisions.
🪄 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: 9dcfb4a4-75e9-4768-9530-bd73e272771d
📒 Files selected for processing (2)
web/src/stores/providers/crud-actions.tsweb/src/stores/providers/list-actions.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: Build Sandbox
- GitHub Check: Dashboard Test
- GitHub Check: Dependency Review
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (4)
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/**/*.{ts,tsx}: Use Tailwind semantic classes (text-foreground,bg-card,text-accent, etc.) or CSS variables (var(--so-*)) for colors. NEVER hardcode hex values or rgba() in.tsx/.tsfiles
Usefont-sansorfont-monofor typography (maps to Geist tokens). NEVER setfontFamilydirectly in styles
Use density-aware spacing tokens (p-card,gap-section-gap,gap-grid-gap) or standard Tailwind spacing. NEVER hardcode pixel values for layout spacing
Use token variables (var(--so-shadow-card-hover),border-border,border-bright) for shadows and borders
Do NOT usergba()with hardcoded values -- use design token variables
Do NOT hardcode Framer Motion transition durations -- use@/lib/motionpresets
Use/// <reference types="vite/client" />to enable side-effect imports for CSS in Storybook 10 with TypeScript 6
Ensure all TypeScript code passes type checking vianpm --prefix web run type-check
Bundle size must be auditable vianpm --prefix web run analyze(generates stats.html treemap)
Achieve Lighthouse performance audit target of 90+ vianpm --prefix web run lighthouse
web/src/**/*.{ts,tsx}: ALWAYS reuse existing components fromweb/src/components/ui/before creating new ones
Web Dashboard Design System (MANDATORY): Seeweb/CLAUDE.mdfor the full component inventory, design token rules, and post-training references (TS6, Storybook 10).
Files:
web/src/stores/providers/list-actions.tsweb/src/stores/providers/crud-actions.ts
web/src/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
Run ESLint with zero warnings enforced via
npm --prefix web run lintLine length: 88 characters (ruff) for JavaScript/TypeScript files
Files:
web/src/stores/providers/list-actions.tsweb/src/stores/providers/crud-actions.ts
web/src/stores/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
Use
web/src/stores/for Zustand store definitions (auth, WebSocket, toast, analytics, and per-domain stores)
Files:
web/src/stores/providers/list-actions.tsweb/src/stores/providers/crud-actions.ts
web/src/**/*.{ts,tsx,css}
📄 CodeRabbit inference engine (CLAUDE.md)
NEVER hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions — use design tokens and
@/lib/motionpresets
Files:
web/src/stores/providers/list-actions.tsweb/src/stores/providers/crud-actions.ts
🧠 Learnings (8)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-03T21:18:15.006Z
Learning: Applies to web/src/**/*.{ts,tsx} : Web Dashboard Design System (MANDATORY): See `web/CLAUDE.md` for the full component inventory, design token rules, and post-training references (TS6, Storybook 10).
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use Zustand stores for state management in the web dashboard; each domain has its own store module (auth, WebSocket, toast, analytics, setup, company, agents, budget, tasks, settings, providers, theme, per-domain stores)
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/**/*.{ts,tsx} : Achieve Lighthouse performance audit target of 90+ via `npm --prefix web run lighthouse`
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T12:44:29.466Z
Learning: Applies to web/src/stores/**/*.{ts,tsx} : Use Zustand stores in web dashboard for state management (auth, WebSocket, toast, analytics, domain shells)
📚 Learning: 2026-03-30T10:41:40.176Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use Zustand stores for state management in the web dashboard; each domain has its own store module (auth, WebSocket, toast, analytics, setup, company, agents, budget, tasks, settings, providers, theme, per-domain stores)
Applied to files:
web/src/stores/providers/list-actions.tsweb/src/stores/providers/crud-actions.ts
📚 Learning: 2026-04-03T18:36:51.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/stores/**/*.{ts,tsx} : Use `web/src/stores/` for Zustand store definitions (auth, WebSocket, toast, analytics, and per-domain stores)
Applied to files:
web/src/stores/providers/list-actions.tsweb/src/stores/providers/crud-actions.ts
📚 Learning: 2026-03-27T12:44:29.466Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T12:44:29.466Z
Learning: Applies to web/src/stores/**/*.{ts,tsx} : Use Zustand stores in web dashboard for state management (auth, WebSocket, toast, analytics, domain shells)
Applied to files:
web/src/stores/providers/list-actions.tsweb/src/stores/providers/crud-actions.ts
📚 Learning: 2026-04-03T21:18:15.006Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-03T21:18:15.006Z
Learning: Applies to web/src/**/*.{ts,tsx,js,jsx} : Line length: 88 characters (ruff) for JavaScript/TypeScript files
Applied to files:
web/src/stores/providers/list-actions.ts
📚 Learning: 2026-04-01T06:12:03.047Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T06:12:03.047Z
Learning: Applies to {**/*.py,web/src/**/*.{ts,tsx}} : Line length: 88 characters (enforced by ruff)
Applied to files:
web/src/stores/providers/list-actions.ts
📚 Learning: 2026-04-01T06:12:03.047Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T06:12:03.047Z
Learning: Applies to {**/*.py,web/src/**/*.{ts,tsx}} : Keep functions under 50 lines and files under 800 lines
Applied to files:
web/src/stores/providers/list-actions.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:
web/src/stores/providers/crud-actions.ts
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (1)
web/src/stores/providers/detail-actions.ts (1)
48-54:⚠️ Potential issue | 🟡 MinorNormalize rejected reasons before logging them.
modelsResult.reason,healthResult.reason, anderrare still raw unknown values here. Logging them directly reopens the unsanitized-error path this logger refactor was meant to remove; normalize once withgetErrorMessage(...)and log the string instead.Suggested fix
const partialErrors: string[] = [] if (modelsResult.status === 'rejected') { - log.warn('Failed to load models:', modelsResult.reason) - partialErrors.push(`models (${getErrorMessage(modelsResult.reason)})`) + const modelsError = getErrorMessage(modelsResult.reason) + log.warn('Failed to load models:', modelsError) + partialErrors.push(`models (${modelsError})`) } if (healthResult.status === 'rejected') { - log.warn('Failed to load health:', healthResult.reason) - partialErrors.push(`health (${getErrorMessage(healthResult.reason)})`) + const healthError = getErrorMessage(healthResult.reason) + log.warn('Failed to load health:', healthError) + partialErrors.push(`health (${healthError})`) } @@ } catch (err) { if (requestId !== _detailRequestId) return - log.error('Failed to fetch provider detail:', err) - set({ detailLoading: false, detailError: getErrorMessage(err) }) + const detailError = getErrorMessage(err) + log.error('Failed to fetch provider detail:', detailError) + set({ detailLoading: false, detailError }) }Also applies to: 68-71
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/stores/providers/detail-actions.ts` around lines 48 - 54, The code logs raw rejection reasons (modelsResult.reason, healthResult.reason, and other rejected values) which can be unsanitized; update the log.warn calls in this file (where modelsResult and healthResult are handled and the similar block at the later rejection handling) to call getErrorMessage(...) and log that string instead of the raw reason, e.g., replace log.warn('Failed to load models:', modelsResult.reason) with logging the normalized message from getErrorMessage(modelsResult.reason) and similarly for healthResult and any err variable so partialErrors still push getErrorMessage(...) outputs.
🤖 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/stores/providers/crud-actions.ts`:
- Around line 24-36: The shared top-level _mutationCount and its helper
functions (beginMutation, endMutation) must be moved inside createCrudActions so
each store instance has its own counter; change createCrudActions to declare a
local let mutationCount = 0 and implement beginMutation(set) and
endMutation(set) as inner functions that close over mutationCount
(increment/decrement and set mutating on the provided ProvidersSet), then use
these inner functions in the rest of createCrudActions; remove or stop exporting
the top-level _mutationCount/builder functions so tests and multiple store
instances don't share state.
- Around line 156-170: The testConnection action always writes
testConnectionResult/testingConnection when the API resolves, allowing late
responses to overwrite state after clearDetail; capture the current active
provider id (or detail id) before calling apiTestConnection inside
testConnection and only commit the success or error result if the active
provider id still matches (drop stale responses), or generate and store a
per-request nonce/id on call and compare it on resolution to ensure same-request
semantics; reference the testConnection function,
testConnectionResult/testingConnection state keys, apiTestConnection call, and
the existing clearDetail flow to implement the guard.
In `@web/src/stores/providers/detail-actions.ts`:
- Around line 12-17: _move the module-scoped _detailRequestId into
createDetailActions so each store instance has its own counter: remove the
top-level let _detailRequestId = 0 and declare a local let detailRequestId = 0
inside createDetailActions(), then update references to requestId generation in
fetchProviderDetail (use ++detailRequestId) and any other methods that
check/increment the counter (e.g., clearDetail or similar methods around lines
75-76) so the stale-request guard is isolated per store instance._
---
Duplicate comments:
In `@web/src/stores/providers/detail-actions.ts`:
- Around line 48-54: The code logs raw rejection reasons (modelsResult.reason,
healthResult.reason, and other rejected values) which can be unsanitized; update
the log.warn calls in this file (where modelsResult and healthResult are handled
and the similar block at the later rejection handling) to call
getErrorMessage(...) and log that string instead of the raw reason, e.g.,
replace log.warn('Failed to load models:', modelsResult.reason) with logging the
normalized message from getErrorMessage(modelsResult.reason) and similarly for
healthResult and any err variable so partialErrors still push
getErrorMessage(...) outputs.
🪄 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: 0e642ac7-d236-4364-90c8-6e60e3835c6c
📒 Files selected for processing (2)
web/src/stores/providers/crud-actions.tsweb/src/stores/providers/detail-actions.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: Build Sandbox
- GitHub Check: Dashboard Test
- GitHub Check: Dependency Review
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (4)
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/**/*.{ts,tsx}: Use Tailwind semantic classes (text-foreground,bg-card,text-accent, etc.) or CSS variables (var(--so-*)) for colors. NEVER hardcode hex values or rgba() in.tsx/.tsfiles
Usefont-sansorfont-monofor typography (maps to Geist tokens). NEVER setfontFamilydirectly in styles
Use density-aware spacing tokens (p-card,gap-section-gap,gap-grid-gap) or standard Tailwind spacing. NEVER hardcode pixel values for layout spacing
Use token variables (var(--so-shadow-card-hover),border-border,border-bright) for shadows and borders
Do NOT usergba()with hardcoded values -- use design token variables
Do NOT hardcode Framer Motion transition durations -- use@/lib/motionpresets
Use/// <reference types="vite/client" />to enable side-effect imports for CSS in Storybook 10 with TypeScript 6
Ensure all TypeScript code passes type checking vianpm --prefix web run type-check
Bundle size must be auditable vianpm --prefix web run analyze(generates stats.html treemap)
Achieve Lighthouse performance audit target of 90+ vianpm --prefix web run lighthouse
web/src/**/*.{ts,tsx}: ALWAYS reuse existing components fromweb/src/components/ui/before creating new ones
Web Dashboard Design System (MANDATORY): Seeweb/CLAUDE.mdfor the full component inventory, design token rules, and post-training references (TS6, Storybook 10).
Files:
web/src/stores/providers/detail-actions.tsweb/src/stores/providers/crud-actions.ts
web/src/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
Run ESLint with zero warnings enforced via
npm --prefix web run lintLine length: 88 characters (ruff) for JavaScript/TypeScript files
Files:
web/src/stores/providers/detail-actions.tsweb/src/stores/providers/crud-actions.ts
web/src/stores/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
Use
web/src/stores/for Zustand store definitions (auth, WebSocket, toast, analytics, and per-domain stores)
Files:
web/src/stores/providers/detail-actions.tsweb/src/stores/providers/crud-actions.ts
web/src/**/*.{ts,tsx,css}
📄 CodeRabbit inference engine (CLAUDE.md)
NEVER hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions — use design tokens and
@/lib/motionpresets
Files:
web/src/stores/providers/detail-actions.tsweb/src/stores/providers/crud-actions.ts
🧠 Learnings (5)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use Zustand stores for state management in the web dashboard; each domain has its own store module (auth, WebSocket, toast, analytics, setup, company, agents, budget, tasks, settings, providers, theme, per-domain stores)
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-03T21:18:15.006Z
Learning: Applies to web/src/**/*.{ts,tsx} : Web Dashboard Design System (MANDATORY): See `web/CLAUDE.md` for the full component inventory, design token rules, and post-training references (TS6, Storybook 10).
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/**/*.{ts,tsx} : Achieve Lighthouse performance audit target of 90+ via `npm --prefix web run lighthouse`
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T12:44:29.466Z
Learning: Applies to web/src/stores/**/*.{ts,tsx} : Use Zustand stores in web dashboard for state management (auth, WebSocket, toast, analytics, domain shells)
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : Place new shared components in `web/src/components/ui/` with descriptive kebab-case filenames
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/pages/**/*.{ts,tsx} : Organize page-scoped sub-components in `pages/<page-name>/` subdirectories rather than inline in page files
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:20:08.544Z
Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : When creating new shared web components, place in web/src/components/ui/ with kebab-case filename, create .stories.tsx alongside with all states (default, hover, loading, error, empty), export props as TypeScript interface, use design tokens exclusively with no hardcoded colors/fonts/spacing, and import cn from `@/lib/utils` for conditional class merging
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/stores/**/*.{ts,tsx} : Use `web/src/stores/` for Zustand store definitions (auth, WebSocket, toast, analytics, and per-domain stores)
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/**/*.{ts,tsx} : Bundle size must be auditable via `npm --prefix web run analyze` (generates stats.html treemap)
📚 Learning: 2026-03-30T10:41:40.176Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use Zustand stores for state management in the web dashboard; each domain has its own store module (auth, WebSocket, toast, analytics, setup, company, agents, budget, tasks, settings, providers, theme, per-domain stores)
Applied to files:
web/src/stores/providers/detail-actions.tsweb/src/stores/providers/crud-actions.ts
📚 Learning: 2026-04-03T18:36:51.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/stores/**/*.{ts,tsx} : Use `web/src/stores/` for Zustand store definitions (auth, WebSocket, toast, analytics, and per-domain stores)
Applied to files:
web/src/stores/providers/crud-actions.ts
📚 Learning: 2026-03-27T12:44:29.466Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T12:44:29.466Z
Learning: Applies to web/src/stores/**/*.{ts,tsx} : Use Zustand stores in web dashboard for state management (auth, WebSocket, toast, analytics, domain shells)
Applied to files:
web/src/stores/providers/crud-actions.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:
web/src/stores/providers/crud-actions.ts
| let _mutationCount = 0 | ||
|
|
||
| function beginMutation(set: ProvidersSet): void { | ||
| _mutationCount++ | ||
| set({ mutating: true }) | ||
| } | ||
|
|
||
| function endMutation(set: ProvidersSet): void { | ||
| _mutationCount = Math.max(0, _mutationCount - 1) | ||
| if (_mutationCount === 0) set({ mutating: false }) | ||
| } | ||
|
|
||
| export function createCrudActions(set: ProvidersSet, get: ProvidersGet) { |
There was a problem hiding this comment.
Keep the mutation counter inside the store factory.
_mutationCount is shared by every store instance created from this module. If a second providers store exists in tests or another isolated render, one instance can leave another stuck in mutating or clear it too early. Scope the counter to createCrudActions() so each Zustand store tracks only its own in-flight mutations.
Suggested fix
-let _mutationCount = 0
-
-function beginMutation(set: ProvidersSet): void {
- _mutationCount++
- set({ mutating: true })
-}
-
-function endMutation(set: ProvidersSet): void {
- _mutationCount = Math.max(0, _mutationCount - 1)
- if (_mutationCount === 0) set({ mutating: false })
-}
-
export function createCrudActions(set: ProvidersSet, get: ProvidersGet) {
+ let mutationCount = 0
+
+ const beginMutation = (set: ProvidersSet): void => {
+ mutationCount += 1
+ set({ mutating: true })
+ }
+
+ const endMutation = (set: ProvidersSet): void => {
+ mutationCount = Math.max(0, mutationCount - 1)
+ if (mutationCount === 0) set({ mutating: false })
+ }
+
return {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/stores/providers/crud-actions.ts` around lines 24 - 36, The shared
top-level _mutationCount and its helper functions (beginMutation, endMutation)
must be moved inside createCrudActions so each store instance has its own
counter; change createCrudActions to declare a local let mutationCount = 0 and
implement beginMutation(set) and endMutation(set) as inner functions that close
over mutationCount (increment/decrement and set mutating on the provided
ProvidersSet), then use these inner functions in the rest of createCrudActions;
remove or stop exporting the top-level _mutationCount/builder functions so tests
and multiple store instances don't share state.
| let _detailRequestId = 0 | ||
|
|
||
| export function createDetailActions(set: ProvidersSet) { | ||
| return { | ||
| fetchProviderDetail: async (name: string) => { | ||
| const requestId = ++_detailRequestId |
There was a problem hiding this comment.
Make the detail request id store-local.
_detailRequestId lives at module scope, so separate store instances share the same invalidation counter. A fetch or clearDetail() in one instance can therefore cancel a valid detail load in another. Move the counter into createDetailActions() so the stale-request guard is isolated per store.
Suggested fix
-let _detailRequestId = 0
-
export function createDetailActions(set: ProvidersSet) {
+ let detailRequestId = 0
+
return {
fetchProviderDetail: async (name: string) => {
- const requestId = ++_detailRequestId
+ const requestId = ++detailRequestId
@@
- if (requestId !== _detailRequestId) return
+ if (requestId !== detailRequestId) return
@@
- if (requestId !== _detailRequestId) return
+ if (requestId !== detailRequestId) return
@@
clearDetail: () => {
- _detailRequestId++ // invalidate in-flight requests
+ detailRequestId += 1 // invalidate in-flight requestsAlso applies to: 75-76
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/stores/providers/detail-actions.ts` around lines 12 - 17, _move the
module-scoped _detailRequestId into createDetailActions so each store instance
has its own counter: remove the top-level let _detailRequestId = 0 and declare a
local let detailRequestId = 0 inside createDetailActions(), then update
references to requestId generation in fetchProviderDetail (use
++detailRequestId) and any other methods that check/increment the counter (e.g.,
clearDetail or similar methods around lines 75-76) so the stale-request guard is
isolated per store instance._
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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/stores/providers/crud-actions.ts`:
- Around line 182-205: The discoverModels function fires a success toast even if
the user navigated away; capture the target provider name at the start (e.g.,
const targetName = name) and use that stale-response guard before showing the
success toast and before calling fetchProviderDetail: only call
useToastStore.getState().add(...) and await get().fetchProviderDetail(name) if
get().selectedProvider?.name === targetName; apply the same guard in the error
path if you want to avoid error toasts for a different provider.
- Around line 156-180: The stale-response guard in testConnection (function
testConnection in crud-actions.ts) returns early when selectedProvider no longer
matches targetProvider but leaves testingConnection true; before each early
return (the two branches that check get().selectedProvider?.name !==
targetProvider) call set({ testingConnection: false }) so the UI stops loading,
then return null; update both the success-path guard and the catch-path guard to
reset testingConnection to false prior to returning.
🪄 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: 2141c298-896d-478d-ac3a-0ba843d6ef59
📒 Files selected for processing (2)
web/src/stores/providers/crud-actions.tsweb/src/stores/providers/detail-actions.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (4)
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/**/*.{ts,tsx}: Use Tailwind semantic classes (text-foreground,bg-card,text-accent, etc.) or CSS variables (var(--so-*)) for colors. NEVER hardcode hex values or rgba() in.tsx/.tsfiles
Usefont-sansorfont-monofor typography (maps to Geist tokens). NEVER setfontFamilydirectly in styles
Use density-aware spacing tokens (p-card,gap-section-gap,gap-grid-gap) or standard Tailwind spacing. NEVER hardcode pixel values for layout spacing
Use token variables (var(--so-shadow-card-hover),border-border,border-bright) for shadows and borders
Do NOT usergba()with hardcoded values -- use design token variables
Do NOT hardcode Framer Motion transition durations -- use@/lib/motionpresets
Use/// <reference types="vite/client" />to enable side-effect imports for CSS in Storybook 10 with TypeScript 6
Ensure all TypeScript code passes type checking vianpm --prefix web run type-check
Bundle size must be auditable vianpm --prefix web run analyze(generates stats.html treemap)
Achieve Lighthouse performance audit target of 90+ vianpm --prefix web run lighthouse
web/src/**/*.{ts,tsx}: A PostToolUse hook (scripts/check_web_design_system.py) enforces design system rules on every Edit/Write toweb/src/
Use property-based testing with fast-check (fc.assert+fc.property) for React component tests
Files:
web/src/stores/providers/detail-actions.tsweb/src/stores/providers/crud-actions.ts
web/src/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
Run ESLint with zero warnings enforced via
npm --prefix web run lint
Files:
web/src/stores/providers/detail-actions.tsweb/src/stores/providers/crud-actions.ts
web/src/stores/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
Use
web/src/stores/for Zustand store definitions (auth, WebSocket, toast, analytics, and per-domain stores)
Files:
web/src/stores/providers/detail-actions.tsweb/src/stores/providers/crud-actions.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Always reuse existing components fromweb/src/components/ui/before creating new ones
Never hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions — use design tokens and@/lib/motionpresets
Files:
web/src/stores/providers/detail-actions.tsweb/src/stores/providers/crud-actions.ts
🧠 Learnings (5)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use Zustand stores for state management in the web dashboard; each domain has its own store module (auth, WebSocket, toast, analytics, setup, company, agents, budget, tasks, settings, providers, theme, per-domain stores)
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T12:44:29.466Z
Learning: Applies to web/src/stores/**/*.{ts,tsx} : Use Zustand stores in web dashboard for state management (auth, WebSocket, toast, analytics, domain shells)
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/**/*.{ts,tsx} : Achieve Lighthouse performance audit target of 90+ via `npm --prefix web run lighthouse`
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/pages/**/*.{ts,tsx} : Organize page-scoped sub-components in `pages/<page-name>/` subdirectories rather than inline in page files
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/stores/**/*.{ts,tsx} : Use `web/src/stores/` for Zustand store definitions (auth, WebSocket, toast, analytics, and per-domain stores)
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : Place new shared components in `web/src/components/ui/` with descriptive kebab-case filenames
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:20:08.544Z
Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : When creating new shared web components, place in web/src/components/ui/ with kebab-case filename, create .stories.tsx alongside with all states (default, hover, loading, error, empty), export props as TypeScript interface, use design tokens exclusively with no hardcoded colors/fonts/spacing, and import cn from `@/lib/utils` for conditional class merging
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/**/*.{ts,tsx} : Bundle size must be auditable via `npm --prefix web run analyze` (generates stats.html treemap)
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:31:11.894Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use React 19, TypeScript 6.0+, and design system tokens from shadcn/ui + Tailwind CSS 4 + Radix UI in web dashboard
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Applies to web/** : Web dashboard: Node.js 20+, dependencies in web/package.json (Vue 3, PrimeVue, Tailwind CSS, Pinia, VueFlow, ECharts, Axios, vue-draggable-plus, Vitest, fast-check, ESLint, vue-tsc).
📚 Learning: 2026-03-30T10:41:40.176Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use Zustand stores for state management in the web dashboard; each domain has its own store module (auth, WebSocket, toast, analytics, setup, company, agents, budget, tasks, settings, providers, theme, per-domain stores)
Applied to files:
web/src/stores/providers/detail-actions.tsweb/src/stores/providers/crud-actions.ts
📚 Learning: 2026-04-03T18:36:51.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-03T18:36:51.730Z
Learning: Applies to web/src/stores/**/*.{ts,tsx} : Use `web/src/stores/` for Zustand store definitions (auth, WebSocket, toast, analytics, and per-domain stores)
Applied to files:
web/src/stores/providers/crud-actions.ts
📚 Learning: 2026-03-27T12:44:29.466Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T12:44:29.466Z
Learning: Applies to web/src/stores/**/*.{ts,tsx} : Use Zustand stores in web dashboard for state management (auth, WebSocket, toast, analytics, domain shells)
Applied to files:
web/src/stores/providers/crud-actions.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:
web/src/stores/providers/crud-actions.ts
🔇 Additional comments (10)
web/src/stores/providers/detail-actions.ts (3)
12-17: Module-scoped_detailRequestIdis still shared across store instances.The previous review flagged that
_detailRequestIdat module scope causes separate store instances to share the same invalidation counter. This has not been addressed—the counter should be moved insidecreateDetailActions()so each store instance has its own stale-request guard.
19-75: Well-structured concurrent fetching with graceful partial-failure handling.The use of
Promise.allSettledwith per-result error handling and aggregatedpartialErrorsis a good pattern. Logging is consistently sanitized viagetErrorMessage, aligning with the PR objectives for structured logging.
77-88: Clear and comprehensive state reset.
clearDetail()properly invalidates in-flight requests by incrementing the counter and resets all detail-related state fields including test connection state.web/src/stores/providers/crud-actions.ts (7)
24-34: Module-scoped_mutationCountis still shared across store instances.The previous review flagged that
_mutationCountat module scope causes separate store instances to share the same mutation counter. This has not been addressed—move the counter and helper functions insidecreateCrudActions()so each store instance tracks only its own in-flight mutations.
38-49: Session-level preset caching with proper error handling.Error logging is correctly normalized via
getErrorMessage(err). The session-level caching for static preset data is reasonable.
51-71: Standard CRUD pattern with proper mutation tracking.Follows the expected pattern with mutation guards, toast notifications, and list refresh. Error handling via
getErrorMessageis consistent.
73-93: Consistent implementation withcreateProvider.
95-119: Correctly refreshes detail view when updating the selected provider.The conditional refresh at lines 105-107 ensures the UI stays in sync when editing the currently viewed provider.
121-154: Properly clears detail state when deleting the selected provider.Lines 127-129 address the previous concern about stale detail state after deletion. The optimistic local removal with error-path refresh is a good UX pattern.
207-208: Simple and correct.
| discoverModels: async (name: string, presetHint?: string) => { | ||
| set({ discoveringModels: true }) | ||
| try { | ||
| const result = await apiDiscoverModels(name, presetHint) | ||
| useToastStore.getState().add({ | ||
| variant: 'success', | ||
| title: `Discovered ${result.discovered_models.length} models`, | ||
| }) | ||
| // Refresh detail to show updated models | ||
| if (get().selectedProvider?.name === name) { | ||
| await get().fetchProviderDetail(name) | ||
| } | ||
| return result | ||
| } catch (err) { | ||
| useToastStore.getState().add({ | ||
| variant: 'error', | ||
| title: 'Model discovery failed', | ||
| description: getErrorMessage(err), | ||
| }) | ||
| return null | ||
| } finally { | ||
| set({ discoveringModels: false }) | ||
| } | ||
| }, |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider adding a stale-response guard for consistency.
Unlike testConnection, discoverModels doesn't guard against stale responses. If the user navigates away during discovery, the success toast still fires. While fetchProviderDetail has its own guard, the toast at lines 186-189 could confuse users viewing a different provider.
Optional: capture target provider and guard the toast
discoverModels: async (name: string, presetHint?: string) => {
+ const targetProvider = name
set({ discoveringModels: true })
try {
const result = await apiDiscoverModels(name, presetHint)
+ if (get().selectedProvider?.name !== targetProvider) {
+ return result // skip toast and detail refresh
+ }
useToastStore.getState().add({🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/stores/providers/crud-actions.ts` around lines 182 - 205, The
discoverModels function fires a success toast even if the user navigated away;
capture the target provider name at the start (e.g., const targetName = name)
and use that stale-response guard before showing the success toast and before
calling fetchProviderDetail: only call useToastStore.getState().add(...) and
await get().fetchProviderDetail(name) if get().selectedProvider?.name ===
targetName; apply the same guard in the error path if you want to avoid error
toasts for a different provider.
Create web/src/lib/logger.ts with createLogger() factory that prefixes messages with [module] and auto-sanitizes string/Error arguments via sanitizeForLog. Closes partial #1055
Split 769-line monolith into 3 files by extension type: - editor-diff.ts: LCS-based line diff + gutter markers - editor-linter.ts: syntax + schema validation - editor-autocomplete.ts: JSON/YAML schema-aware completions Original file becomes a barrel re-export preserving existing imports. Add unit tests for computeLineDiff and validateSchema. Partial #1055
Move 5 pure utility functions (entriesToObject, serializeEntries, detectRemovedKeys, buildChanges, parseText) and related types from CodeEditorPanel.tsx to a dedicated module. Component drops from ~410 to ~295 lines. Add unit tests for all extracted functions. Partial #1055
Split 509-line monolithic store into focused helper files: - providers/types.ts: state interface + set/get type aliases - providers/list-actions.ts: fetchProviders + filter setters - providers/detail-actions.ts: fetchProviderDetail + clearDetail - providers/crud-actions.ts: CRUD operations + presets - providers/local-model-actions.ts: pull/delete/update model Main store shrinks to ~44 lines (defaults + delegation). Partial #1055
Migrate 85+ console.error/console.warn calls across 30 files to use createLogger() from @/lib/logger. Each module gets a prefixed logger (e.g. [ws], [budget], [settings]) that auto-sanitizes arguments. Files migrated: stores (websocket, setup-wizard, budget, company, approvals, theme, messages, tasks, meetings, settings, auth, analytics, agents), hooks (useWebSocket, usePolling, useOrgChartData, useOptimisticUpdate, useSettingsDirtyState), utils (format, errors, dashboard), components (error-boundary, drawer, command-palette), pages (WorkflowEditor, OrgChart, SettingsPage, SettingField, SinkFormDrawer, ProviderFormModal, ProviderProbeResults, AccountStep), and api (client, endpoints/providers). Only web/src/lib/logger.ts itself uses bare console methods. Closes #1055
- Restore sanitizeForLog for attacker-controlled WS fields in meetings.ts and messages.ts (security regression) - Harden logger sanitizeArg: sanitize non-string/non-Error primitives - Move const log declarations after all imports in 19 files - Update web/CLAUDE.md: lib/ and stores/ descriptions - Update docs/design/ux-guidelines.md: add logger to reference table - Add editor-autocomplete.test.ts (missing test coverage) - Add position assertions to linter schema validation tests - Add logging to provider store catch blocks (fetchPresets, fetchProviders, fetchProviderDetail) - Tighten diff test assertion with toHaveLength Pre-reviewed by 5 agents, 12 findings addressed
…emini, CodeRabbit Critical: remove mutating guard blocking post-CRUD refreshes (list-actions), prototype pollution prevention via Object.create(null) (code-editor-utils). Major: move dynamic values to logger args for sanitization (settings, format, meetings), sanitize candidate.id in tasks WS handler, normalize raw err objects to getErrorMessage in meetings and setup-wizard (12+ call sites), add structured logger to local-model-actions, unconditional error logging in ErrorBoundary, log polling errors in analytics, clean healthMap on provider delete, fetchProviders after updateModelConfig, document logging convention in web/CLAUDE.md, update stores enumeration. Medium: CRLF normalization and fast path in editor-diff, clamp removed-line number to 1, remove redundant sort in gutter builder, relax YAML indent heuristic in autocomplete, handle quoted YAML keys in linter, memoize completion schema, fix line-length violation, use gap-grid-gap token in CodeEditorPanel, fix misleading module/test comments, add YAML syntax error and injection safety tests, add debug level to Logger, explicit format validation with exhaustive checks. Minor: add null/undefined/debug edge case tests for logger, add unchanged-values test for buildChanges, use ParsedSettings alias in entriesToObject return type.
The fast-check property test renders 100 component instances; with V8 coverage instrumentation the default 5s timeout is insufficient. Raise to 15s to prevent flaky CI failures.
StatusBar imports useAnalyticsStore. When fetchOverview fails (unmocked endpoint) and logs via console.warn, the output can race with Vitest worker teardown in CI, causing EnvironmentTeardownError: 'Closing rpc while onUserConsoleLog was pending'. Mock the analytics endpoints to prevent unhandled rejections during test cleanup.
- analytics.ts: move dept.name out of message string into separate arg - crud-actions.ts: clear selectedProvider detail on provider delete - list-actions.ts: use getErrorMessage() for raw error objects in logs
…er delete - list-actions.ts: split 99-char log.warn call across multiple lines - crud-actions.ts: call get().clearDetail() instead of inline state reset when deleting the selected provider -- this also resets the _detailRequestName stale-request guard, testConnectionResult, and testingConnection that the inline reset missed
- crud-actions.ts: replace boolean mutating flag with a counter (beginMutation/endMutation) so overlapping CRUD operations don't clear the loading state prematurely -- the flag stays true until all in-flight mutations complete - crud-actions.ts: normalize raw err in fetchPresets log.warn - detail-actions.ts: replace name-based stale-request guard (_detailRequestName) with numeric _detailRequestId -- the name guard only prevented different-name races; the counter also prevents same-name races where a slower fetch for the same provider could overwrite fresher data - detail-actions.ts: clearDetail() increments the counter to invalidate any in-flight fetchProviderDetail calls
- crud-actions.ts: testConnection now captures targetProvider before the API call and drops the response if selectedProvider changed (user navigated away via clearDetail while test was in flight) - detail-actions.ts: normalize raw rejection reasons and err objects via getErrorMessage() in log.warn/log.error calls for consistency with the pattern applied across all other store modules
The stale-response guard returned null without clearing testingConnection, leaving the UI spinner stuck if the user navigated to a different provider while a test was in flight.
b738ae5 to
964af10
Compare
🤖 I have created a release *beep* *boop* --- ## [0.6.1](v0.6.0...v0.6.1) (2026-04-04) ### Features * capability-aware prompt profiles for model tier adaptation ([#1047](#1047)) ([67650c5](67650c5)), closes [#805](#805) * implement procedural memory auto-generation from agent failures ([#1048](#1048)) ([55f5206](55f5206)), closes [#420](#420) * implement quality scoring Layers 2+3 -- LLM judge and human override ([#1057](#1057)) ([4a8adfe](4a8adfe)), closes [#230](#230) * token-based personality trimming via PromptProfile.max_personality_tokens ([#1059](#1059)) ([75afd52](75afd52)), closes [#1045](#1045) * workflow execution lifecycle + editor improvements ([#1058](#1058)) ([7b54262](7b54262)), closes [#1029](#1029) [#1042](#1042) ### Refactoring * **web:** address complexity and logging issues in dashboard ([#1056](#1056)) ([ada997b](ada997b)), closes [#1055](#1055) ### Documentation * comprehensive documentation refresh ([#1050](#1050)) ([c7a4259](c7a4259)) ### Tests * fix Hypothesis fuzzing infra and speed up slow unit tests ([#1044](#1044)) ([1111602](1111602)) ### Maintenance * add text=auto catch-all to .gitattributes ([#1051](#1051)) ([fc65d72](fc65d72)) * bump defu from 6.1.4 to 6.1.6 in /site ([#1062](#1062)) ([f0cc439](f0cc439)) --- 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>
Summary
Addresses complexity hotspots and inconsistent error logging across the web dashboard, as flagged by static analysis.
Complexity reduction
providers.ts(509 lines, complexity 41): Decomposed into 4 focused action modules understores/providers/(list, detail, CRUD, local-model). Main store shrinks to ~44 lines.editor-extensions.ts(769 lines): Split intoeditor-diff.ts,editor-linter.ts,editor-autocomplete.ts+ barrel re-export. Zero import changes for consumers.CodeEditorPanel.tsx(410 lines, complexity 49): Extracted 5 pure utility functions tocode-editor-utils.ts. Component drops to ~305 lines.Structured logging
web/src/lib/logger.ts-- lightweightcreateLogger(module)factory that prefixes messages with[module]and auto-sanitizes string/Error arguments viasanitizeForLog.console.error/console.warncalls across 37 files (stores, hooks, utils, components, pages, API client) to use the structured logger.logger.tsitself uses bare console methods now.Test coverage
computeLineDiff,validateSchema/buildSchemaInfo,editor-autocompleteextension, and all 5 extracted CodeEditorPanel helpers.usePolling.test.tsassertion for logger prefix.Documentation
web/CLAUDE.mdpackage structure (lib/ and stores/ descriptions).docs/design/ux-guidelines.mdreference table.Test plan
npm --prefix web run type-check-- cleannpm --prefix web run lint-- zero warningsnpm --prefix web run test-- 2390 tests passnpm --prefix web run build-- clean production buildReview coverage
Pre-reviewed by 5 agents (docs-consistency, frontend-reviewer, issue-resolution-verifier, silent-failure-hunter, test-quality-reviewer). 12 findings identified and addressed:
sanitizeForLogfor attacker-controlled WS payload fieldsconst logafter all imports in 19 filesCloses #1055