feat: interaction components and animation patterns#853
Conversation
WalkthroughAdds many interaction components, hooks, stores, motion presets, Storybook stories, and tests. New UI components: CommandPalette, Toast/ToastContainer, Skeleton suite, EmptyState, ErrorBoundary, ConfirmDialog, InlineEdit, AnimatedPresence, StaggerGroup/StaggerItem. New hooks/libs: useCommandPalette (and useRegisterCommands), useFlash, useStatusTransition, plus motion exports (toastEntrance, overlayBackdrop, modalEntrance, listItemLayout, inlineEditEntrance, statusColorTransition, reducedPageVariants). Introduces a Zustand toast store, integrates AnimatedPresence/CommandPalette/ToastContainer into AppLayout and Sidebar, and adds reduced-motion CSS rules. No public API removals. 🚥 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.
Code Review
This pull request introduces a suite of UI interaction components and hooks, including a global command palette, toast notifications, error boundaries, and animation utilities, supported by extensive test coverage. Key additions include the CommandPalette for navigation, ToastContainer for system feedback, and AnimatedPresence for route-based transitions. Feedback suggests refining the ErrorBoundary reset logic to avoid disruptive page reloads and removing redundant args in Storybook stories to improve code conciseness.
| <Suspense fallback={<PageLoadingFallback />}> | ||
| <Outlet /> | ||
| </Suspense> | ||
| <ErrorBoundary level="page" onReset={() => window.location.reload()}> |
There was a problem hiding this comment.
The onReset prop for the page-level ErrorBoundary currently triggers a full page reload. While this ensures a clean state, it can be disruptive to the user experience. Consider implementing a more granular reset mechanism, such as re-fetching data or resetting specific component states, if possible, to provide a smoother recovery.
| } | ||
|
|
||
| export const PageLevel: Story = { | ||
| args: { children: null }, |
| } | ||
|
|
||
| export const SectionLevel: Story = { | ||
| args: { children: null }, |
| args: { children: null }, | ||
| render: () => ( | ||
| <ErrorBoundary level="component"> | ||
| <BrokenComponent /> |
| args: { children: null }, | ||
| render: () => ( | ||
| <ErrorBoundary | ||
| fallback={({ error, resetErrorBoundary }) => ( |
| export const Interactive: Story = { | ||
| args: { children: null }, | ||
| render: () => <InteractiveDemo />, | ||
| } |
There was a problem hiding this comment.
Actionable comments posted: 28
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@web/src/__tests__/components/ui/command-palette.test.tsx`:
- Around line 11-18: The test assigns ResizeObserverMock to
globalThis.ResizeObserver and replaces Element.prototype.scrollIntoView at
module scope which can leak to other tests; change the setup to run inside the
test lifecycle (e.g. in beforeAll/afterAll within this test file or in a shared
test setup file) and restore originals in afterAll so the global state is not
mutated permanently—specifically reference the ResizeObserverMock,
globalThis.ResizeObserver, and Element.prototype.scrollIntoView symbols to
locate and wrap the assignments with setup/teardown logic or move them into your
centralized test setup.
In `@web/src/__tests__/components/ui/error-boundary.test.tsx`:
- Around line 20-22: CustomFallback uses a too-broad type for level; change its
signature to use the precise union or the shared type: replace the level: string
annotation in the CustomFallback({ error }: { error: Error; resetErrorBoundary:
() => void; level: string }) signature with the correct type (either level:
'page' | 'section' | 'component' or import and use the ErrorFallbackProps type)
so the test uses the same strong typing as ErrorFallbackProps.
In `@web/src/__tests__/components/ui/inline-edit.test.tsx`:
- Around line 107-120: The test named "cancel preserves original value" only
checks the initial render and doesn't exercise the cancel path; update the
property-based test that uses fc.property, render and InlineEdit (with vi.fn()
for onSave) to simulate opening the inline editor, change the input value,
trigger the cancel action (click the cancel button or trigger the cancel
control), then assert that the element selected by
document.body.querySelector('[data-inline-display]') still shows the original
generated value, and finally unmount; ensure the simulated edit and cancel use
the actual data attributes or controls implemented by InlineEdit so the cancel
flow is validated for all generated strings.
In `@web/src/__tests__/components/ui/skeleton.test.tsx`:
- Around line 35-36: The test is using an implementation-detail selector
('.rounded') via container.querySelectorAll which couples it to Tailwind
classes; change the Skeleton component to expose a stable selector (add
data-testid="skeleton-row" or data-skeleton-row attributes) and update the test
assertions that create the lines variable (replace
container.querySelectorAll('.rounded') with
container.querySelectorAll('[data-skeleton-row]') or use
getAllByTestId('skeleton-row')), and apply the same fix to the other occurrences
that query '.rounded' (the similar assertions at the other two spots) so tests
rely on semantic/test ids instead of CSS classes; reference SkeletonTable which
already uses the data-skeleton-row pattern as the model.
- Around line 61-65: The test currently only checks existence of the wrapper for
SkeletonCard but must assert the lines prop is honored: update the test in
skeleton.test.tsx to render <SkeletonCard lines={4} /> and then query container
(or wrapper) for elements with a data attribute like data-skeleton-line (e.g.,
wrapper.querySelectorAll('[data-skeleton-line]')) and expect its length
toEqual(4); if SkeletonCard does not yet render this attribute, modify the
SkeletonCard implementation to add data-skeleton-line on each rendered line so
the test can reliably count them.
In `@web/src/__tests__/components/ui/stagger-group.test.tsx`:
- Around line 28-66: Add unit tests that exercise animation-related props on
StaggerGroup and StaggerItem: render StaggerGroup with animate={false} and
assert children are rendered but no animation-related classes/attributes or
motion elements are present; render StaggerItem with layoutId and layout props
and assert those attributes/props are present on the DOM/motion element; and
render StaggerGroup with different staggerDelay values and assert timing-related
classes/inline-styles or computed animation-delay values reflect the provided
delay. Use the existing test helpers (render, screen, container) and reference
the StaggerGroup and StaggerItem components when adding these assertions.
In `@web/src/__tests__/hooks/useCommandPalette.test.ts`:
- Around line 10-18: The helper makeCommand currently returns a static id
('test-1') which risks collisions; change it to produce a unique id by default
(e.g., use a module-level counter or a UID generator like nanoid/uuid) so calls
to makeCommand() without overrides get distinct ids; update makeCommand (and add
the needed import or counter variable) to set id: generatedId unless
overrides.id is provided, keeping tests that explicitly pass an id unchanged.
In `@web/src/__tests__/hooks/useFlash.test.ts`:
- Around line 87-108: Replace the hardcoded 300ms in the rapid-trigger test with
a value derived from STATUS_FLASH so the test adapts if timings change: compute
an intermediate advance like Math.floor(STATUS_FLASH.totalMs / 2) (or another
fraction of STATUS_FLASH.totalMs) and use that value in the two
vi.advanceTimersByTime calls that currently pass 300; keep the rest of the test
logic the same and continue to use result.current.triggerFlash and assertions on
result.current.flashing.
In `@web/src/__tests__/hooks/useStatusTransition.test.ts`:
- Around line 25-43: Replace the loose substring assertions in the
useStatusTransition tests with exact-token assertions: for each test that calls
renderHook(() => useStatusTransition(...)), assert result.current.displayColor
equals the exact expected token (use toBe or toEqual) rather than using
toContain; update the four tests that check 'active', 'error', 'idle', and
'offline' to assert the precise displayColor token returned by
useStatusTransition (reference the useStatusTransition hook and
result.current.displayColor to locate the assertions).
In `@web/src/__tests__/lib/motion.test.ts`:
- Line 5: The import includes a redundant alias "cardEntrance as cardEntrance";
remove the no-op alias and import the symbol directly (replace the "cardEntrance
as cardEntrance" specifier with just "cardEntrance") in the import statement in
motion.test.ts so the test imports the symbol without the unnecessary alias.
- Around line 163-177: The tests override window.matchMedia when checking
prefersReducedMotion and never restore it, which can pollute other tests;
capture the original window.matchMedia before mocking (e.g., const
originalMatchMedia = window.matchMedia) and add an afterEach() that restores
window.matchMedia = originalMatchMedia so reducedMotionInstant and
prefersReducedMotion tests clean up after themselves.
In `@web/src/components/layout/AppLayout.tsx`:
- Around line 68-74: The current ErrorBoundary onReset handler calls
window.location.reload(), which flushes client state; change it to perform
navigation to a safe route instead so app state (Zustand caches, other pages) is
preserved: import and use react-router's useNavigate and replace the onReset={()
=> window.location.reload()} with an onReset that calls navigate('/dashboard')
(or another safe route) via the navigate function from useNavigate; ensure this
change is applied where ErrorBoundary is rendered (around
AnimatedPresence/Outlet) so page-level errors trigger route navigation rather
than a full reload.
In `@web/src/components/layout/Sidebar.tsx`:
- Around line 193-205: The "Cmd+K" hint in Sidebar.tsx is macOS-specific; update
the UI in the button that calls openCommandPalette to show a platform-aware
shortcut using SIDEBAR_BUTTON_CLASS and the collapsed state: detect platform
(e.g., via navigator.platform or navigator.userAgent) to set a shortcutKey
variable (⌘ for Mac, Ctrl for others) and replace the static "Cmd+K to search"
span with "{shortcutKey}+K to search" while preserving the existing collapsed
conditional rendering and accessibility attributes.
In `@web/src/components/ui/animated-presence.stories.tsx`:
- Around line 60-63: The Default story currently sets args ({ routeKey: '/',
children: null }) but its render function ignores them and mounts TransitionDemo
directly; either remove the unused args from the Default export or update the
Default.render to accept story args and pass them into the component (e.g.,
change Default.render to ({ args }) => <TransitionDemo routeKey={args.routeKey}
children={args.children} />) so the story controls reflect the story state;
alternatively add a new simple story that directly uses the routeKey/children
args instead of TransitionDemo.
In `@web/src/components/ui/command-palette.tsx`:
- Around line 109-116: The command palette markup lacks a focus trap and modal
attributes; wrap the interactive panel (the <Command> component) in a
focus-trap/FocusScope (e.g., `@radix-ui/react-focus-scope` or focus-trap-react)
with trapping and restoreFocus enabled so keyboard focus cannot escape while
open, add role="dialog" and aria-modal="true" to the panel element, and keep the
existing backdrop onClick={close} and onKeyDown={handleScopeToggle} behavior
unchanged; update the component that renders the panel (search for the Command
component in this file) to include these accessibility props and the focus-trap
wrapper.
In `@web/src/components/ui/confirm-dialog.stories.tsx`:
- Around line 18-78: Add the missing Storybook states for ConfirmDialog by
exporting additional stories (e.g., Hover, Error, Empty) alongside the existing
Default, Destructive, Loading and Interactive exports: create a Hover story that
demonstrates the dialog/button hover state (either via Storybook play
interaction or a decorator that applies a hover/pseudo-class to the confirm
Button), an Error story that passes an error-like description/title or an error
prop if ConfirmDialog supports it to show validation/error UI, and an Empty
story that supplies empty title/description (or placeholder UI) to demonstrate
the empty-state visuals; use the same prop keys shown in the diff (open,
onOpenChange, title, description, variant, confirmLabel, loading, onConfirm) and
name any helper render function similarly to InteractiveDemo to keep stories
consistent.
In `@web/src/components/ui/confirm-dialog.tsx`:
- Around line 6-21: The interface ConfirmDialogProps is currently declared but
not exported; add the export keyword to make it reusable and type-safe for
consumers—i.e., change the declaration to export interface ConfirmDialogProps {
... } and update any local imports/usages to import the exported
ConfirmDialogProps where needed (e.g., in parent components or tests) to keep
types consistent.
- Around line 65-75: The confirm button is wrapped in AlertDialog.Action which
auto-closes the dialog before the async onConfirm completes; replace the
AlertDialog.Action usage and instead wire the Button's onClick to an async
handler that awaits onConfirm and only closes the dialog via the controlled
AlertDialog.Root open/onOpenChange state (manage a local setOpen state), keeping
existing props like confirmLabel, loading, variant, and Loader2; ensure errors
prevent closing by awaiting onConfirm inside a try/catch and only call
setOpen(false) after successful completion.
In `@web/src/components/ui/empty-state.tsx`:
- Around line 5-21: EmptyStateAction and EmptyStateProps are currently internal
only; export both interfaces so consumers can import the component's prop
contracts. Modify the declarations for EmptyStateAction and EmptyStateProps to
be exported (e.g., add the export keyword to the interface declarations),
keeping the same shape and comments so external callers can reference these
types when building actions or wrappers for the EmptyState component.
In `@web/src/components/ui/error-boundary.stories.tsx`:
- Around line 17-20: The BrokenComponent function contains unreachable dead code
after a throw; remove the trailing "return null" in BrokenComponent so the
function only throws (keep the throw line as-is) to clean up dead code and avoid
linter complaints related to unreachable code.
In `@web/src/components/ui/error-boundary.tsx`:
- Around line 105-110: The fallback button currently uses the default submit
behavior which can trigger form submission; update the button element that calls
resetErrorBoundary in the error boundary component to include type="button" so
clicking "Try Again" only invokes resetErrorBoundary and does not submit any
enclosing form.
In `@web/src/components/ui/skeleton.tsx`:
- Around line 3-8: The SkeletonProps interface is currently internal; export it
so consumers can import and extend the base props when building custom skeleton
variants. Modify the declaration of SkeletonProps to be exported (export
interface SkeletonProps { ... }) and ensure any uses in the file (e.g., the
Skeleton component type annotations) continue to reference the exported name so
external code can import SkeletonProps.
In `@web/src/components/ui/stagger-group.tsx`:
- Around line 36-44: The `containerVariants` object is recreated every render
even when `staggerDelay` is stable; wrap its creation in React's useMemo so it
only recomputes when `staggerDelay` changes. Locate the `containerVariants`
constant in stagger-group.tsx and replace its direct object literal with a
memoized value using useMemo(() => ({ hidden: {}, visible: { transition: {
staggerChildren: staggerDelay, delayChildren: 0 } } }), [staggerDelay]) so
re-renders don't recreate the variants unnecessarily.
In `@web/src/components/ui/toast.stories.tsx`:
- Around line 58-83: The AllVariants story defines unused args that are not
referenced in its render function; remove the unused args object from the
AllVariants export (i.e. delete the args: { toast: ..., onDismiss: ... } block)
or alternatively update the render to use those args, ensuring references to
AllVariants, render, args, and Toast are consistent; keep the story rendering
the four Toast instances and verify Storybook controls no longer show the stale
props.
In `@web/src/hooks/useCommandPalette.ts`:
- Around line 28-31: The module-level state (commandGroups, openState,
registrationCounter) needs a test-only reset helper so tests can fully clean up;
add and export a function named e.g. _reset() that clears commandGroups, resets
openState to false and sets registrationCounter back to 0, and update any
existing test-only exports (_setOpen, _commandGroups, _updateCommandsSnapshot)
to include this new _reset export so tests can call it between runs; locate the
variables commandGroups, openState, and registrationCounter in
useCommandPalette.ts and implement _reset to mutate those exact symbols.
- Around line 122-127: useRegisterCommands currently re-runs registerCommands
whenever the commands array reference changes (because of
useEffect([commands])), causing unnecessary unregister/register churn if callers
pass inline arrays; update useRegisterCommands to avoid re-registration by
adding a stable-content check (e.g., keep previous commands in a ref and compare
array length+items or use a shallow/deep equality helper) and only call
registerCommands when the content actually differs, or alternatively add a short
note in the hook docs that callers must memoize CommandItem[] (e.g., via
useMemo) to prevent repeated re-registration; reference the functions
useRegisterCommands and registerCommands for where to apply the change and the
useEffect dependency to modify.
In `@web/src/hooks/useStatusTransition.ts`:
- Around line 8-14: The STATUS_COLOR_MAP is typed too loosely as
Record<string,string>; tighten it by declaring a literal key union (e.g. type
StatusToken = 'success' | 'accent' | 'warning' | 'danger' | 'text-secondary')
and change STATUS_COLOR_MAP to Record<StatusToken, string> (or use a const
assertion with an inferred readonly record of those keys). Also update
getStatusColor to accept/return that StatusToken (or narrow its input param to
StatusToken) so the compiler will catch unmapped tokens and prevent drift
between allowed semantic tokens and the map.
In `@web/src/styles/design-tokens.css`:
- Around line 133-149: The status flash keyframes (`@keyframes` so-status-flash)
lacks reduced-motion handling; add a media query for prefers-reduced-motion:
reduce that targets the element(s) using the so-status-flash animation (or the
animation name itself) and either remove/override the animation (animation:
none) or make it instantaneous (animation-duration: 0s) so users who prefer
reduced motion do not see the flash; reference the `@keyframes` so-status-flash
and the CSS rule that applies the animation to update 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: e33a2e1e-00c0-485d-a54b-f2809faaa410
📒 Files selected for processing (43)
CLAUDE.mddocs/design/brand-and-ux.mdweb/src/__tests__/App.test.tsxweb/src/__tests__/components/ui/animated-presence.test.tsxweb/src/__tests__/components/ui/command-palette.test.tsxweb/src/__tests__/components/ui/confirm-dialog.test.tsxweb/src/__tests__/components/ui/empty-state.test.tsxweb/src/__tests__/components/ui/error-boundary.test.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxweb/src/__tests__/components/ui/skeleton.test.tsxweb/src/__tests__/components/ui/stagger-group.test.tsxweb/src/__tests__/components/ui/toast.test.tsxweb/src/__tests__/hooks/useCommandPalette.test.tsweb/src/__tests__/hooks/useFlash.test.tsweb/src/__tests__/hooks/useStatusTransition.test.tsweb/src/__tests__/lib/motion.test.tsweb/src/__tests__/stores/toast.test.tsweb/src/components/layout/AppLayout.tsxweb/src/components/layout/Sidebar.tsxweb/src/components/ui/animated-presence.stories.tsxweb/src/components/ui/animated-presence.tsxweb/src/components/ui/command-palette.stories.tsxweb/src/components/ui/command-palette.tsxweb/src/components/ui/confirm-dialog.stories.tsxweb/src/components/ui/confirm-dialog.tsxweb/src/components/ui/empty-state.stories.tsxweb/src/components/ui/empty-state.tsxweb/src/components/ui/error-boundary.stories.tsxweb/src/components/ui/error-boundary.tsxweb/src/components/ui/inline-edit.stories.tsxweb/src/components/ui/inline-edit.tsxweb/src/components/ui/skeleton.stories.tsxweb/src/components/ui/skeleton.tsxweb/src/components/ui/stagger-group.stories.tsxweb/src/components/ui/stagger-group.tsxweb/src/components/ui/toast.stories.tsxweb/src/components/ui/toast.tsxweb/src/hooks/useCommandPalette.tsweb/src/hooks/useFlash.tsweb/src/hooks/useStatusTransition.tsweb/src/lib/motion.tsweb/src/stores/toast.tsweb/src/styles/design-tokens.css
📜 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). (5)
- 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 (11)
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
web/src/**/*.{ts,tsx}: ALWAYS reuse existing components from web/src/components/ui/ before creating new ones: StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button.
Use Tailwind semantic classes (text-foreground, bg-card, text-accent, text-success, bg-danger, etc.) or CSS variables (var(--so-accent)). NEVER hardcode hex values in .tsx/.ts files.
Use font-sans or font-mono (maps to Geist tokens). NEVER set fontFamily directly.
Use density-aware 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 recreate status dots inline - use StatusBadge. Do NOT build card-with-header layouts from scratch - use SectionCard. Do NOT create metric displays with 'text-metric font-bold' - use MetricCard. Do NOT render initials circles manually - use Avatar. Do NOT create complex (>8 line) JSX inside .map() - extract to a shared component. Do NOT use rgba() with hardcoded values - use design token variables.
A PostToolUse hook (scripts/check_web_design_system.py) runs automatically on every Edit/Write to web/src/ files. It catches hardcoded hex colors, rgba values, fontFamily declarations, new components without Storybook stories, duplicate patterns, and complex .map() blocks. Fix all violations before proceeding - do not suppress or ignore hook output.
React 19 is required. Use React hooks patterns and functional components throughout.
Files:
web/src/__tests__/App.test.tsxweb/src/__tests__/components/ui/animated-presence.test.tsxweb/src/__tests__/components/ui/stagger-group.test.tsxweb/src/components/layout/Sidebar.tsxweb/src/__tests__/hooks/useStatusTransition.test.tsweb/src/__tests__/components/ui/command-palette.test.tsxweb/src/__tests__/components/ui/empty-state.test.tsxweb/src/__tests__/components/ui/error-boundary.test.tsxweb/src/components/ui/animated-presence.tsxweb/src/components/ui/animated-presence.stories.tsxweb/src/__tests__/hooks/useCommandPalette.test.tsweb/src/hooks/useStatusTransition.tsweb/src/__tests__/stores/toast.test.tsweb/src/components/ui/toast.stories.tsxweb/src/__tests__/components/ui/confirm-dialog.test.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxweb/src/__tests__/hooks/useFlash.test.tsweb/src/components/ui/stagger-group.stories.tsxweb/src/components/ui/error-boundary.stories.tsxweb/src/__tests__/components/ui/skeleton.test.tsxweb/src/__tests__/components/ui/toast.test.tsxweb/src/components/ui/empty-state.stories.tsxweb/src/components/ui/skeleton.stories.tsxweb/src/lib/motion.tsweb/src/components/ui/stagger-group.tsxweb/src/components/layout/AppLayout.tsxweb/src/components/ui/command-palette.tsxweb/src/components/ui/confirm-dialog.stories.tsxweb/src/components/ui/confirm-dialog.tsxweb/src/components/ui/empty-state.tsxweb/src/hooks/useFlash.tsweb/src/components/ui/command-palette.stories.tsxweb/src/components/ui/error-boundary.tsxweb/src/components/ui/inline-edit.stories.tsxweb/src/__tests__/lib/motion.test.tsweb/src/components/ui/inline-edit.tsxweb/src/stores/toast.tsweb/src/components/ui/toast.tsxweb/src/components/ui/skeleton.tsxweb/src/hooks/useCommandPalette.ts
**/*
📄 CodeRabbit inference engine (CLAUDE.md)
NEVER use 'cd' in Bash commands - the working directory is already set to the project root. Use absolute paths or run commands directly. Do NOT prefix commands with 'cd C:/Users/Aurelio/synthorg &&'.
Files:
web/src/__tests__/App.test.tsxweb/src/__tests__/components/ui/animated-presence.test.tsxweb/src/__tests__/components/ui/stagger-group.test.tsxweb/src/components/layout/Sidebar.tsxweb/src/__tests__/hooks/useStatusTransition.test.tsweb/src/__tests__/components/ui/command-palette.test.tsxweb/src/__tests__/components/ui/empty-state.test.tsxCLAUDE.mdweb/src/__tests__/components/ui/error-boundary.test.tsxweb/src/components/ui/animated-presence.tsxweb/src/styles/design-tokens.cssweb/src/components/ui/animated-presence.stories.tsxweb/src/__tests__/hooks/useCommandPalette.test.tsweb/src/hooks/useStatusTransition.tsweb/src/__tests__/stores/toast.test.tsweb/src/components/ui/toast.stories.tsxweb/src/__tests__/components/ui/confirm-dialog.test.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxdocs/design/brand-and-ux.mdweb/src/__tests__/hooks/useFlash.test.tsweb/src/components/ui/stagger-group.stories.tsxweb/src/components/ui/error-boundary.stories.tsxweb/src/__tests__/components/ui/skeleton.test.tsxweb/src/__tests__/components/ui/toast.test.tsxweb/src/components/ui/empty-state.stories.tsxweb/src/components/ui/skeleton.stories.tsxweb/src/lib/motion.tsweb/src/components/ui/stagger-group.tsxweb/src/components/layout/AppLayout.tsxweb/src/components/ui/command-palette.tsxweb/src/components/ui/confirm-dialog.stories.tsxweb/src/components/ui/confirm-dialog.tsxweb/src/components/ui/empty-state.tsxweb/src/hooks/useFlash.tsweb/src/components/ui/command-palette.stories.tsxweb/src/components/ui/error-boundary.tsxweb/src/components/ui/inline-edit.stories.tsxweb/src/__tests__/lib/motion.test.tsweb/src/components/ui/inline-edit.tsxweb/src/stores/toast.tsweb/src/components/ui/toast.tsxweb/src/components/ui/skeleton.tsxweb/src/hooks/useCommandPalette.ts
web/src/**/*.test.tsx
📄 CodeRabbit inference engine (CLAUDE.md)
Run Vitest with coverage scoped to files changed vs origin/main.
Files:
web/src/__tests__/App.test.tsxweb/src/__tests__/components/ui/animated-presence.test.tsxweb/src/__tests__/components/ui/stagger-group.test.tsxweb/src/__tests__/components/ui/command-palette.test.tsxweb/src/__tests__/components/ui/empty-state.test.tsxweb/src/__tests__/components/ui/error-boundary.test.tsxweb/src/__tests__/components/ui/confirm-dialog.test.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxweb/src/__tests__/components/ui/skeleton.test.tsxweb/src/__tests__/components/ui/toast.test.tsx
web/src/__tests__/**/*.test.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Use Vitest for unit and property tests. Mirror src/ directory structure. Use fast-check for property-based testing (fc.assert + fc.property).
Files:
web/src/__tests__/hooks/useStatusTransition.test.tsweb/src/__tests__/hooks/useCommandPalette.test.tsweb/src/__tests__/stores/toast.test.tsweb/src/__tests__/hooks/useFlash.test.tsweb/src/__tests__/lib/motion.test.ts
**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.md: Every implementation plan must be presented to the user for accept/deny before coding starts. At every phase of planning and implementation, be critical - actively look for ways to improve the design in the spirit of what we're building (robustness, correctness, simplicity, future-proofing). Surface improvements as suggestions, not silent changes - user decides.
Prioritize issues by dependency order, not priority labels - unblocked dependencies come first.
Files:
CLAUDE.mddocs/design/brand-and-ux.md
web/src/components/ui/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
When creating new shared components, place them in web/src/components/ui/ with descriptive kebab-case filename. Create a .stories.tsx file alongside with all states (default, hover, loading, error, empty). Export props as TypeScript interface. Use design tokens exclusively - no hardcoded colors, fonts, or spacing. Import cn from
@/lib/utilsfor conditional class merging.
Files:
web/src/components/ui/animated-presence.tsxweb/src/components/ui/animated-presence.stories.tsxweb/src/components/ui/toast.stories.tsxweb/src/components/ui/stagger-group.stories.tsxweb/src/components/ui/error-boundary.stories.tsxweb/src/components/ui/empty-state.stories.tsxweb/src/components/ui/skeleton.stories.tsxweb/src/components/ui/stagger-group.tsxweb/src/components/ui/command-palette.tsxweb/src/components/ui/confirm-dialog.stories.tsxweb/src/components/ui/confirm-dialog.tsxweb/src/components/ui/empty-state.tsxweb/src/components/ui/command-palette.stories.tsxweb/src/components/ui/error-boundary.tsxweb/src/components/ui/inline-edit.stories.tsxweb/src/components/ui/inline-edit.tsxweb/src/components/ui/toast.tsxweb/src/components/ui/skeleton.tsx
web/src/styles/**/*.css
📄 CodeRabbit inference engine (CLAUDE.md)
Define design tokens using --so-* CSS custom properties as a single source of truth. Bridge with Tailwind theme configuration.
Files:
web/src/styles/design-tokens.css
web/src/**/*.stories.tsx
📄 CodeRabbit inference engine (CLAUDE.md)
web/src/**/*.stories.tsx: Create .stories.tsx files for all new components in web/src/components/ui/ with all possible states (default, hover, loading, error, empty) to be used in Storybook.
Use Storybook 10 ESM syntax. Import test utilities from 'storybook/test' (not@storybook/test) and 'storybook/actions' (not@storybook/addon-actions). Storybook essentials (backgrounds, controls, viewport, actions, toolbars, measure, outline) and interactions are built into core storybook.
Files:
web/src/components/ui/animated-presence.stories.tsxweb/src/components/ui/toast.stories.tsxweb/src/components/ui/stagger-group.stories.tsxweb/src/components/ui/error-boundary.stories.tsxweb/src/components/ui/empty-state.stories.tsxweb/src/components/ui/skeleton.stories.tsxweb/src/components/ui/confirm-dialog.stories.tsxweb/src/components/ui/command-palette.stories.tsxweb/src/components/ui/inline-edit.stories.tsx
web/src/hooks/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Create custom React hooks for auth, login lockout, WebSocket, polling, and optimistic updates.
Files:
web/src/hooks/useStatusTransition.tsweb/src/hooks/useFlash.tsweb/src/hooks/useCommandPalette.ts
docs/design/**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
docs/design/**/*.md: ALWAYS read the relevant docs/design/ page before implementing any feature or planning any issue. The design spec is the starting point for architecture, data models, and behavior.
If implementation deviates from the spec (better approach found, scope evolved, etc.), alert the user and explain why - user decides whether to proceed or update the spec. Do NOT silently diverge - every deviation needs explicit user approval.
When approved deviations occur, update the relevant docs/design/ page to reflect the new reality.
Files:
docs/design/brand-and-ux.md
web/src/stores/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Use Zustand for global state management. Separate stores by domain (auth, WebSocket, domain shells).
Files:
web/src/stores/toast.ts
🧠 Learnings (26)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : When creating new shared components, place them in web/src/components/ui/ with descriptive kebab-case filename. Create a .stories.tsx file alongside with all states (default, hover, loading, error, empty). Export props as TypeScript interface. Use design tokens exclusively - no hardcoded colors, fonts, or spacing. Import cn from `@/lib/utils` for conditional class merging.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.stories.tsx : Use Storybook 10 ESM syntax. Import test utilities from 'storybook/test' (not storybook/test) and 'storybook/actions' (not storybook/addon-actions). Storybook essentials (backgrounds, controls, viewport, actions, toolbars, measure, outline) and interactions are built into core storybook.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : React 19 is required. Use React hooks patterns and functional components throughout.
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.stories.tsx : Use Storybook 10 ESM syntax. Import test utilities from 'storybook/test' (not storybook/test) and 'storybook/actions' (not storybook/addon-actions). Storybook essentials (backgrounds, controls, viewport, actions, toolbars, measure, outline) and interactions are built into core storybook.
Applied to files:
web/src/__tests__/App.test.tsxweb/src/__tests__/components/ui/animated-presence.test.tsxweb/src/__tests__/components/ui/stagger-group.test.tsxweb/src/__tests__/hooks/useStatusTransition.test.tsweb/src/__tests__/components/ui/command-palette.test.tsxweb/src/__tests__/components/ui/empty-state.test.tsxweb/src/__tests__/components/ui/error-boundary.test.tsxweb/src/components/ui/animated-presence.stories.tsxweb/src/__tests__/hooks/useCommandPalette.test.tsweb/src/__tests__/stores/toast.test.tsweb/src/components/ui/toast.stories.tsxweb/src/__tests__/components/ui/confirm-dialog.test.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxdocs/design/brand-and-ux.mdweb/src/__tests__/hooks/useFlash.test.tsweb/src/components/ui/stagger-group.stories.tsxweb/src/components/ui/error-boundary.stories.tsxweb/src/__tests__/components/ui/skeleton.test.tsxweb/src/__tests__/components/ui/toast.test.tsxweb/src/components/ui/empty-state.stories.tsxweb/src/components/ui/skeleton.stories.tsxweb/src/components/ui/confirm-dialog.stories.tsxweb/src/components/ui/command-palette.stories.tsxweb/src/components/ui/inline-edit.stories.tsxweb/src/__tests__/lib/motion.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__/App.test.tsxweb/src/__tests__/components/ui/animated-presence.test.tsxweb/src/__tests__/components/ui/stagger-group.test.tsxweb/src/__tests__/components/ui/command-palette.test.tsxweb/src/__tests__/components/ui/empty-state.test.tsxweb/src/__tests__/components/ui/error-boundary.test.tsxweb/src/__tests__/stores/toast.test.tsweb/src/__tests__/components/ui/confirm-dialog.test.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxweb/src/__tests__/hooks/useFlash.test.tsweb/src/__tests__/components/ui/skeleton.test.tsxweb/src/__tests__/components/ui/toast.test.tsxweb/src/__tests__/lib/motion.test.ts
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.stories.tsx : Create .stories.tsx files for all new components in web/src/components/ui/ with all possible states (default, hover, loading, error, empty) to be used in Storybook.
Applied to files:
web/src/__tests__/components/ui/animated-presence.test.tsxweb/src/__tests__/components/ui/stagger-group.test.tsxweb/src/__tests__/hooks/useStatusTransition.test.tsweb/src/__tests__/components/ui/empty-state.test.tsxweb/src/__tests__/components/ui/error-boundary.test.tsxweb/src/components/ui/animated-presence.stories.tsxweb/src/__tests__/stores/toast.test.tsweb/src/components/ui/toast.stories.tsxweb/src/__tests__/components/ui/confirm-dialog.test.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxweb/src/components/ui/stagger-group.stories.tsxweb/src/components/ui/error-boundary.stories.tsxweb/src/__tests__/components/ui/skeleton.test.tsxweb/src/__tests__/components/ui/toast.test.tsxweb/src/components/ui/empty-state.stories.tsxweb/src/components/ui/skeleton.stories.tsxweb/src/components/ui/stagger-group.tsxweb/src/components/ui/confirm-dialog.stories.tsxweb/src/components/ui/empty-state.tsxweb/src/components/ui/command-palette.stories.tsxweb/src/components/ui/inline-edit.stories.tsxweb/src/__tests__/lib/motion.test.tsweb/src/components/ui/skeleton.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : When creating new shared components, place them in web/src/components/ui/ with descriptive kebab-case filename. Create a .stories.tsx file alongside with all states (default, hover, loading, error, empty). Export props as TypeScript interface. Use design tokens exclusively - no hardcoded colors, fonts, or spacing. Import cn from `@/lib/utils` for conditional class merging.
Applied to files:
web/src/__tests__/components/ui/animated-presence.test.tsxweb/src/__tests__/components/ui/stagger-group.test.tsxweb/src/__tests__/components/ui/command-palette.test.tsxweb/src/__tests__/components/ui/empty-state.test.tsxCLAUDE.mdweb/src/__tests__/components/ui/error-boundary.test.tsxweb/src/components/ui/animated-presence.stories.tsxweb/src/components/ui/toast.stories.tsxweb/src/__tests__/components/ui/confirm-dialog.test.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxdocs/design/brand-and-ux.mdweb/src/components/ui/stagger-group.stories.tsxweb/src/components/ui/error-boundary.stories.tsxweb/src/__tests__/components/ui/skeleton.test.tsxweb/src/__tests__/components/ui/toast.test.tsxweb/src/components/ui/empty-state.stories.tsxweb/src/components/ui/skeleton.stories.tsxweb/src/components/ui/stagger-group.tsxweb/src/components/layout/AppLayout.tsxweb/src/components/ui/command-palette.tsxweb/src/components/ui/confirm-dialog.stories.tsxweb/src/components/ui/confirm-dialog.tsxweb/src/components/ui/empty-state.tsxweb/src/components/ui/command-palette.stories.tsxweb/src/components/ui/inline-edit.stories.tsxweb/src/components/ui/inline-edit.tsxweb/src/stores/toast.tsweb/src/components/ui/toast.tsxweb/src/components/ui/skeleton.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/__tests__/**/*.test.ts : Use Vitest for unit and property tests. Mirror src/ directory structure. Use fast-check for property-based testing (fc.assert + fc.property).
Applied to files:
web/src/__tests__/components/ui/animated-presence.test.tsxweb/src/__tests__/components/ui/stagger-group.test.tsxweb/src/__tests__/components/ui/command-palette.test.tsxweb/src/__tests__/components/ui/empty-state.test.tsxweb/src/__tests__/stores/toast.test.tsweb/src/__tests__/components/ui/confirm-dialog.test.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxweb/src/__tests__/hooks/useFlash.test.tsweb/src/__tests__/components/ui/skeleton.test.tsxweb/src/__tests__/components/ui/toast.test.tsxweb/src/__tests__/lib/motion.test.ts
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.test.tsx : Run Vitest with coverage scoped to files changed vs origin/main.
Applied to files:
web/src/__tests__/components/ui/stagger-group.test.tsxweb/src/__tests__/components/ui/command-palette.test.tsxweb/src/__tests__/components/ui/error-boundary.test.tsxweb/src/__tests__/components/ui/confirm-dialog.test.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxweb/src/__tests__/components/ui/skeleton.test.tsxweb/src/__tests__/components/ui/toast.test.tsxweb/src/__tests__/lib/motion.test.ts
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/hooks/**/*.ts : Create custom React hooks for auth, login lockout, WebSocket, polling, and optimistic updates.
Applied to files:
web/src/__tests__/hooks/useStatusTransition.test.tsCLAUDE.mdweb/src/__tests__/hooks/useCommandPalette.test.tsweb/src/hooks/useStatusTransition.tsdocs/design/brand-and-ux.mdweb/src/__tests__/hooks/useFlash.test.tsweb/src/hooks/useFlash.tsweb/src/hooks/useCommandPalette.ts
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : A PostToolUse hook (scripts/check_web_design_system.py) runs automatically on every Edit/Write to web/src/ files. It catches hardcoded hex colors, rgba values, fontFamily declarations, new components without Storybook stories, duplicate patterns, and complex .map() blocks. Fix all violations before proceeding - do not suppress or ignore hook output.
Applied to files:
web/src/__tests__/hooks/useStatusTransition.test.tsCLAUDE.mdweb/src/__tests__/hooks/useCommandPalette.test.tsweb/src/hooks/useStatusTransition.tsweb/src/components/ui/toast.stories.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxdocs/design/brand-and-ux.mdweb/src/__tests__/hooks/useFlash.test.tsweb/src/hooks/useFlash.tsweb/src/components/ui/inline-edit.stories.tsxweb/src/components/ui/inline-edit.tsxweb/src/hooks/useCommandPalette.ts
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : React 19 is required. Use React hooks patterns and functional components throughout.
Applied to files:
web/src/__tests__/hooks/useStatusTransition.test.tsCLAUDE.mdweb/src/hooks/useStatusTransition.tsdocs/design/brand-and-ux.mdweb/src/__tests__/hooks/useFlash.test.tsweb/src/components/layout/AppLayout.tsxweb/src/components/ui/confirm-dialog.tsxweb/src/components/ui/empty-state.tsxweb/src/hooks/useFlash.tsweb/src/components/ui/error-boundary.tsxweb/src/components/ui/inline-edit.tsxweb/src/components/ui/skeleton.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do NOT recreate status dots inline - use StatusBadge. Do NOT build card-with-header layouts from scratch - use SectionCard. Do NOT create metric displays with 'text-metric font-bold' - use MetricCard. Do NOT render initials circles manually - use Avatar. Do NOT create complex (>8 line) JSX inside .map() - extract to a shared component. Do NOT use rgba() with hardcoded values - use design token variables.
Applied to files:
CLAUDE.mdweb/src/styles/design-tokens.cssweb/src/hooks/useStatusTransition.tsdocs/design/brand-and-ux.mdweb/src/components/layout/AppLayout.tsxweb/src/components/ui/empty-state.tsxweb/src/hooks/useFlash.tsweb/src/components/ui/inline-edit.tsxweb/src/components/ui/toast.tsxweb/src/components/ui/skeleton.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : ALWAYS reuse existing components from web/src/components/ui/ before creating new ones: StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button.
Applied to files:
CLAUDE.mddocs/design/brand-and-ux.mdweb/src/components/ui/stagger-group.tsxweb/src/components/ui/empty-state.tsxweb/src/components/ui/inline-edit.tsxweb/src/components/ui/skeleton.tsx
📚 Learning: 2026-03-15T18:17:43.675Z
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).
Applied to files:
CLAUDE.md
📚 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 web/package.json : Web dashboard Node.js 20+; dependencies in web/package.json (Vue 3, PrimeVue, Tailwind CSS, Pinia, VueFlow, ECharts, Axios, vue-draggable-plus, Vitest, ESLint, vue-tsc)
Applied to files:
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:
CLAUDE.mddocs/design/brand-and-ux.md
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/stores/**/*.ts : Use Zustand for global state management. Separate stores by domain (auth, WebSocket, domain shells).
Applied to files:
CLAUDE.mdweb/src/__tests__/stores/toast.test.tsweb/src/stores/toast.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:
CLAUDE.md
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/pages/**/*.tsx : Pages should be lazy-loaded with one page per route using React Router.
Applied to files:
CLAUDE.mdweb/src/components/layout/AppLayout.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : 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:
CLAUDE.mddocs/design/brand-and-ux.mdweb/src/components/ui/skeleton.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use Tailwind semantic classes (text-foreground, bg-card, text-accent, text-success, bg-danger, etc.) or CSS variables (var(--so-accent)). NEVER hardcode hex values in .tsx/.ts files.
Applied to files:
CLAUDE.mdweb/src/hooks/useStatusTransition.tsdocs/design/brand-and-ux.mdweb/src/components/ui/skeleton.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/styles/**/*.css : Define design tokens using --so-* CSS custom properties as a single source of truth. Bridge with Tailwind theme configuration.
Applied to files:
CLAUDE.mdweb/src/styles/design-tokens.css
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use token variables (var(--so-shadow-card-hover), border-border, border-bright) for shadows and borders.
Applied to files:
CLAUDE.mdweb/src/styles/design-tokens.cssweb/src/hooks/useStatusTransition.tsweb/src/components/ui/skeleton.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/.storybook/main.ts : Use 'defineMain' from 'storybook/react-vite/node' and include explicit 'framework' field for type-safe Storybook configuration.
Applied to files:
web/src/components/ui/animated-presence.stories.tsxweb/src/components/ui/toast.stories.tsxweb/src/components/ui/error-boundary.stories.tsxweb/src/components/ui/empty-state.stories.tsxweb/src/components/ui/skeleton.stories.tsxweb/src/components/ui/confirm-dialog.stories.tsxweb/src/components/ui/command-palette.stories.tsxweb/src/components/ui/inline-edit.stories.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/.storybook/preview.tsx : Use 'definePreview' from 'storybook/react-vite' for type-safe preview configuration. Use 'parameters.backgrounds.options' (object keyed by name) + 'initialGlobals.backgrounds.value' for backgrounds API. Set 'parameters.a11y.test: "error" | "todo" | "off"' globally to enforce WCAG compliance on all stories.
Applied to files:
web/src/components/ui/animated-presence.stories.tsxweb/src/components/ui/toast.stories.tsxdocs/design/brand-and-ux.mdweb/src/components/ui/stagger-group.stories.tsxweb/src/components/ui/error-boundary.stories.tsxweb/src/components/ui/confirm-dialog.stories.tsxweb/src/components/ui/command-palette.stories.tsxweb/src/components/ui/inline-edit.stories.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to docs/design/**/*.md : If implementation deviates from the spec (better approach found, scope evolved, etc.), alert the user and explain why - user decides whether to proceed or update the spec. Do NOT silently diverge - every deviation needs explicit user approval.
Applied to files:
docs/design/brand-and-ux.md
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to docs/design/**/*.md : When approved deviations occur, update the relevant docs/design/ page to reflect the new reality.
Applied to files:
docs/design/brand-and-ux.md
| describe('StaggerGroup', () => { | ||
| it('renders all children', () => { | ||
| render( | ||
| <StaggerGroup> | ||
| <StaggerItem>Card 1</StaggerItem> | ||
| <StaggerItem>Card 2</StaggerItem> | ||
| <StaggerItem>Card 3</StaggerItem> | ||
| </StaggerGroup>, | ||
| ) | ||
| expect(screen.getByText('Card 1')).toBeInTheDocument() | ||
| expect(screen.getByText('Card 2')).toBeInTheDocument() | ||
| expect(screen.getByText('Card 3')).toBeInTheDocument() | ||
| }) | ||
|
|
||
| it('applies className to StaggerGroup wrapper', () => { | ||
| const { container } = render( | ||
| <StaggerGroup className="grid grid-cols-3"> | ||
| <StaggerItem>Card</StaggerItem> | ||
| </StaggerGroup>, | ||
| ) | ||
| expect(container.firstChild).toHaveClass('grid', 'grid-cols-3') | ||
| }) | ||
|
|
||
| it('applies className to StaggerItem', () => { | ||
| render( | ||
| <StaggerGroup> | ||
| <StaggerItem className="custom-item" data-testid="item"> | ||
| Card | ||
| </StaggerItem> | ||
| </StaggerGroup>, | ||
| ) | ||
| expect(screen.getByTestId('item')).toHaveClass('custom-item') | ||
| }) | ||
|
|
||
| it('renders correctly with no children', () => { | ||
| const { container } = render(<StaggerGroup />) | ||
| expect(container.firstChild).toBeInTheDocument() | ||
| }) | ||
| }) |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider adding tests for animation-specific props.
The current tests cover basic rendering and className application. For completeness, consider adding tests for:
animate={false}disabling animationslayoutIdandlayoutprops on StaggerItem- Different
staggerDelayvalues
These are optional since the core functionality is verified.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/__tests__/components/ui/stagger-group.test.tsx` around lines 28 - 66,
Add unit tests that exercise animation-related props on StaggerGroup and
StaggerItem: render StaggerGroup with animate={false} and assert children are
rendered but no animation-related classes/attributes or motion elements are
present; render StaggerItem with layoutId and layout props and assert those
attributes/props are present on the DOM/motion element; and render StaggerGroup
with different staggerDelay values and assert timing-related
classes/inline-styles or computed animation-delay values reflect the provided
delay. Use the existing test helpers (render, screen, container) and reference
the StaggerGroup and StaggerItem components when adding these assertions.
| export const AllVariants: Story = { | ||
| render: () => ( | ||
| <div className="flex flex-col gap-2"> | ||
| <Toast | ||
| toast={{ id: '1', variant: 'success', title: 'Changes saved' }} | ||
| onDismiss={() => {}} | ||
| /> | ||
| <Toast | ||
| toast={{ id: '2', variant: 'error', title: 'Failed to save' }} | ||
| onDismiss={() => {}} | ||
| /> | ||
| <Toast | ||
| toast={{ id: '3', variant: 'warning', title: 'Budget nearing limit' }} | ||
| onDismiss={() => {}} | ||
| /> | ||
| <Toast | ||
| toast={{ id: '4', variant: 'info', title: 'Agent started' }} | ||
| onDismiss={() => {}} | ||
| /> | ||
| </div> | ||
| ), | ||
| args: { | ||
| toast: { id: '1', variant: 'success', title: 'All variants' }, | ||
| onDismiss: () => {}, | ||
| }, | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
AllVariants story has unused args.
The AllVariants story defines args (lines 79-82) that aren't used by the render function. This could confuse users viewing the Storybook controls panel. Consider removing the unused args.
💡 Optional: Remove unused args from AllVariants
export const AllVariants: Story = {
render: () => (
<div className="flex flex-col gap-2">
<Toast
toast={{ id: '1', variant: 'success', title: 'Changes saved' }}
onDismiss={() => {}}
/>
<Toast
toast={{ id: '2', variant: 'error', title: 'Failed to save' }}
onDismiss={() => {}}
/>
<Toast
toast={{ id: '3', variant: 'warning', title: 'Budget nearing limit' }}
onDismiss={() => {}}
/>
<Toast
toast={{ id: '4', variant: 'info', title: 'Agent started' }}
onDismiss={() => {}}
/>
</div>
),
- args: {
- toast: { id: '1', variant: 'success', title: 'All variants' },
- onDismiss: () => {},
- },
}📝 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.
| export const AllVariants: Story = { | |
| render: () => ( | |
| <div className="flex flex-col gap-2"> | |
| <Toast | |
| toast={{ id: '1', variant: 'success', title: 'Changes saved' }} | |
| onDismiss={() => {}} | |
| /> | |
| <Toast | |
| toast={{ id: '2', variant: 'error', title: 'Failed to save' }} | |
| onDismiss={() => {}} | |
| /> | |
| <Toast | |
| toast={{ id: '3', variant: 'warning', title: 'Budget nearing limit' }} | |
| onDismiss={() => {}} | |
| /> | |
| <Toast | |
| toast={{ id: '4', variant: 'info', title: 'Agent started' }} | |
| onDismiss={() => {}} | |
| /> | |
| </div> | |
| ), | |
| args: { | |
| toast: { id: '1', variant: 'success', title: 'All variants' }, | |
| onDismiss: () => {}, | |
| }, | |
| } | |
| export const AllVariants: Story = { | |
| render: () => ( | |
| <div className="flex flex-col gap-2"> | |
| <Toast | |
| toast={{ id: '1', variant: 'success', title: 'Changes saved' }} | |
| onDismiss={() => {}} | |
| /> | |
| <Toast | |
| toast={{ id: '2', variant: 'error', title: 'Failed to save' }} | |
| onDismiss={() => {}} | |
| /> | |
| <Toast | |
| toast={{ id: '3', variant: 'warning', title: 'Budget nearing limit' }} | |
| onDismiss={() => {}} | |
| /> | |
| <Toast | |
| toast={{ id: '4', variant: 'info', title: 'Agent started' }} | |
| onDismiss={() => {}} | |
| /> | |
| </div> | |
| ), | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/components/ui/toast.stories.tsx` around lines 58 - 83, The
AllVariants story defines unused args that are not referenced in its render
function; remove the unused args object from the AllVariants export (i.e. delete
the args: { toast: ..., onDismiss: ... } block) or alternatively update the
render to use those args, ensuring references to AllVariants, render, args, and
Toast are consistent; keep the story rendering the four Toast instances and
verify Storybook controls no longer show the stale props.
| export function useRegisterCommands(commands: CommandItem[]) { | ||
| useEffect(() => { | ||
| const cleanup = registerCommands(commands) | ||
| return cleanup | ||
| }, [commands]) | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Potential re-registration on every render if commands is not memoized.
The useEffect dependency on commands means if the caller passes a new array reference on each render (e.g., inline [{ ... }]), this will unregister and re-register commands repeatedly.
Consider documenting this requirement or adding a ref-based comparison to skip re-registration when the array content is unchanged.
📚 Documentation suggestion
/**
* Hook that registers commands on mount and cleans up on unmount.
+ *
+ * Note: `commands` should be memoized (e.g., via useMemo or module-level constant)
+ * to avoid re-registration on every render.
*/
export function useRegisterCommands(commands: CommandItem[]) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/hooks/useCommandPalette.ts` around lines 122 - 127,
useRegisterCommands currently re-runs registerCommands whenever the commands
array reference changes (because of useEffect([commands])), causing unnecessary
unregister/register churn if callers pass inline arrays; update
useRegisterCommands to avoid re-registration by adding a stable-content check
(e.g., keep previous commands in a ref and compare array length+items or use a
shallow/deep equality helper) and only call registerCommands when the content
actually differs, or alternatively add a short note in the hook docs that
callers must memoize CommandItem[] (e.g., via useMemo) to prevent repeated
re-registration; reference the functions useRegisterCommands and
registerCommands for where to apply the change and the useEffect dependency to
modify.
5891f8d to
a3d21c4
Compare
There was a problem hiding this comment.
Actionable comments posted: 15
♻️ Duplicate comments (2)
web/src/__tests__/components/ui/skeleton.test.tsx (1)
35-36:⚠️ Potential issue | 🟡 MinorUse stable test selectors instead of Tailwind class selectors.
Counting elements via
.rounded/.h-3couples tests to presentation classes and makes them fragile on harmless style refactors. Prefer semantic selectors (e.g.,data-*attributes or role/text-based queries) for line/cell counting.Also applies to: 41-42, 47-48, 57-58, 66-67, 76-77, 86-87, 94-95
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/__tests__/components/ui/skeleton.test.tsx` around lines 35 - 36, Tests in web/src/__tests__/components/ui/skeleton.test.tsx are currently querying presentational Tailwind classes (e.g., container.querySelectorAll('.rounded') and '.h-3') which is fragile; change the Skeleton component to render a stable attribute (for example data-testid="skeleton-line" or data-skeleton-line) on each line/cell and update the assertions in the tests to query those stable selectors (e.g., getAllByTestId('skeleton-line') or container.querySelectorAll('[data-skeleton-line]') and keep the existing expect(...).toHaveLength(...) assertions); replace every occurrence of the '.rounded' and '.h-3' queries in this file with the new data-* selector (also update the other similar assertions referenced in the comment).web/src/components/ui/toast.stories.tsx (1)
99-105: 🧹 Nitpick | 🔵 TrivialStacked story also has unused args.
Similar to
AllVariants, theStackedstory definesargsthat are ignored by the customrenderfunction. Remove or use them for consistency.💡 Proposed fix
export const Stacked: Story = { render: () => <StackedDemo />, - args: { - toast: { id: '1', variant: 'success', title: 'Stacked' }, - onDismiss: () => {}, - }, parameters: { layout: 'fullscreen' }, }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/components/ui/toast.stories.tsx` around lines 99 - 105, The Stacked story defines unused args that are ignored by its custom render; either remove the unused args block or pass them into the custom renderer. Update the export const Stacked: Story so that its args (currently { toast: { id: '1', variant: 'success', title: 'Stacked' }, onDismiss: () => {} }) are removed, OR change render: () => <StackedDemo /> to render: (props) => <StackedDemo {...props} /> (and ensure StackedDemo accepts and forwards toast and onDismiss), referencing the Stacked story and StackedDemo component 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__/components/ui/command-palette.test.tsx`:
- Around line 35-49: Tests currently couple to internal APIs by directly
mutating _commandGroups and calling _updateCommandsSnapshot; instead, stop
touching those internals and use the public command registration API (or add one
if missing). Replace setupCommands so it registers commands through the module's
exported public function (e.g., a registerCommands/registerCommandGroup or
addCommand/addCommands API) or, if no suitable public API exists, add and export
a small test-friendly wrapper (e.g., export function registerCommandGroup(key,
commands)) and use that from the test; ensure makeCommand remains usable but do
not reference _commandGroups or _updateCommandsSnapshot directly.
In `@web/src/__tests__/components/ui/inline-edit.test.tsx`:
- Around line 107-122: Tests for InlineEdit use inconsistent element selection;
in the 'saves on blur' test replace screen.getByRole('button') with the same
text-based selector used earlier (e.g., screen.getByText('original') to click
the displayed value) so the test 'saves on blur' matches the selection strategy
used in other tests for InlineEdit and improves readability and maintainability.
In `@web/src/components/layout/Sidebar.tsx`:
- Around line 196-207: The tooltip title "Search (Cmd+K)" is hardcoded and
conflicts with the platform-aware visible hint; update Sidebar.tsx so the title
uses the same platform check as the visible span (the navigator.platform test)
or compute a single shortcutLabel (e.g., in a const) and use it in both the
title prop and the visible text to ensure consistency for keyboard users;
reference the title prop on the surrounding element and the span that currently
renders the conditional '⌘' or 'Ctrl' so both use the same computed
shortcutLabel.
In `@web/src/components/ui/animated-presence.stories.tsx`:
- Around line 43-46: The active-state class hardcodes a white foreground
("text-white") which breaks theme tokens; in the className template where the
ternary checks `i === index` (the active branch that currently uses 'bg-accent
text-white'), replace the hardcoded 'text-white' with the semantic foreground
token used with accent backgrounds (e.g. 'text-accent-foreground') so the active
state pairs `bg-accent` with the matching foreground token and remains correct
across themes.
In `@web/src/components/ui/animated-presence.tsx`:
- Around line 6-11: AnimatedPresenceProps is currently declared but not
exported; export the interface so downstream consumers can import and reuse its
type. Update the declaration for AnimatedPresenceProps to be exported (export
interface AnimatedPresenceProps { ... }) and ensure any references to this type
(e.g., props of the AnimatedPresence component) continue to work with the
exported name.
In `@web/src/components/ui/command-palette.stories.tsx`:
- Around line 1-14: Add a new Storybook story demonstrating the CommandPalette's
loading and error UI by using the existing PaletteSetup helper and the
CommandPalette component: simulate an async command fetch (or an error) via the
useCommandPalette hook (or by dispatching through _setOpen/registered commands)
to show the intermediate loading state and an error state; ensure the story uses
PaletteSetup to register a promise-based command source that first delays (to
show loading) and then rejects (to show the error) so reviewers can see both
states rendered in the CommandPalette component.
In `@web/src/components/ui/command-palette.tsx`:
- Around line 88-93: recentItems currently maps recentIds over all commands (in
the useMemo that depends on search, recentIds, commands) so global recents show
in local scope and duplicates appear in grouped render; update the recentItems
computation to (1) short-circuit when search is non-empty as before, (2) only
include commands that match the current active scope (e.g., filter by
command.scope or a helper like command.isInScope(activeScope) before mapping
recentIds → commands), and (3) build a Set of recent command ids and use it when
rendering the grouped lists (the other grouped render around lines ~176-184) to
filter out any command whose id is in recentItems so items aren’t duplicated.
Ensure you reference recentItems, recentIds, commands and the grouped list
render when applying these filters.
- Around line 37-39: The CommandPaletteProps interface is currently file-local;
export it so callers can import the props type. Update the declaration of
CommandPaletteProps to be exported (export interface CommandPaletteProps { ...
}) in the command-palette.tsx component and keep the existing optional className
field; ensure any local usages (e.g., the CommandPalette component prop
annotation) continue to reference the now-exported CommandPaletteProps.
- Around line 110-119: handleScopeToggle currently prevents default and flips
scope on Tab even when no local commands exist; change handleScopeToggle to
first check whether any local commands exist (using the existing commands array
or hasLocalCommands derived from it) and only call e.preventDefault() and
setScope(...) when a local scope actually exists to switch to; otherwise leave
the Tab key alone. Reference: handleScopeToggle, setScope, commands (or
hasLocalCommands).
In `@web/src/components/ui/confirm-dialog.stories.tsx`:
- Around line 70-78: The Interactive story defines unused args (open,
onOpenChange, title, onConfirm) that are ignored because the story uses a custom
render function returning <InteractiveDemo />; remove the redundant args object
from the Interactive export or move any needed props into the InteractiveDemo
usage in the render function (update the Interactive export and/or the
InteractiveDemo invocation) so props are actually applied; look for the
Interactive story symbol and its render: () => <InteractiveDemo /> to make the
change.
In `@web/src/components/ui/error-boundary.stories.tsx`:
- Around line 53-62: The story's custom fallback and interactive demo currently
use hand-rolled <button> elements; update both places (the fallback render
passed to ErrorBoundary and the interactive demo at the other instance) to
import and use the shared Button component from web/src/components/ui/Button
instead of raw <button>, passing the same onClick handler (resetErrorBoundary or
the demo handler) and equivalent className/props to preserve spacing and visual
intent so the story exercises the real shared control states; ensure you remove
duplicated styling and rely on Button props for appearance and accessibility.
In `@web/src/components/ui/error-boundary.tsx`:
- Around line 15-24: ErrorBoundaryProps is not exported which prevents consumers
from typing wrappers or extensions; export the interface by adding an export
modifier to ErrorBoundaryProps so it becomes available to consumers (keep the
existing fields: children: ReactNode, fallback?:
React.ComponentType<ErrorFallbackProps>, onReset?: () => void, level?:
ErrorLevel, className?: string) and ensure ErrorFallbackProps remains exported
to preserve the dependency between types used by ErrorBoundary and any consumer
components or wrappers like ErrorBoundary.
In `@web/src/components/ui/stagger-group.tsx`:
- Around line 6-23: The StaggerGroupProps and StaggerItemProps interfaces are
currently internal; export them so consumers and Storybook can type against
them. Modify the declarations for StaggerGroupProps and StaggerItemProps to be
exported (e.g., add the export keyword to the interface declarations), and
ensure any local references (StaggerGroup, StaggerItem components) continue to
use the now-exported types without other changes.
In `@web/src/hooks/useFlash.ts`:
- Around line 62-68: The inline flashStyle currently sets an animation string
which bypasses users' prefers-reduced-motion settings; update useFlash to
respect reduced motion by checking window.matchMedia('(prefers-reduced-motion:
reduce)') (or a platform motion hook) and when it matches, do not set the inline
animation — return an empty flashStyle; alternatively move the animation
entirely into CSS (using the so-status-flash class) and only use flashClassName
for triggering so the stylesheet's prefers-reduced-motion rules can suppress the
animation; refer to the flashing, flashStyle and totalMs logic in useFlash to
implement this conditional behavior.
- Around line 36-60: The flash doesn't restart on repeated triggers because
setFlashing(true) is a no-op when flashing is already true; update triggerFlash
(in useFlash) to force a restart by briefly clearing the mounted state before
re-applying it: when called, clear any existing timerRef, if flashing is true
first setFlashing(false) and schedule setFlashing(true) on the next tick (e.g.,
via setTimeout 0) so the DOM/CSS class is removed and re-added to restart the
animation, then set timerRef to a timeout that clears flashing after totalMs;
ensure you still clear timerRef and pending restart timeouts to avoid leaks.
---
Duplicate comments:
In `@web/src/__tests__/components/ui/skeleton.test.tsx`:
- Around line 35-36: Tests in web/src/__tests__/components/ui/skeleton.test.tsx
are currently querying presentational Tailwind classes (e.g.,
container.querySelectorAll('.rounded') and '.h-3') which is fragile; change the
Skeleton component to render a stable attribute (for example
data-testid="skeleton-line" or data-skeleton-line) on each line/cell and update
the assertions in the tests to query those stable selectors (e.g.,
getAllByTestId('skeleton-line') or
container.querySelectorAll('[data-skeleton-line]') and keep the existing
expect(...).toHaveLength(...) assertions); replace every occurrence of the
'.rounded' and '.h-3' queries in this file with the new data-* selector (also
update the other similar assertions referenced in the comment).
In `@web/src/components/ui/toast.stories.tsx`:
- Around line 99-105: The Stacked story defines unused args that are ignored by
its custom render; either remove the unused args block or pass them into the
custom renderer. Update the export const Stacked: Story so that its args
(currently { toast: { id: '1', variant: 'success', title: 'Stacked' },
onDismiss: () => {} }) are removed, OR change render: () => <StackedDemo /> to
render: (props) => <StackedDemo {...props} /> (and ensure StackedDemo accepts
and forwards toast and onDismiss), referencing the Stacked story and StackedDemo
component 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: f5b03667-8482-402c-aabb-a661c667fe92
📒 Files selected for processing (43)
CLAUDE.mddocs/design/brand-and-ux.mdweb/src/__tests__/App.test.tsxweb/src/__tests__/components/ui/animated-presence.test.tsxweb/src/__tests__/components/ui/command-palette.test.tsxweb/src/__tests__/components/ui/confirm-dialog.test.tsxweb/src/__tests__/components/ui/empty-state.test.tsxweb/src/__tests__/components/ui/error-boundary.test.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxweb/src/__tests__/components/ui/skeleton.test.tsxweb/src/__tests__/components/ui/stagger-group.test.tsxweb/src/__tests__/components/ui/toast.test.tsxweb/src/__tests__/hooks/useCommandPalette.test.tsweb/src/__tests__/hooks/useFlash.test.tsweb/src/__tests__/hooks/useStatusTransition.test.tsweb/src/__tests__/lib/motion.test.tsweb/src/__tests__/stores/toast.test.tsweb/src/components/layout/AppLayout.tsxweb/src/components/layout/Sidebar.tsxweb/src/components/ui/animated-presence.stories.tsxweb/src/components/ui/animated-presence.tsxweb/src/components/ui/command-palette.stories.tsxweb/src/components/ui/command-palette.tsxweb/src/components/ui/confirm-dialog.stories.tsxweb/src/components/ui/confirm-dialog.tsxweb/src/components/ui/empty-state.stories.tsxweb/src/components/ui/empty-state.tsxweb/src/components/ui/error-boundary.stories.tsxweb/src/components/ui/error-boundary.tsxweb/src/components/ui/inline-edit.stories.tsxweb/src/components/ui/inline-edit.tsxweb/src/components/ui/skeleton.stories.tsxweb/src/components/ui/skeleton.tsxweb/src/components/ui/stagger-group.stories.tsxweb/src/components/ui/stagger-group.tsxweb/src/components/ui/toast.stories.tsxweb/src/components/ui/toast.tsxweb/src/hooks/useCommandPalette.tsweb/src/hooks/useFlash.tsweb/src/hooks/useStatusTransition.tsweb/src/lib/motion.tsweb/src/stores/toast.tsweb/src/styles/design-tokens.css
📜 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). (5)
- 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 (11)
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
web/src/**/*.{ts,tsx}: ALWAYS reuse existing components from web/src/components/ui/ before creating new ones: StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button.
Use Tailwind semantic classes (text-foreground, bg-card, text-accent, text-success, bg-danger, etc.) or CSS variables (var(--so-accent)). NEVER hardcode hex values in .tsx/.ts files.
Use font-sans or font-mono (maps to Geist tokens). NEVER set fontFamily directly.
Use density-aware 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 recreate status dots inline - use StatusBadge. Do NOT build card-with-header layouts from scratch - use SectionCard. Do NOT create metric displays with 'text-metric font-bold' - use MetricCard. Do NOT render initials circles manually - use Avatar. Do NOT create complex (>8 line) JSX inside .map() - extract to a shared component. Do NOT use rgba() with hardcoded values - use design token variables.
A PostToolUse hook (scripts/check_web_design_system.py) runs automatically on every Edit/Write to web/src/ files. It catches hardcoded hex colors, rgba values, fontFamily declarations, new components without Storybook stories, duplicate patterns, and complex .map() blocks. Fix all violations before proceeding - do not suppress or ignore hook output.
React 19 is required. Use React hooks patterns and functional components throughout.
Files:
web/src/__tests__/components/ui/skeleton.test.tsxweb/src/__tests__/components/ui/stagger-group.test.tsxweb/src/__tests__/components/ui/animated-presence.test.tsxweb/src/components/layout/Sidebar.tsxweb/src/__tests__/hooks/useStatusTransition.test.tsweb/src/components/ui/toast.stories.tsxweb/src/__tests__/components/ui/empty-state.test.tsxweb/src/__tests__/components/ui/toast.test.tsxweb/src/__tests__/hooks/useCommandPalette.test.tsweb/src/__tests__/components/ui/error-boundary.test.tsxweb/src/hooks/useStatusTransition.tsweb/src/components/ui/animated-presence.tsxweb/src/__tests__/hooks/useFlash.test.tsweb/src/__tests__/components/ui/command-palette.test.tsxweb/src/components/ui/error-boundary.stories.tsxweb/src/__tests__/components/ui/confirm-dialog.test.tsxweb/src/hooks/useFlash.tsweb/src/components/layout/AppLayout.tsxweb/src/components/ui/animated-presence.stories.tsxweb/src/__tests__/App.test.tsxweb/src/components/ui/skeleton.stories.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxweb/src/components/ui/empty-state.tsxweb/src/components/ui/stagger-group.stories.tsxweb/src/components/ui/empty-state.stories.tsxweb/src/components/ui/command-palette.tsxweb/src/components/ui/confirm-dialog.stories.tsxweb/src/__tests__/stores/toast.test.tsweb/src/components/ui/confirm-dialog.tsxweb/src/components/ui/toast.tsxweb/src/__tests__/lib/motion.test.tsweb/src/components/ui/inline-edit.stories.tsxweb/src/lib/motion.tsweb/src/components/ui/command-palette.stories.tsxweb/src/stores/toast.tsweb/src/components/ui/error-boundary.tsxweb/src/components/ui/inline-edit.tsxweb/src/hooks/useCommandPalette.tsweb/src/components/ui/skeleton.tsxweb/src/components/ui/stagger-group.tsx
**/*
📄 CodeRabbit inference engine (CLAUDE.md)
NEVER use 'cd' in Bash commands - the working directory is already set to the project root. Use absolute paths or run commands directly. Do NOT prefix commands with 'cd C:/Users/Aurelio/synthorg &&'.
Files:
web/src/__tests__/components/ui/skeleton.test.tsxweb/src/__tests__/components/ui/stagger-group.test.tsxweb/src/__tests__/components/ui/animated-presence.test.tsxweb/src/components/layout/Sidebar.tsxweb/src/__tests__/hooks/useStatusTransition.test.tsweb/src/components/ui/toast.stories.tsxweb/src/__tests__/components/ui/empty-state.test.tsxCLAUDE.mdweb/src/__tests__/components/ui/toast.test.tsxweb/src/__tests__/hooks/useCommandPalette.test.tsweb/src/__tests__/components/ui/error-boundary.test.tsxweb/src/hooks/useStatusTransition.tsweb/src/components/ui/animated-presence.tsxweb/src/__tests__/hooks/useFlash.test.tsweb/src/__tests__/components/ui/command-palette.test.tsxweb/src/components/ui/error-boundary.stories.tsxdocs/design/brand-and-ux.mdweb/src/__tests__/components/ui/confirm-dialog.test.tsxweb/src/styles/design-tokens.cssweb/src/hooks/useFlash.tsweb/src/components/layout/AppLayout.tsxweb/src/components/ui/animated-presence.stories.tsxweb/src/__tests__/App.test.tsxweb/src/components/ui/skeleton.stories.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxweb/src/components/ui/empty-state.tsxweb/src/components/ui/stagger-group.stories.tsxweb/src/components/ui/empty-state.stories.tsxweb/src/components/ui/command-palette.tsxweb/src/components/ui/confirm-dialog.stories.tsxweb/src/__tests__/stores/toast.test.tsweb/src/components/ui/confirm-dialog.tsxweb/src/components/ui/toast.tsxweb/src/__tests__/lib/motion.test.tsweb/src/components/ui/inline-edit.stories.tsxweb/src/lib/motion.tsweb/src/components/ui/command-palette.stories.tsxweb/src/stores/toast.tsweb/src/components/ui/error-boundary.tsxweb/src/components/ui/inline-edit.tsxweb/src/hooks/useCommandPalette.tsweb/src/components/ui/skeleton.tsxweb/src/components/ui/stagger-group.tsx
web/src/**/*.test.tsx
📄 CodeRabbit inference engine (CLAUDE.md)
Run Vitest with coverage scoped to files changed vs origin/main.
Files:
web/src/__tests__/components/ui/skeleton.test.tsxweb/src/__tests__/components/ui/stagger-group.test.tsxweb/src/__tests__/components/ui/animated-presence.test.tsxweb/src/__tests__/components/ui/empty-state.test.tsxweb/src/__tests__/components/ui/toast.test.tsxweb/src/__tests__/components/ui/error-boundary.test.tsxweb/src/__tests__/components/ui/command-palette.test.tsxweb/src/__tests__/components/ui/confirm-dialog.test.tsxweb/src/__tests__/App.test.tsxweb/src/__tests__/components/ui/inline-edit.test.tsx
web/src/__tests__/**/*.test.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Use Vitest for unit and property tests. Mirror src/ directory structure. Use fast-check for property-based testing (fc.assert + fc.property).
Files:
web/src/__tests__/hooks/useStatusTransition.test.tsweb/src/__tests__/hooks/useCommandPalette.test.tsweb/src/__tests__/hooks/useFlash.test.tsweb/src/__tests__/stores/toast.test.tsweb/src/__tests__/lib/motion.test.ts
web/src/components/ui/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
When creating new shared components, place them in web/src/components/ui/ with descriptive kebab-case filename. Create a .stories.tsx file alongside with all states (default, hover, loading, error, empty). Export props as TypeScript interface. Use design tokens exclusively - no hardcoded colors, fonts, or spacing. Import cn from
@/lib/utilsfor conditional class merging.
Files:
web/src/components/ui/toast.stories.tsxweb/src/components/ui/animated-presence.tsxweb/src/components/ui/error-boundary.stories.tsxweb/src/components/ui/animated-presence.stories.tsxweb/src/components/ui/skeleton.stories.tsxweb/src/components/ui/empty-state.tsxweb/src/components/ui/stagger-group.stories.tsxweb/src/components/ui/empty-state.stories.tsxweb/src/components/ui/command-palette.tsxweb/src/components/ui/confirm-dialog.stories.tsxweb/src/components/ui/confirm-dialog.tsxweb/src/components/ui/toast.tsxweb/src/components/ui/inline-edit.stories.tsxweb/src/components/ui/command-palette.stories.tsxweb/src/components/ui/error-boundary.tsxweb/src/components/ui/inline-edit.tsxweb/src/components/ui/skeleton.tsxweb/src/components/ui/stagger-group.tsx
web/src/**/*.stories.tsx
📄 CodeRabbit inference engine (CLAUDE.md)
web/src/**/*.stories.tsx: Create .stories.tsx files for all new components in web/src/components/ui/ with all possible states (default, hover, loading, error, empty) to be used in Storybook.
Use Storybook 10 ESM syntax. Import test utilities from 'storybook/test' (not@storybook/test) and 'storybook/actions' (not@storybook/addon-actions). Storybook essentials (backgrounds, controls, viewport, actions, toolbars, measure, outline) and interactions are built into core storybook.
Files:
web/src/components/ui/toast.stories.tsxweb/src/components/ui/error-boundary.stories.tsxweb/src/components/ui/animated-presence.stories.tsxweb/src/components/ui/skeleton.stories.tsxweb/src/components/ui/stagger-group.stories.tsxweb/src/components/ui/empty-state.stories.tsxweb/src/components/ui/confirm-dialog.stories.tsxweb/src/components/ui/inline-edit.stories.tsxweb/src/components/ui/command-palette.stories.tsx
**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.md: Every implementation plan must be presented to the user for accept/deny before coding starts. At every phase of planning and implementation, be critical - actively look for ways to improve the design in the spirit of what we're building (robustness, correctness, simplicity, future-proofing). Surface improvements as suggestions, not silent changes - user decides.
Prioritize issues by dependency order, not priority labels - unblocked dependencies come first.
Files:
CLAUDE.mddocs/design/brand-and-ux.md
web/src/hooks/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Create custom React hooks for auth, login lockout, WebSocket, polling, and optimistic updates.
Files:
web/src/hooks/useStatusTransition.tsweb/src/hooks/useFlash.tsweb/src/hooks/useCommandPalette.ts
docs/design/**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
docs/design/**/*.md: ALWAYS read the relevant docs/design/ page before implementing any feature or planning any issue. The design spec is the starting point for architecture, data models, and behavior.
If implementation deviates from the spec (better approach found, scope evolved, etc.), alert the user and explain why - user decides whether to proceed or update the spec. Do NOT silently diverge - every deviation needs explicit user approval.
When approved deviations occur, update the relevant docs/design/ page to reflect the new reality.
Files:
docs/design/brand-and-ux.md
web/src/styles/**/*.css
📄 CodeRabbit inference engine (CLAUDE.md)
Define design tokens using --so-* CSS custom properties as a single source of truth. Bridge with Tailwind theme configuration.
Files:
web/src/styles/design-tokens.css
web/src/stores/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Use Zustand for global state management. Separate stores by domain (auth, WebSocket, domain shells).
Files:
web/src/stores/toast.ts
🧠 Learnings (26)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.stories.tsx : Use Storybook 10 ESM syntax. Import test utilities from 'storybook/test' (not storybook/test) and 'storybook/actions' (not storybook/addon-actions). Storybook essentials (backgrounds, controls, viewport, actions, toolbars, measure, outline) and interactions are built into core storybook.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : When creating new shared components, place them in web/src/components/ui/ with descriptive kebab-case filename. Create a .stories.tsx file alongside with all states (default, hover, loading, error, empty). Export props as TypeScript interface. Use design tokens exclusively - no hardcoded colors, fonts, or spacing. Import cn from `@/lib/utils` for conditional class merging.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : React 19 is required. Use React hooks patterns and functional components throughout.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.stories.tsx : Create .stories.tsx files for all new components in web/src/components/ui/ with all possible states (default, hover, loading, error, empty) to be used in Storybook.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do NOT recreate status dots inline - use StatusBadge. Do NOT build card-with-header layouts from scratch - use SectionCard. Do NOT create metric displays with 'text-metric font-bold' - use MetricCard. Do NOT render initials circles manually - use Avatar. Do NOT create complex (>8 line) JSX inside .map() - extract to a shared component. Do NOT use rgba() with hardcoded values - use design token variables.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/hooks/**/*.ts : Create custom React hooks for auth, login lockout, WebSocket, polling, and optimistic updates.
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.stories.tsx : Use Storybook 10 ESM syntax. Import test utilities from 'storybook/test' (not storybook/test) and 'storybook/actions' (not storybook/addon-actions). Storybook essentials (backgrounds, controls, viewport, actions, toolbars, measure, outline) and interactions are built into core storybook.
Applied to files:
web/src/__tests__/components/ui/skeleton.test.tsxweb/src/__tests__/components/ui/stagger-group.test.tsxweb/src/__tests__/components/ui/animated-presence.test.tsxweb/src/__tests__/hooks/useStatusTransition.test.tsweb/src/components/ui/toast.stories.tsxweb/src/__tests__/components/ui/empty-state.test.tsxweb/src/__tests__/components/ui/toast.test.tsxweb/src/__tests__/hooks/useCommandPalette.test.tsweb/src/__tests__/components/ui/error-boundary.test.tsxweb/src/__tests__/hooks/useFlash.test.tsweb/src/__tests__/components/ui/command-palette.test.tsxweb/src/components/ui/error-boundary.stories.tsxdocs/design/brand-and-ux.mdweb/src/__tests__/components/ui/confirm-dialog.test.tsxweb/src/components/ui/animated-presence.stories.tsxweb/src/__tests__/App.test.tsxweb/src/components/ui/skeleton.stories.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxweb/src/components/ui/stagger-group.stories.tsxweb/src/components/ui/empty-state.stories.tsxweb/src/components/ui/confirm-dialog.stories.tsxweb/src/__tests__/stores/toast.test.tsweb/src/components/ui/toast.tsxweb/src/__tests__/lib/motion.test.tsweb/src/components/ui/inline-edit.stories.tsxweb/src/components/ui/command-palette.stories.tsxweb/src/components/ui/skeleton.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : When creating new shared components, place them in web/src/components/ui/ with descriptive kebab-case filename. Create a .stories.tsx file alongside with all states (default, hover, loading, error, empty). Export props as TypeScript interface. Use design tokens exclusively - no hardcoded colors, fonts, or spacing. Import cn from `@/lib/utils` for conditional class merging.
Applied to files:
web/src/__tests__/components/ui/skeleton.test.tsxweb/src/__tests__/components/ui/stagger-group.test.tsxweb/src/__tests__/components/ui/animated-presence.test.tsxweb/src/__tests__/hooks/useStatusTransition.test.tsweb/src/components/ui/toast.stories.tsxweb/src/__tests__/components/ui/empty-state.test.tsxCLAUDE.mdweb/src/__tests__/components/ui/toast.test.tsxweb/src/__tests__/components/ui/command-palette.test.tsxweb/src/components/ui/error-boundary.stories.tsxdocs/design/brand-and-ux.mdweb/src/__tests__/components/ui/confirm-dialog.test.tsxweb/src/components/ui/animated-presence.stories.tsxweb/src/components/ui/skeleton.stories.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxweb/src/components/ui/empty-state.tsxweb/src/components/ui/stagger-group.stories.tsxweb/src/components/ui/empty-state.stories.tsxweb/src/components/ui/confirm-dialog.stories.tsxweb/src/components/ui/confirm-dialog.tsxweb/src/components/ui/toast.tsxweb/src/components/ui/inline-edit.stories.tsxweb/src/components/ui/command-palette.stories.tsxweb/src/components/ui/error-boundary.tsxweb/src/components/ui/inline-edit.tsxweb/src/components/ui/skeleton.tsxweb/src/components/ui/stagger-group.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.stories.tsx : Create .stories.tsx files for all new components in web/src/components/ui/ with all possible states (default, hover, loading, error, empty) to be used in Storybook.
Applied to files:
web/src/__tests__/components/ui/skeleton.test.tsxweb/src/__tests__/components/ui/stagger-group.test.tsxweb/src/__tests__/components/ui/animated-presence.test.tsxweb/src/__tests__/hooks/useStatusTransition.test.tsweb/src/components/ui/toast.stories.tsxweb/src/__tests__/components/ui/empty-state.test.tsxweb/src/__tests__/components/ui/toast.test.tsxweb/src/components/ui/error-boundary.stories.tsxweb/src/__tests__/components/ui/confirm-dialog.test.tsxweb/src/components/ui/animated-presence.stories.tsxweb/src/components/ui/skeleton.stories.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxweb/src/components/ui/empty-state.tsxweb/src/components/ui/stagger-group.stories.tsxweb/src/components/ui/empty-state.stories.tsxweb/src/components/ui/confirm-dialog.stories.tsxweb/src/__tests__/stores/toast.test.tsweb/src/components/ui/toast.tsxweb/src/__tests__/lib/motion.test.tsweb/src/components/ui/inline-edit.stories.tsxweb/src/components/ui/command-palette.stories.tsxweb/src/components/ui/skeleton.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/__tests__/**/*.test.ts : Use Vitest for unit and property tests. Mirror src/ directory structure. Use fast-check for property-based testing (fc.assert + fc.property).
Applied to files:
web/src/__tests__/components/ui/skeleton.test.tsxweb/src/__tests__/components/ui/empty-state.test.tsxweb/src/__tests__/components/ui/toast.test.tsxweb/src/__tests__/hooks/useCommandPalette.test.tsweb/src/__tests__/components/ui/error-boundary.test.tsxweb/src/__tests__/hooks/useFlash.test.tsweb/src/__tests__/components/ui/command-palette.test.tsxweb/src/__tests__/components/ui/confirm-dialog.test.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxweb/src/__tests__/stores/toast.test.tsweb/src/__tests__/lib/motion.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__/components/ui/skeleton.test.tsxweb/src/__tests__/components/ui/stagger-group.test.tsxweb/src/__tests__/components/ui/empty-state.test.tsxweb/src/__tests__/components/ui/toast.test.tsxweb/src/__tests__/hooks/useCommandPalette.test.tsweb/src/__tests__/components/ui/error-boundary.test.tsxweb/src/__tests__/hooks/useFlash.test.tsweb/src/__tests__/components/ui/command-palette.test.tsxweb/src/__tests__/components/ui/confirm-dialog.test.tsxweb/src/__tests__/App.test.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxweb/src/__tests__/stores/toast.test.tsweb/src/__tests__/lib/motion.test.ts
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do NOT recreate status dots inline - use StatusBadge. Do NOT build card-with-header layouts from scratch - use SectionCard. Do NOT create metric displays with 'text-metric font-bold' - use MetricCard. Do NOT render initials circles manually - use Avatar. Do NOT create complex (>8 line) JSX inside .map() - extract to a shared component. Do NOT use rgba() with hardcoded values - use design token variables.
Applied to files:
web/src/__tests__/components/ui/skeleton.test.tsxweb/src/__tests__/components/ui/stagger-group.test.tsxweb/src/__tests__/hooks/useStatusTransition.test.tsweb/src/components/ui/toast.stories.tsxCLAUDE.mdweb/src/hooks/useStatusTransition.tsweb/src/__tests__/components/ui/command-palette.test.tsxdocs/design/brand-and-ux.mdweb/src/styles/design-tokens.cssweb/src/components/layout/AppLayout.tsxweb/src/components/ui/empty-state.tsxweb/src/components/ui/confirm-dialog.stories.tsxweb/src/components/ui/toast.tsxweb/src/lib/motion.tsweb/src/components/ui/skeleton.tsxweb/src/components/ui/stagger-group.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.test.tsx : Run Vitest with coverage scoped to files changed vs origin/main.
Applied to files:
web/src/__tests__/components/ui/skeleton.test.tsxweb/src/__tests__/components/ui/stagger-group.test.tsxweb/src/__tests__/components/ui/toast.test.tsxweb/src/__tests__/hooks/useCommandPalette.test.tsweb/src/__tests__/components/ui/error-boundary.test.tsxweb/src/__tests__/components/ui/command-palette.test.tsxweb/src/__tests__/components/ui/confirm-dialog.test.tsxweb/src/__tests__/App.test.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxweb/src/__tests__/stores/toast.test.tsweb/src/__tests__/lib/motion.test.ts
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use Tailwind semantic classes (text-foreground, bg-card, text-accent, text-success, bg-danger, etc.) or CSS variables (var(--so-accent)). NEVER hardcode hex values in .tsx/.ts files.
Applied to files:
web/src/__tests__/components/ui/skeleton.test.tsxweb/src/__tests__/hooks/useStatusTransition.test.tsCLAUDE.mdweb/src/hooks/useStatusTransition.tsdocs/design/brand-and-ux.mdweb/src/components/ui/skeleton.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/hooks/**/*.ts : Create custom React hooks for auth, login lockout, WebSocket, polling, and optimistic updates.
Applied to files:
web/src/__tests__/hooks/useStatusTransition.test.tsCLAUDE.mdweb/src/__tests__/hooks/useCommandPalette.test.tsweb/src/hooks/useStatusTransition.tsweb/src/__tests__/hooks/useFlash.test.tsdocs/design/brand-and-ux.mdweb/src/hooks/useFlash.tsweb/src/components/ui/toast.tsxweb/src/hooks/useCommandPalette.ts
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : A PostToolUse hook (scripts/check_web_design_system.py) runs automatically on every Edit/Write to web/src/ files. It catches hardcoded hex colors, rgba values, fontFamily declarations, new components without Storybook stories, duplicate patterns, and complex .map() blocks. Fix all violations before proceeding - do not suppress or ignore hook output.
Applied to files:
web/src/__tests__/hooks/useStatusTransition.test.tsCLAUDE.mdweb/src/__tests__/hooks/useCommandPalette.test.tsweb/src/hooks/useStatusTransition.tsweb/src/__tests__/hooks/useFlash.test.tsdocs/design/brand-and-ux.mdweb/src/hooks/useFlash.tsweb/src/components/ui/confirm-dialog.stories.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : React 19 is required. Use React hooks patterns and functional components throughout.
Applied to files:
web/src/__tests__/hooks/useStatusTransition.test.tsCLAUDE.mdweb/src/hooks/useStatusTransition.tsweb/src/__tests__/hooks/useFlash.test.tsdocs/design/brand-and-ux.mdweb/src/hooks/useFlash.tsweb/src/components/layout/AppLayout.tsxweb/src/components/ui/empty-state.tsxweb/src/components/ui/toast.tsxweb/src/components/ui/error-boundary.tsxweb/src/components/ui/inline-edit.tsxweb/src/components/ui/skeleton.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use token variables (var(--so-shadow-card-hover), border-border, border-bright) for shadows and borders.
Applied to files:
web/src/__tests__/hooks/useStatusTransition.test.tsCLAUDE.mdweb/src/hooks/useStatusTransition.tsweb/src/styles/design-tokens.cssweb/src/components/ui/skeleton.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : 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/__tests__/hooks/useStatusTransition.test.tsCLAUDE.mddocs/design/brand-and-ux.mdweb/src/lib/motion.tsweb/src/components/ui/skeleton.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/.storybook/preview.tsx : Use 'definePreview' from 'storybook/react-vite' for type-safe preview configuration. Use 'parameters.backgrounds.options' (object keyed by name) + 'initialGlobals.backgrounds.value' for backgrounds API. Set 'parameters.a11y.test: "error" | "todo" | "off"' globally to enforce WCAG compliance on all stories.
Applied to files:
web/src/components/ui/toast.stories.tsxweb/src/components/ui/error-boundary.stories.tsxdocs/design/brand-and-ux.mdweb/src/components/ui/animated-presence.stories.tsxweb/src/components/ui/skeleton.stories.tsxweb/src/components/ui/stagger-group.stories.tsxweb/src/components/ui/confirm-dialog.stories.tsxweb/src/components/ui/inline-edit.stories.tsxweb/src/components/ui/command-palette.stories.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/.storybook/main.ts : Use 'defineMain' from 'storybook/react-vite/node' and include explicit 'framework' field for type-safe Storybook configuration.
Applied to files:
web/src/components/ui/toast.stories.tsxweb/src/components/ui/error-boundary.stories.tsxweb/src/components/ui/animated-presence.stories.tsxweb/src/components/ui/skeleton.stories.tsxweb/src/components/ui/stagger-group.stories.tsxweb/src/components/ui/empty-state.stories.tsxweb/src/components/ui/confirm-dialog.stories.tsxweb/src/components/ui/inline-edit.stories.tsxweb/src/components/ui/command-palette.stories.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : ALWAYS reuse existing components from web/src/components/ui/ before creating new ones: StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button.
Applied to files:
CLAUDE.mddocs/design/brand-and-ux.mdweb/src/components/ui/empty-state.tsxweb/src/components/ui/confirm-dialog.stories.tsxweb/src/components/ui/toast.tsxweb/src/components/ui/skeleton.tsx
📚 Learning: 2026-03-15T18:17:43.675Z
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).
Applied to files:
CLAUDE.md
📚 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 web/package.json : Web dashboard Node.js 20+; dependencies in web/package.json (Vue 3, PrimeVue, Tailwind CSS, Pinia, VueFlow, ECharts, Axios, vue-draggable-plus, Vitest, ESLint, vue-tsc)
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/stores/**/*.ts : Use Zustand for global state management. Separate stores by domain (auth, WebSocket, domain shells).
Applied to files:
CLAUDE.mdweb/src/__tests__/stores/toast.test.tsweb/src/stores/toast.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:
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:
CLAUDE.mddocs/design/brand-and-ux.md
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/pages/**/*.tsx : Pages should be lazy-loaded with one page per route using React Router.
Applied to files:
CLAUDE.mdweb/src/components/layout/AppLayout.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/styles/**/*.css : Define design tokens using --so-* CSS custom properties as a single source of truth. Bridge with Tailwind theme configuration.
Applied to files:
CLAUDE.mdweb/src/styles/design-tokens.css
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to docs/design/**/*.md : If implementation deviates from the spec (better approach found, scope evolved, etc.), alert the user and explain why - user decides whether to proceed or update the spec. Do NOT silently diverge - every deviation needs explicit user approval.
Applied to files:
docs/design/brand-and-ux.md
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to docs/design/**/*.md : When approved deviations occur, update the relevant docs/design/ page to reflect the new reality.
Applied to files:
docs/design/brand-and-ux.md
| const [flashing, setFlashing] = useState(false) | ||
| const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null) | ||
|
|
||
| const triggerFlash = useCallback(() => { | ||
| // Clear any existing timer to prevent stacking | ||
| if (timerRef.current !== null) { | ||
| clearTimeout(timerRef.current) | ||
| } | ||
|
|
||
| setFlashing(true) | ||
|
|
||
| timerRef.current = setTimeout(() => { | ||
| setFlashing(false) | ||
| timerRef.current = null | ||
| }, totalMs) | ||
| }, [totalMs]) | ||
|
|
||
| // Clear timer on unmount to prevent setState on unmounted component | ||
| useEffect(() => { | ||
| return () => { | ||
| if (timerRef.current !== null) { | ||
| clearTimeout(timerRef.current) | ||
| } | ||
| } | ||
| }, []) |
There was a problem hiding this comment.
Repeated triggers do not actually restart the flash.
When flashing is already true, setFlashing(true) is a no-op, so rapid updates only extend the timeout. The class/style stays mounted and the CSS animation continues from its current progress instead of restarting.
One way to force a restart
const [flashing, setFlashing] = useState(false)
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
+ const rafRef = useRef<number | null>(null)
const triggerFlash = useCallback(() => {
// Clear any existing timer to prevent stacking
if (timerRef.current !== null) {
clearTimeout(timerRef.current)
}
+ if (rafRef.current !== null) {
+ cancelAnimationFrame(rafRef.current)
+ }
- setFlashing(true)
-
- timerRef.current = setTimeout(() => {
- setFlashing(false)
- timerRef.current = null
- }, totalMs)
+ setFlashing(false)
+ rafRef.current = requestAnimationFrame(() => {
+ setFlashing(true)
+ timerRef.current = setTimeout(() => {
+ setFlashing(false)
+ timerRef.current = null
+ }, totalMs)
+ })
}, [totalMs])
// Clear timer on unmount to prevent setState on unmounted component
useEffect(() => {
return () => {
if (timerRef.current !== null) {
clearTimeout(timerRef.current)
}
+ if (rafRef.current !== null) {
+ cancelAnimationFrame(rafRef.current)
+ }
}
}, [])📝 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.
| const [flashing, setFlashing] = useState(false) | |
| const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null) | |
| const triggerFlash = useCallback(() => { | |
| // Clear any existing timer to prevent stacking | |
| if (timerRef.current !== null) { | |
| clearTimeout(timerRef.current) | |
| } | |
| setFlashing(true) | |
| timerRef.current = setTimeout(() => { | |
| setFlashing(false) | |
| timerRef.current = null | |
| }, totalMs) | |
| }, [totalMs]) | |
| // Clear timer on unmount to prevent setState on unmounted component | |
| useEffect(() => { | |
| return () => { | |
| if (timerRef.current !== null) { | |
| clearTimeout(timerRef.current) | |
| } | |
| } | |
| }, []) | |
| const [flashing, setFlashing] = useState(false) | |
| const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null) | |
| const rafRef = useRef<number | null>(null) | |
| const triggerFlash = useCallback(() => { | |
| // Clear any existing timer to prevent stacking | |
| if (timerRef.current !== null) { | |
| clearTimeout(timerRef.current) | |
| } | |
| if (rafRef.current !== null) { | |
| cancelAnimationFrame(rafRef.current) | |
| } | |
| setFlashing(false) | |
| rafRef.current = requestAnimationFrame(() => { | |
| setFlashing(true) | |
| timerRef.current = setTimeout(() => { | |
| setFlashing(false) | |
| timerRef.current = null | |
| }, totalMs) | |
| }) | |
| }, [totalMs]) | |
| // Clear timer on unmount to prevent setState on unmounted component | |
| useEffect(() => { | |
| return () => { | |
| if (timerRef.current !== null) { | |
| clearTimeout(timerRef.current) | |
| } | |
| if (rafRef.current !== null) { | |
| cancelAnimationFrame(rafRef.current) | |
| } | |
| } | |
| }, []) |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/hooks/useFlash.ts` around lines 36 - 60, The flash doesn't restart on
repeated triggers because setFlashing(true) is a no-op when flashing is already
true; update triggerFlash (in useFlash) to force a restart by briefly clearing
the mounted state before re-applying it: when called, clear any existing
timerRef, if flashing is true first setFlashing(false) and schedule
setFlashing(true) on the next tick (e.g., via setTimeout 0) so the DOM/CSS class
is removed and re-added to restart the animation, then set timerRef to a timeout
that clears flashing after totalMs; ensure you still clear timerRef and pending
restart timeouts to avoid leaks.
There was a problem hiding this comment.
Actionable comments posted: 11
♻️ Duplicate comments (5)
web/src/__tests__/components/ui/skeleton.test.tsx (3)
53-68: 🧹 Nitpick | 🔵 TrivialRemaining fragile
.roundedselector in header test.Line 57 still queries by
.roundedclass, which couples the test to Tailwind implementation details. Thelinestest correctly uses[data-skeleton-line](lines 66-67), but the header test should follow the same pattern.Consider adding
data-skeleton-headeror similar attributes to the header skeleton elements in the implementation.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/__tests__/components/ui/skeleton.test.tsx` around lines 53 - 68, The header test for SkeletonCard is brittle because it queries the Tailwind-specific '.rounded' class; update the implementation to add a stable data attribute (e.g. data-skeleton-header) on the header skeleton elements inside the SkeletonCard (or the header subcomponent), then change the test to query for '[data-skeleton-header]' instead of '.rounded' so it no longer depends on styling classes; ensure the attribute is added where header is rendered (SkeletonCard render path that accepts header prop) and the test asserts its presence.
81-96: 🧹 Nitpick | 🔵 TrivialMixed selector strategy in SkeletonTable tests.
Row queries correctly use
[data-skeleton-row](lines 84, 92), but cell queries still use.rounded(lines 86, 94). For consistency and resilience, consider addingdata-skeleton-cellattributes to the cell skeletons.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/__tests__/components/ui/skeleton.test.tsx` around lines 81 - 96, The tests for SkeletonTable mix selectors (they query rows with data-skeleton-row but cells with the .rounded class); update the SkeletonTable component to add a data-skeleton-cell attribute to each cell skeleton and then change the tests (in web/src/__tests__/components/ui/skeleton.test.tsx) to query cells via '[data-skeleton-cell]' instead of '.rounded' so both rows and cells use resilient data-* selectors; ensure the attribute is applied where cells are rendered in the SkeletonTable component and adjust the two tests that assert column counts to use the new selector.
71-78: 🧹 Nitpick | 🔵 TrivialFragile
.roundedselector in SkeletonMetric test.Line 76 queries by
.roundedclass. This test only verifies "at least 2 skeletons exist" which is a weak assertion. Consider adding data attributes toSkeletonMetricinternal elements and asserting specific structure (label, value, progress bar).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/__tests__/components/ui/skeleton.test.tsx` around lines 71 - 78, The test for SkeletonMetric uses a fragile CSS selector ('.rounded') and weak assertion; update the SkeletonMetric component to add stable data attributes on its internal elements (e.g., data-testid="skeleton-label", "skeleton-value", "skeleton-progress") and then change the test in web/src/__tests__/components/ui/skeleton.test.tsx to query those data attributes (using getByTestId or querySelectorAll('[data-testid="..."]')) and assert the specific structure and counts (label, value, progress bar) instead of relying on '.rounded'; reference the SkeletonMetric component and the test file when making these changes.web/src/hooks/useFlash.ts (1)
37-51:⚠️ Potential issue | 🟠 MajorFlash retriggers still don’t restart the animation.
On Line 45,
setFlashing(true)is a no-op when alreadytrue, so rapid calls only extend timeout and do not restart CSS animation despite the contract on Line 18.Proposed fix
const [flashing, setFlashing] = useState(false) const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null) + const restartRef = useRef<ReturnType<typeof setTimeout> | null>(null) const triggerFlash = useCallback(() => { // Clear any existing timer to prevent stacking if (timerRef.current !== null) { clearTimeout(timerRef.current) + timerRef.current = null + } + if (restartRef.current !== null) { + clearTimeout(restartRef.current) + restartRef.current = null } - setFlashing(true) - - timerRef.current = setTimeout(() => { - setFlashing(false) - timerRef.current = null - }, totalMs) + // Force false -> true transition so CSS animation restarts + setFlashing(false) + restartRef.current = setTimeout(() => { + setFlashing(true) + timerRef.current = setTimeout(() => { + setFlashing(false) + timerRef.current = null + }, totalMs) + restartRef.current = null + }, 0) }, [totalMs]) // Clear timer on unmount to prevent setState on unmounted component useEffect(() => { return () => { if (timerRef.current !== null) { clearTimeout(timerRef.current) } + if (restartRef.current !== null) { + clearTimeout(restartRef.current) + } } }, [])Also applies to: 53-60
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/hooks/useFlash.ts` around lines 37 - 51, The triggerFlash handler currently calls setFlashing(true) which is a no-op if flashing is already true, so rapid retriggers don't restart the CSS animation; modify triggerFlash (and the similar block around the other handler at 53-60) to force a restart by briefly clearing the flag before setting it true again—e.g., clear any existing timer, setFlashing(false), then on the next tick (use requestAnimationFrame or setTimeout 0) setFlashing(true) and start timerRef with totalMs; update the same logic where the duplicate code exists so each retrigger reliably restarts the animation.web/src/components/ui/animated-presence.stories.tsx (1)
60-63: 🧹 Nitpick | 🔵 TrivialStory args are not used by the render function.
The
argsobject definesrouteKeyandchildren, but therenderfunction ignores them and rendersTransitionDemowhich manages its own state. This causes a disconnect between the Storybook controls panel and the actual component behavior.Consider removing the unused args or adding a simple static story that uses them directly:
♻️ Suggested fix
export const Default: Story = { - args: { routeKey: '/', children: null }, render: () => <TransitionDemo />, }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/components/ui/animated-presence.stories.tsx` around lines 60 - 63, The Default Story declares args (routeKey, children) but its render ignores them and returns <TransitionDemo />; update the Default story to either remove the unused args or make the render use them by accepting args and passing them into TransitionDemo (e.g. change the render to (args) => <TransitionDemo routeKey={args.routeKey} children={args.children} />) so Storybook controls drive the component, referencing the Default story and TransitionDemo component names when making 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__/components/ui/command-palette.test.tsx`:
- Around line 127-142: The test currently calls cleanup() which unmounts both
the CommandPalette render and the hook container, so the later unmount() from
renderHook no longer verifies the hook's cleanup; change the palette render to
capture its result (e.g., const palette = render(<CommandPalette />)) and
replace the first cleanup() with palette.unmount() so only the palette is
removed while the hook remains mounted, then call unmount() from renderHook to
assert the hook's cleanup; keep the final cleanup() if you need to reset global
DOM state.
In `@web/src/__tests__/components/ui/inline-edit.test.tsx`:
- Around line 124-138: The test currently never triggers blur during the
in-progress save; update the test for InlineEdit so onSave returns a promise
that you can delay (e.g., a manually controlled Promise) to simulate a save in
progress, then after calling user.type/user.keyboard to submit (triggering the
Enter save via onSave) trigger a blur on the same input (e.g., fireEvent.blur or
user.tab) while that promise is still unresolved, then resolve the promise and
await resolution; assert onSave was called exactly once to verify the
saveInProgressRef guard in inline-edit.tsx prevented a second save.
In `@web/src/components/ui/command-palette.tsx`:
- Around line 188-197: The inline JSX rendering of grouped entries inside the
.map over grouped (starting at the Command.Group block) is too complex; extract
it into a small helper/component (e.g., VisibleGroup or renderVisibleGroup) that
accepts props { groupName, items, recentIdSet, onSelect } and returns the
Command.Group with filtered items mapped to CommandItemRow using handleSelect;
update the .map to call this helper/component with grouped.entries() so the .map
body stays short and the recent-item filtering (recentIdSet.has) and rendering
logic are centralized and testable.
- Around line 46-58: The keyboard handler handleKeyDown currently toggles the
palette even when focus is in editable elements; update handleKeyDown (inside
the useEffect) to detect editable targets (e.g., tagName input, textarea,
select, or any element with contentEditable not equal to "false") and, when the
shortcut is for opening (Cmd/Ctrl+K), bail out if the event target is editable
and the palette is not already open; keep the Escape/close behavior working when
isOpen is true so Escape still closes the palette. Use the existing toggle,
close, and isOpen symbols to implement this guard.
- Around line 148-152: The Command palette primitive uses hardcoded
sizing/typography/shadow classes (e.g., pt-[15vh], max-w-[640px], max-h-[320px],
repeated text-[10px], shadow-lg) inside the Command component's className in
command-palette.tsx (within the FocusScope wrapper); replace these with the
project's design tokens or standard Tailwind scale values (use density-aware
tokens like p-card, gap-section-gap, gap-grid-gap, token-based spacing instead
of pixel values) and replace shadow-lg with the tokenized shadow variable (e.g.,
var(--so-shadow-card-hover) or the project's shadow token) and tokenized borders
(border-border, border-bright) so the component adheres to the shared design
scale; apply the same substitutions to the other occurrences mentioned in the
comment (the blocks around the Command list items and text sizes).
In `@web/src/components/ui/error-boundary.stories.tsx`:
- Around line 22-49: Add a baseline (non-error) Storybook story for
ErrorBoundary so pass-through behavior is documented: create a new story (e.g.,
Default or Healthy) alongside PageLevel/SectionLevel/ComponentLevel that renders
<ErrorBoundary level="..."> with a healthy child (a simple element or a new
GoodComponent) instead of BrokenComponent, ensuring you cover the default render
state for each relevant level (page/section/component) so Storybook shows
normal, non-error behavior.
In `@web/src/components/ui/error-boundary.tsx`:
- Around line 44-46: The ErrorBoundary.componentDidCatch currently logs raw
error and component stack to console; change it to avoid printing full details
in production by gating console.error to a dev environment check (e.g.,
process.env.NODE_ENV === 'development' or an isDev flag) and, for non-dev,
forward only sanitized data to your telemetry/reporting function (e.g., call
reportError or sendTelemetry) instead of logging error and info.componentStack
directly; reference the componentDidCatch(error: Error, info: ErrorInfo) method
and ensure you sanitize or strip the componentStack before sending or storing.
- Around line 111-117: Replace the raw <button> in the fallback with the shared
Button component: import the existing Button component, remove the hand-rolled
<button> element and render <Button> with type="button" and
onClick={resetErrorBoundary}, preserving the existing className/styling and
accessible label ("Try Again"); update the top of the file to import Button and
ensure resetErrorBoundary is passed to the Button's onClick handler.
In `@web/src/components/ui/skeleton.tsx`:
- Around line 27-53: Export the unexported props interface(s) so consumers can
import types for extending/wrapping components: add exports for
SkeletonTextProps (and likewise for SkeletonCardProps and SkeletonTableProps if
present) by making the interfaces exported (e.g., change "interface
SkeletonTextProps" to "export interface SkeletonTextProps") and ensure the
declared names match the component props (SkeletonTextProps for the SkeletonText
component) so type consumers can import them from the module.
- Around line 55-79: Export the SkeletonCardProps interface so consumers can
import and extend the prop types; update the declaration of SkeletonCardProps to
be exported (export interface SkeletonCardProps extends SkeletonProps { ... })
and keep the component SkeletonCard unchanged so external wrappers or HOCs can
type against SkeletonCardProps just like SkeletonTextProps.
- Around line 94-126: Export the SkeletonTableProps interface so consumers can
type props when wrapping or extending SkeletonTable; modify the declaration of
SkeletonTableProps to be exported (export interface SkeletonTableProps ...) and
keep the same properties (rows, columns, extending SkeletonProps) so existing
code using SkeletonTable and SkeletonProps remains compatible.
---
Duplicate comments:
In `@web/src/__tests__/components/ui/skeleton.test.tsx`:
- Around line 53-68: The header test for SkeletonCard is brittle because it
queries the Tailwind-specific '.rounded' class; update the implementation to add
a stable data attribute (e.g. data-skeleton-header) on the header skeleton
elements inside the SkeletonCard (or the header subcomponent), then change the
test to query for '[data-skeleton-header]' instead of '.rounded' so it no longer
depends on styling classes; ensure the attribute is added where header is
rendered (SkeletonCard render path that accepts header prop) and the test
asserts its presence.
- Around line 81-96: The tests for SkeletonTable mix selectors (they query rows
with data-skeleton-row but cells with the .rounded class); update the
SkeletonTable component to add a data-skeleton-cell attribute to each cell
skeleton and then change the tests (in
web/src/__tests__/components/ui/skeleton.test.tsx) to query cells via
'[data-skeleton-cell]' instead of '.rounded' so both rows and cells use
resilient data-* selectors; ensure the attribute is applied where cells are
rendered in the SkeletonTable component and adjust the two tests that assert
column counts to use the new selector.
- Around line 71-78: The test for SkeletonMetric uses a fragile CSS selector
('.rounded') and weak assertion; update the SkeletonMetric component to add
stable data attributes on its internal elements (e.g.,
data-testid="skeleton-label", "skeleton-value", "skeleton-progress") and then
change the test in web/src/__tests__/components/ui/skeleton.test.tsx to query
those data attributes (using getByTestId or
querySelectorAll('[data-testid="..."]')) and assert the specific structure and
counts (label, value, progress bar) instead of relying on '.rounded'; reference
the SkeletonMetric component and the test file when making these changes.
In `@web/src/components/ui/animated-presence.stories.tsx`:
- Around line 60-63: The Default Story declares args (routeKey, children) but
its render ignores them and returns <TransitionDemo />; update the Default story
to either remove the unused args or make the render use them by accepting args
and passing them into TransitionDemo (e.g. change the render to (args) =>
<TransitionDemo routeKey={args.routeKey} children={args.children} />) so
Storybook controls drive the component, referencing the Default story and
TransitionDemo component names when making the change.
In `@web/src/hooks/useFlash.ts`:
- Around line 37-51: The triggerFlash handler currently calls setFlashing(true)
which is a no-op if flashing is already true, so rapid retriggers don't restart
the CSS animation; modify triggerFlash (and the similar block around the other
handler at 53-60) to force a restart by briefly clearing the flag before setting
it true again—e.g., clear any existing timer, setFlashing(false), then on the
next tick (use requestAnimationFrame or setTimeout 0) setFlashing(true) and
start timerRef with totalMs; update the same logic where the duplicate code
exists so each retrigger reliably restarts the animation.
🪄 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: 0135d6a5-41cf-4f99-a89d-d48ec78182da
📒 Files selected for processing (13)
web/src/__tests__/components/ui/command-palette.test.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxweb/src/__tests__/components/ui/skeleton.test.tsxweb/src/components/layout/Sidebar.tsxweb/src/components/ui/animated-presence.stories.tsxweb/src/components/ui/animated-presence.tsxweb/src/components/ui/command-palette.tsxweb/src/components/ui/error-boundary.stories.tsxweb/src/components/ui/error-boundary.tsxweb/src/components/ui/skeleton.tsxweb/src/components/ui/stagger-group.tsxweb/src/hooks/useCommandPalette.tsweb/src/hooks/useFlash.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: Deploy Preview
- 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 (6)
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
web/src/**/*.{ts,tsx}: ALWAYS reuse existing components from web/src/components/ui/ before creating new ones: StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button.
Use Tailwind semantic classes (text-foreground, bg-card, text-accent, text-success, bg-danger, etc.) or CSS variables (var(--so-accent)). NEVER hardcode hex values in .tsx/.ts files.
Use font-sans or font-mono (maps to Geist tokens). NEVER set fontFamily directly.
Use density-aware 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 recreate status dots inline - use StatusBadge. Do NOT build card-with-header layouts from scratch - use SectionCard. Do NOT create metric displays with 'text-metric font-bold' - use MetricCard. Do NOT render initials circles manually - use Avatar. Do NOT create complex (>8 line) JSX inside .map() - extract to a shared component. Do NOT use rgba() with hardcoded values - use design token variables.
A PostToolUse hook (scripts/check_web_design_system.py) runs automatically on every Edit/Write to web/src/ files. It catches hardcoded hex colors, rgba values, fontFamily declarations, new components without Storybook stories, duplicate patterns, and complex .map() blocks. Fix all violations before proceeding - do not suppress or ignore hook output.
React 19 is required. Use React hooks patterns and functional components throughout.
Files:
web/src/components/layout/Sidebar.tsxweb/src/__tests__/components/ui/skeleton.test.tsxweb/src/components/ui/animated-presence.tsxweb/src/components/ui/animated-presence.stories.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxweb/src/components/ui/error-boundary.stories.tsxweb/src/hooks/useFlash.tsweb/src/components/ui/command-palette.tsxweb/src/components/ui/stagger-group.tsxweb/src/__tests__/components/ui/command-palette.test.tsxweb/src/components/ui/error-boundary.tsxweb/src/hooks/useCommandPalette.tsweb/src/components/ui/skeleton.tsx
**/*
📄 CodeRabbit inference engine (CLAUDE.md)
NEVER use 'cd' in Bash commands - the working directory is already set to the project root. Use absolute paths or run commands directly. Do NOT prefix commands with 'cd C:/Users/Aurelio/synthorg &&'.
Files:
web/src/components/layout/Sidebar.tsxweb/src/__tests__/components/ui/skeleton.test.tsxweb/src/components/ui/animated-presence.tsxweb/src/components/ui/animated-presence.stories.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxweb/src/components/ui/error-boundary.stories.tsxweb/src/hooks/useFlash.tsweb/src/components/ui/command-palette.tsxweb/src/components/ui/stagger-group.tsxweb/src/__tests__/components/ui/command-palette.test.tsxweb/src/components/ui/error-boundary.tsxweb/src/hooks/useCommandPalette.tsweb/src/components/ui/skeleton.tsx
web/src/**/*.test.tsx
📄 CodeRabbit inference engine (CLAUDE.md)
Run Vitest with coverage scoped to files changed vs origin/main.
Files:
web/src/__tests__/components/ui/skeleton.test.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxweb/src/__tests__/components/ui/command-palette.test.tsx
web/src/components/ui/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
When creating new shared components, place them in web/src/components/ui/ with descriptive kebab-case filename. Create a .stories.tsx file alongside with all states (default, hover, loading, error, empty). Export props as TypeScript interface. Use design tokens exclusively - no hardcoded colors, fonts, or spacing. Import cn from
@/lib/utilsfor conditional class merging.
Files:
web/src/components/ui/animated-presence.tsxweb/src/components/ui/animated-presence.stories.tsxweb/src/components/ui/error-boundary.stories.tsxweb/src/components/ui/command-palette.tsxweb/src/components/ui/stagger-group.tsxweb/src/components/ui/error-boundary.tsxweb/src/components/ui/skeleton.tsx
web/src/**/*.stories.tsx
📄 CodeRabbit inference engine (CLAUDE.md)
web/src/**/*.stories.tsx: Create .stories.tsx files for all new components in web/src/components/ui/ with all possible states (default, hover, loading, error, empty) to be used in Storybook.
Use Storybook 10 ESM syntax. Import test utilities from 'storybook/test' (not@storybook/test) and 'storybook/actions' (not@storybook/addon-actions). Storybook essentials (backgrounds, controls, viewport, actions, toolbars, measure, outline) and interactions are built into core storybook.
Files:
web/src/components/ui/animated-presence.stories.tsxweb/src/components/ui/error-boundary.stories.tsx
web/src/hooks/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Create custom React hooks for auth, login lockout, WebSocket, polling, and optimistic updates.
Files:
web/src/hooks/useFlash.tsweb/src/hooks/useCommandPalette.ts
🧠 Learnings (17)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.stories.tsx : Use Storybook 10 ESM syntax. Import test utilities from 'storybook/test' (not storybook/test) and 'storybook/actions' (not storybook/addon-actions). Storybook essentials (backgrounds, controls, viewport, actions, toolbars, measure, outline) and interactions are built into core storybook.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : When creating new shared components, place them in web/src/components/ui/ with descriptive kebab-case filename. Create a .stories.tsx file alongside with all states (default, hover, loading, error, empty). Export props as TypeScript interface. Use design tokens exclusively - no hardcoded colors, fonts, or spacing. Import cn from `@/lib/utils` for conditional class merging.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : React 19 is required. Use React hooks patterns and functional components throughout.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.stories.tsx : Create .stories.tsx files for all new components in web/src/components/ui/ with all possible states (default, hover, loading, error, empty) to be used in Storybook.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do NOT recreate status dots inline - use StatusBadge. Do NOT build card-with-header layouts from scratch - use SectionCard. Do NOT create metric displays with 'text-metric font-bold' - use MetricCard. Do NOT render initials circles manually - use Avatar. Do NOT create complex (>8 line) JSX inside .map() - extract to a shared component. Do NOT use rgba() with hardcoded values - use design token variables.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/.storybook/preview.tsx : Use 'definePreview' from 'storybook/react-vite' for type-safe preview configuration. Use 'parameters.backgrounds.options' (object keyed by name) + 'initialGlobals.backgrounds.value' for backgrounds API. Set 'parameters.a11y.test: "error" | "todo" | "off"' globally to enforce WCAG compliance on all stories.
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do NOT recreate status dots inline - use StatusBadge. Do NOT build card-with-header layouts from scratch - use SectionCard. Do NOT create metric displays with 'text-metric font-bold' - use MetricCard. Do NOT render initials circles manually - use Avatar. Do NOT create complex (>8 line) JSX inside .map() - extract to a shared component. Do NOT use rgba() with hardcoded values - use design token variables.
Applied to files:
web/src/components/layout/Sidebar.tsxweb/src/__tests__/components/ui/skeleton.test.tsxweb/src/components/ui/animated-presence.stories.tsxweb/src/components/ui/error-boundary.stories.tsxweb/src/hooks/useFlash.tsweb/src/components/ui/stagger-group.tsxweb/src/__tests__/components/ui/command-palette.test.tsxweb/src/components/ui/skeleton.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.stories.tsx : Use Storybook 10 ESM syntax. Import test utilities from 'storybook/test' (not storybook/test) and 'storybook/actions' (not storybook/addon-actions). Storybook essentials (backgrounds, controls, viewport, actions, toolbars, measure, outline) and interactions are built into core storybook.
Applied to files:
web/src/__tests__/components/ui/skeleton.test.tsxweb/src/components/ui/animated-presence.stories.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxweb/src/components/ui/error-boundary.stories.tsxweb/src/__tests__/components/ui/command-palette.test.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : When creating new shared components, place them in web/src/components/ui/ with descriptive kebab-case filename. Create a .stories.tsx file alongside with all states (default, hover, loading, error, empty). Export props as TypeScript interface. Use design tokens exclusively - no hardcoded colors, fonts, or spacing. Import cn from `@/lib/utils` for conditional class merging.
Applied to files:
web/src/__tests__/components/ui/skeleton.test.tsxweb/src/components/ui/animated-presence.tsxweb/src/components/ui/animated-presence.stories.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxweb/src/components/ui/error-boundary.stories.tsxweb/src/components/ui/command-palette.tsxweb/src/components/ui/stagger-group.tsxweb/src/__tests__/components/ui/command-palette.test.tsxweb/src/components/ui/error-boundary.tsxweb/src/components/ui/skeleton.tsx
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to web/src/__tests__/**/*.{ts,js} : Dashboard testing: Vitest unit tests organized by feature under `web/src/__tests__/`. Use fast-check for property-based testing (`fc.assert` + `fc.property`).
Applied to files:
web/src/__tests__/components/ui/skeleton.test.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxweb/src/__tests__/components/ui/command-palette.test.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.stories.tsx : Create .stories.tsx files for all new components in web/src/components/ui/ with all possible states (default, hover, loading, error, empty) to be used in Storybook.
Applied to files:
web/src/__tests__/components/ui/skeleton.test.tsxweb/src/components/ui/animated-presence.stories.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxweb/src/components/ui/error-boundary.stories.tsxweb/src/components/ui/stagger-group.tsxweb/src/components/ui/skeleton.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/__tests__/**/*.test.ts : Use Vitest for unit and property tests. Mirror src/ directory structure. Use fast-check for property-based testing (fc.assert + fc.property).
Applied to files:
web/src/__tests__/components/ui/skeleton.test.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxweb/src/__tests__/components/ui/command-palette.test.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.test.tsx : Run Vitest with coverage scoped to files changed vs origin/main.
Applied to files:
web/src/__tests__/components/ui/skeleton.test.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxweb/src/__tests__/components/ui/command-palette.test.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use Tailwind semantic classes (text-foreground, bg-card, text-accent, text-success, bg-danger, etc.) or CSS variables (var(--so-accent)). NEVER hardcode hex values in .tsx/.ts files.
Applied to files:
web/src/__tests__/components/ui/skeleton.test.tsxweb/src/components/ui/animated-presence.stories.tsxweb/src/components/ui/skeleton.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/.storybook/preview.tsx : Use 'definePreview' from 'storybook/react-vite' for type-safe preview configuration. Use 'parameters.backgrounds.options' (object keyed by name) + 'initialGlobals.backgrounds.value' for backgrounds API. Set 'parameters.a11y.test: "error" | "todo" | "off"' globally to enforce WCAG compliance on all stories.
Applied to files:
web/src/components/ui/animated-presence.stories.tsxweb/src/components/ui/error-boundary.stories.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/.storybook/main.ts : Use 'defineMain' from 'storybook/react-vite/node' and include explicit 'framework' field for type-safe Storybook configuration.
Applied to files:
web/src/components/ui/animated-presence.stories.tsxweb/src/components/ui/error-boundary.stories.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use token variables (var(--so-shadow-card-hover), border-border, border-bright) for shadows and borders.
Applied to files:
web/src/components/ui/animated-presence.stories.tsxweb/src/components/ui/skeleton.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : 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/components/ui/animated-presence.stories.tsxweb/src/components/ui/skeleton.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : ALWAYS reuse existing components from web/src/components/ui/ before creating new ones: StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button.
Applied to files:
web/src/components/ui/error-boundary.stories.tsxweb/src/components/ui/skeleton.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/hooks/**/*.ts : Create custom React hooks for auth, login lockout, WebSocket, polling, and optimistic updates.
Applied to files:
web/src/hooks/useFlash.tsweb/src/hooks/useCommandPalette.tsweb/src/components/ui/skeleton.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : React 19 is required. Use React hooks patterns and functional components throughout.
Applied to files:
web/src/hooks/useFlash.tsweb/src/components/ui/error-boundary.tsxweb/src/components/ui/skeleton.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : A PostToolUse hook (scripts/check_web_design_system.py) runs automatically on every Edit/Write to web/src/ files. It catches hardcoded hex colors, rgba values, fontFamily declarations, new components without Storybook stories, duplicate patterns, and complex .map() blocks. Fix all violations before proceeding - do not suppress or ignore hook output.
Applied to files:
web/src/hooks/useFlash.tsweb/src/hooks/useCommandPalette.ts
🔇 Additional comments (16)
web/src/components/ui/stagger-group.tsx (2)
31-60: Clean stagger container implementation.
StaggerGroupis well-structured: memoized variants, sensible defaults, and correctinitial/animategating for opt-out animation behavior.
68-86: Solid reusable item primitive.
StaggerItemcorrectly composes shared motion variants (cardEntrance) and cleanly forwards layout/test props while keeping class merging consistent.web/src/__tests__/components/ui/skeleton.test.tsx (2)
10-29: LGTM!The base
Skeletoncomponent tests cover the essential behaviors: default rendering, shimmer class application, shimmer opt-out, and custom className merging. These assertions appropriately test the component's public API.
32-51: LGTM!Good use of
[data-skeleton-line]data attribute selectors for verifying line counts. This approach is more resilient to styling changes than class-based selectors.web/src/components/ui/skeleton.tsx (2)
1-25: LGTM!The base
Skeletoncomponent is well-structured with proper design token usage (bg-border,border-bright). The shimmer animation classso-shimmercorrectly integrates with the CSS keyframes defined in design-tokens.css, which includes@media (prefers-reduced-motion: reduce)to disable animation for accessibility.
81-92: LGTM!
SkeletonMetricuses appropriate design tokens and provides a clean layout for metric placeholder UI. The three skeleton elements (label, value, progress bar) align with typical metric card anatomy.web/src/hooks/useFlash.ts (1)
64-75: Reduced-motion handling for inline animation is correctly guarded.Nice use of
prefers-reduced-motionto suppress inline animation output when reduced motion is requested.web/src/components/ui/animated-presence.tsx (1)
1-57: LGTM!The implementation correctly wraps Framer Motion's
AnimatePresencewith reduced-motion support. The exported interface addresses the previous review feedback, variant keys align betweenpageVariantsandreducedPageVariants, and all design token guidelines are followed.web/src/components/ui/animated-presence.stories.tsx (2)
17-25: LGTM!
PageContentcorrectly uses semantic design tokens (border-border,text-foreground) and Tailwind utilities. The helper keeps the demo clean and readable.
36-57: LGTM!The
TransitionDemouses proper semantic tokens throughout (bg-card,bg-surface,bg-accent,text-accent-foreground,text-foreground,hover:bg-card-hover). The previous concern about hardcodedtext-whitehas been addressed.web/src/__tests__/components/ui/inline-edit.test.tsx (5)
1-11: LGTM!Imports are appropriate for the testing patterns used, and the initial render test correctly verifies display mode behavior.
13-49: LGTM!Tests for edit mode activation, Enter-to-save, and Escape-to-cancel are well structured with proper async handling and clear assertions.
51-105: LGTM!Tests for validation errors, save rejection handling, disabled state, custom rendering, and className application are thorough and follow testing best practices.
107-122: LGTM!The blur-triggered save test properly uses
user.tab()to trigger blur and verifies the save is called with the updated value.
140-153: LGTM!The property-based test correctly verifies that arbitrary string values render in the display mode. The previous issue regarding misaligned test name has been addressed—the test is now appropriately named "display renders generated values."
web/src/components/ui/error-boundary.tsx (1)
15-24: Good API surface for consumers.
ErrorBoundaryPropsis exported and typed cleanly, which keeps wrapper/consumer usage type-safe.
| useEffect(() => { | ||
| function handleKeyDown(e: KeyboardEvent) { | ||
| if (e.key === 'k' && (e.metaKey || e.ctrlKey)) { | ||
| e.preventDefault() | ||
| toggle() | ||
| } else if (e.key === 'Escape' && isOpen) { | ||
| e.preventDefault() | ||
| close() | ||
| } | ||
| } | ||
| document.addEventListener('keydown', handleKeyDown) | ||
| return () => document.removeEventListener('keydown', handleKeyDown) | ||
| }, [toggle, close, isOpen]) |
There was a problem hiding this comment.
Don't steal Cmd/Ctrl+K from active editors.
This listener toggles the palette even when focus is inside an input, textarea, select, or contenteditable element. That will interrupt the inline-edit and form flows this PR adds. Skip the opener for editable targets unless the palette is already open and you're using the shortcut to close it.
🛡️ Suggested guard for editable targets
useEffect(() => {
function handleKeyDown(e: KeyboardEvent) {
- if (e.key === 'k' && (e.metaKey || e.ctrlKey)) {
+ const target = e.target as HTMLElement | null
+ const isEditable =
+ target instanceof HTMLInputElement ||
+ target instanceof HTMLTextAreaElement ||
+ target instanceof HTMLSelectElement ||
+ target?.isContentEditable === true
+
+ if (e.key === 'k' && (e.metaKey || e.ctrlKey) && (!isEditable || isOpen)) {
e.preventDefault()
toggle()
} else if (e.key === 'Escape' && isOpen) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/components/ui/command-palette.tsx` around lines 46 - 58, The keyboard
handler handleKeyDown currently toggles the palette even when focus is in
editable elements; update handleKeyDown (inside the useEffect) to detect
editable targets (e.g., tagName input, textarea, select, or any element with
contentEditable not equal to "false") and, when the shortcut is for opening
(Cmd/Ctrl+K), bail out if the event target is editable and the palette is not
already open; keep the Escape/close behavior working when isOpen is true so
Escape still closes the palette. Use the existing toggle, close, and isOpen
symbols to implement this guard.
| <div className="flex items-start justify-center pt-[15vh]"> | ||
| <FocusScope trapped loop> | ||
| <Command | ||
| className={cn( | ||
| 'relative w-full max-w-[640px] rounded-xl border border-border-bright bg-surface shadow-lg', |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Replace arbitrary sizing/typography classes in this shared primitive.
pt-[15vh], max-w-[640px], max-h-[320px], the repeated text-[10px], and shadow-lg bypass the shared design scale this repo requires for UI components. Please swap them for design-token classes or standard Tailwind scale values before landing.
As per coding guidelines, "Use design tokens exclusively - no hardcoded colors, fonts, or spacing.", "Use density-aware tokens (p-card, gap-section-gap, gap-grid-gap) or standard Tailwind spacing. NEVER hardcode pixel values for layout spacing.", and "Use token variables (var(--so-shadow-card-hover), border-border, border-bright) for shadows and borders."
Also applies to: 167-169, 173-175, 202-212, 244-248
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/components/ui/command-palette.tsx` around lines 148 - 152, The
Command palette primitive uses hardcoded sizing/typography/shadow classes (e.g.,
pt-[15vh], max-w-[640px], max-h-[320px], repeated text-[10px], shadow-lg) inside
the Command component's className in command-palette.tsx (within the FocusScope
wrapper); replace these with the project's design tokens or standard Tailwind
scale values (use density-aware tokens like p-card, gap-section-gap,
gap-grid-gap, token-based spacing instead of pixel values) and replace shadow-lg
with the tokenized shadow variable (e.g., var(--so-shadow-card-hover) or the
project's shadow token) and tokenized borders (border-border, border-bright) so
the component adheres to the shared design scale; apply the same substitutions
to the other occurrences mentioned in the comment (the blocks around the Command
list items and text sizes).
| {[...grouped.entries()].map(([groupName, items]) => ( | ||
| <Command.Group | ||
| key={groupName} | ||
| heading={groupName} | ||
| className="mb-1" | ||
| > | ||
| {items.filter((cmd) => !recentIdSet.has(cmd.id)).map((cmd) => ( | ||
| <CommandItemRow key={cmd.id} item={cmd} onSelect={handleSelect} /> | ||
| ))} | ||
| </Command.Group> |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Extract this grouped-list render out of the inline .map().
This block is already over the repo's limit for complex JSX inside .map(). Pull the visible-group rendering into a small helper/component so the recent-item filtering path stays readable and easier to test.
As per coding guidelines, "Do NOT create complex (>8 line) JSX inside .map() - extract to a shared component."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/components/ui/command-palette.tsx` around lines 188 - 197, The inline
JSX rendering of grouped entries inside the .map over grouped (starting at the
Command.Group block) is too complex; extract it into a small helper/component
(e.g., VisibleGroup or renderVisibleGroup) that accepts props { groupName,
items, recentIdSet, onSelect } and returns the Command.Group with filtered items
mapped to CommandItemRow using handleSelect; update the .map to call this
helper/component with grouped.entries() so the .map body stays short and the
recent-item filtering (recentIdSet.has) and rendering logic are centralized and
testable.
| componentDidCatch(error: Error, info: ErrorInfo) { | ||
| console.error('ErrorBoundary caught:', error, info.componentStack) | ||
| } |
There was a problem hiding this comment.
Avoid logging raw error details in production.
Line 45 always logs the full error and component stack, which undermines the production-safe error handling goal. Gate this to DEV (or route to sanitized telemetry only).
🔧 Proposed fix
componentDidCatch(error: Error, info: ErrorInfo) {
- console.error('ErrorBoundary caught:', error, info.componentStack)
+ if (import.meta.env.DEV) {
+ console.error('ErrorBoundary caught:', error, info.componentStack)
+ }
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/components/ui/error-boundary.tsx` around lines 44 - 46, The
ErrorBoundary.componentDidCatch currently logs raw error and component stack to
console; change it to avoid printing full details in production by gating
console.error to a dev environment check (e.g., process.env.NODE_ENV ===
'development' or an isDev flag) and, for non-dev, forward only sanitized data to
your telemetry/reporting function (e.g., call reportError or sendTelemetry)
instead of logging error and info.componentStack directly; reference the
componentDidCatch(error: Error, info: ErrorInfo) method and ensure you sanitize
or strip the componentStack before sending or storing.
| <button | ||
| type="button" | ||
| onClick={resetErrorBoundary} | ||
| className="text-accent underline underline-offset-2 hover:text-accent-foreground" | ||
| > | ||
| Try Again | ||
| </button> |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Use shared Button instead of raw <button> in the component-level fallback.
Line 111 introduces a hand-rolled button in a shared UI component; this should reuse the existing Button component for consistency.
♻️ Proposed fix
- <button
- type="button"
- onClick={resetErrorBoundary}
- className="text-accent underline underline-offset-2 hover:text-accent-foreground"
- >
+ <Button
+ type="button"
+ onClick={resetErrorBoundary}
+ className="h-auto p-0 text-accent underline underline-offset-2 hover:text-accent-foreground"
+ >
Try Again
- </button>
+ </Button>As per coding guidelines, "ALWAYS reuse existing components from web/src/components/ui/ before creating new ones: ... Button."
📝 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.
| <button | |
| type="button" | |
| onClick={resetErrorBoundary} | |
| className="text-accent underline underline-offset-2 hover:text-accent-foreground" | |
| > | |
| Try Again | |
| </button> | |
| <Button | |
| type="button" | |
| onClick={resetErrorBoundary} | |
| className="h-auto p-0 text-accent underline underline-offset-2 hover:text-accent-foreground" | |
| > | |
| Try Again | |
| </Button> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/components/ui/error-boundary.tsx` around lines 111 - 117, Replace the
raw <button> in the fallback with the shared Button component: import the
existing Button component, remove the hand-rolled <button> element and render
<Button> with type="button" and onClick={resetErrorBoundary}, preserving the
existing className/styling and accessible label ("Try Again"); update the top of
the file to import Button and ensure resetErrorBoundary is passed to the
Button's onClick handler.
Add command palette (cmdk + Ctrl+K), toast notification system with auto-dismiss queue, loading skeletons (card/metric/table variants), error boundaries with page/section/component levels, confirmation dialogs (Radix AlertDialog), inline editing with optimistic updates, and Framer Motion animation primitives (page transitions, card stagger, real-time update flash, status color transitions). Includes Storybook stories for all 9 new components and 14 test files covering hooks, stores, and component behavior. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix useFlash timer leak on unmount (cleanup in useEffect) - Fix useCommandPalette.toggle stale closure over openState - Fix InlineEdit double-save on Enter+blur (saveInProgressRef guard) - Fix InlineEdit hardcoded error id (use useId() for unique IDs) - Fix InlineEdit missing aria-label on display button - Fix Sidebar Cmd+K button missing aria-label when collapsed - Fix ErrorBoundary showing raw error.message in production - Fix App.test.tsx flaky timeout in concurrent test runs - Update CLAUDE.md component inventory with 9 new components - Update CLAUDE.md package structure (hooks, stores descriptions) - Update brand-and-ux.md with interaction component inventory - Update brand-and-ux.md animation hooks documentation - Update prefers-reduced-motion from "planned" to "supported" Pre-reviewed by 4 agents, 15 findings addressed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… Gemini Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… a11y, data attributes - Export all component prop interfaces per CLAUDE.md convention - Filter recentItems by active scope, deduplicate in grouped render - Guard Tab scope toggle when no local commands exist - Use shared Button in error-boundary stories - Platform-aware shortcutKey for Sidebar title + visible text - Replace text-white with text-accent-foreground in story - Add data-skeleton-line attributes and use in tests - Respect prefers-reduced-motion in useFlash flashStyle - Use public registerCommands API in tests instead of internal _commandGroups - Consistent getByText element selection in inline-edit tests Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…lation, exports - Gate componentDidCatch console.error to dev environment only - Add Healthy story showing non-error ErrorBoundary state - Export SkeletonTextProps, SkeletonCardProps, SkeletonTableProps - Add data-skeleton-header, data-skeleton-cell, data-testid to skeleton components - Replace all fragile .rounded CSS selectors with stable data-* attributes in tests - Fix useRegisterCommands test: use palette.unmount() instead of cleanup() - Fix double-save test: use deferred promise to properly test saveInProgressRef guard Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
88271be to
a40c237
Compare
There was a problem hiding this comment.
Actionable comments posted: 15
♻️ Duplicate comments (7)
web/src/components/ui/toast.stories.tsx (1)
58-63:⚠️ Potential issue | 🟡 MinorRemove unused
argsfrom stories with fixed customrender.Both
AllVariantsandStackedprovideargsthat are never consumed by their render functions.♻️ Proposed fix
export const AllVariants: Story = { - args: { - toast: { id: '1', variant: 'success', title: 'All variants' }, - onDismiss: () => {}, - }, render: () => ( <div className="flex flex-col gap-2"> ... </div> ), } @@ export const Stacked: Story = { render: () => <StackedDemo />, - args: { - toast: { id: '1', variant: 'success', title: 'Stacked' }, - onDismiss: () => {}, - }, parameters: { layout: 'fullscreen' }, }#!/bin/bash # Verify custom-render stories with unused args blocks rg -n -C3 "export const (AllVariants|Stacked): Story|args:|render:" web/src/components/ui/toast.stories.tsxAlso applies to: 99-104
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/components/ui/toast.stories.tsx` around lines 58 - 63, The stories AllVariants and Stacked declare an args object that is never used because both provide a custom render function; remove the unused args property from each story (delete the args: { ... } block) and keep the existing render implementation intact so the Story objects only include the render field (and any other used fields). Target the Story exports named AllVariants and Stacked and remove their args properties to eliminate dead code.web/src/hooks/useFlash.ts (1)
39-50:⚠️ Potential issue | 🟠 MajorRepeated triggers do not reliably restart the flash animation.
If already flashing,
setFlashing(true)is effectively a no-op, so the visual effect may not restart on rapid updates.♻️ Proposed fix
const [flashing, setFlashing] = useState(false) const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null) + const rafRef = useRef<number | null>(null) const triggerFlash = useCallback(() => { // Clear any existing timer to prevent stacking if (timerRef.current !== null) { clearTimeout(timerRef.current) } + if (rafRef.current !== null) { + cancelAnimationFrame(rafRef.current) + } - setFlashing(true) - - timerRef.current = setTimeout(() => { - setFlashing(false) - timerRef.current = null - }, totalMs) + setFlashing(false) + rafRef.current = requestAnimationFrame(() => { + setFlashing(true) + timerRef.current = setTimeout(() => { + setFlashing(false) + timerRef.current = null + }, totalMs) + }) }, [totalMs]) // Clear timer on unmount to prevent setState on unmounted component useEffect(() => { return () => { if (timerRef.current !== null) { clearTimeout(timerRef.current) } + if (rafRef.current !== null) { + cancelAnimationFrame(rafRef.current) + } } }, [])🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/hooks/useFlash.ts` around lines 39 - 50, The triggerFlash handler doesn't reliably restart the animation because calling setFlashing(true) while flashing is a no-op; modify triggerFlash (which uses timerRef, setFlashing and totalMs) to force a restart by first clearing the timerRef, then setFlashing(false) and on the next frame (e.g. via requestAnimationFrame or setTimeout 0) setFlashing(true) before starting the new timeout to setFlashing(false) after totalMs; ensure timerRef is reset to null when cleared so repeated rapid calls reliably restart the flash.web/src/components/ui/animated-presence.stories.tsx (1)
60-63:⚠️ Potential issue | 🟡 MinorRemove unused
Defaultstory args or wire them intorender.
argsare currently dead here, so Storybook controls don’t represent the rendered state.♻️ Proposed fix
export const Default: Story = { - args: { routeKey: '/', children: null }, render: () => <TransitionDemo />, }#!/bin/bash # Verify the story defines args but does not consume them in render rg -n -C2 "export const Default|args:|render:" web/src/components/ui/animated-presence.stories.tsx🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/components/ui/animated-presence.stories.tsx` around lines 60 - 63, The exported story Default currently defines args but its render function ignores them; either remove the unused args property from Default or make render accept the story args and pass them into the component (e.g., have render take args and forward them to TransitionDemo), ensuring the unique symbols to update are the Default export's args field and the render function and the TransitionDemo invocation so Storybook controls reflect the rendered state.web/src/components/ui/command-palette.tsx (3)
46-58:⚠️ Potential issue | 🟠 MajorDon't steal Cmd/Ctrl+K from active editors.
This listener toggles the palette even when focus is inside an
input,textarea,select, orcontenteditableelement. That will interrupt the inline-edit and form flows this PR adds. Skip the opener for editable targets unless the palette is already open.🛡️ Suggested guard for editable targets
useEffect(() => { function handleKeyDown(e: KeyboardEvent) { + const target = e.target as HTMLElement | null + const isEditable = + target instanceof HTMLInputElement || + target instanceof HTMLTextAreaElement || + target instanceof HTMLSelectElement || + target?.isContentEditable === true + - if (e.key === 'k' && (e.metaKey || e.ctrlKey)) { + if (e.key === 'k' && (e.metaKey || e.ctrlKey) && (!isEditable || isOpen)) { e.preventDefault() toggle() } else if (e.key === 'Escape' && isOpen) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/components/ui/command-palette.tsx` around lines 46 - 58, The keyboard handler in useEffect (function handleKeyDown) should ignore Cmd/Ctrl+K when the active element is editable to avoid stealing focus from inputs: detect if the event target (or document.activeElement) is an input, textarea, select, or an element with contentEditable="true" (or matches '[contenteditable="true"]') and return early (do nothing) unless isOpen is already true; keep using the existing toggle(), close(), and isOpen symbols and ensure you still call preventDefault()/toggle when the guard permits activation and preserve the Escape handling when isOpen is true.
148-152: 🛠️ Refactor suggestion | 🟠 MajorReplace arbitrary sizing/typography classes in this shared primitive.
pt-[15vh],max-w-[640px],max-h-[320px], repeatedtext-[10px], andshadow-lgbypass the shared design scale this repo requires for UI components. Please swap them for design-token classes or standard Tailwind scale values.As per coding guidelines, "Use design tokens exclusively - no hardcoded colors, fonts, or spacing.", "Use density-aware tokens (p-card, gap-section-gap, gap-grid-gap) or standard Tailwind spacing. NEVER hardcode pixel values for layout spacing.", and "Use token variables (var(--so-shadow-card-hover), border-border, border-bright) for shadows and borders."
Also applies to: 167-169, 173-175, 202-212, 244-248
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/components/ui/command-palette.tsx` around lines 148 - 152, The component uses arbitrary/specific sizing and shadow classes in the Command/FocusScope wrapper (e.g., pt-[15vh], max-w-[640px], max-h-[320px], text-[10px], shadow-lg) which violate the design-token policy; update the className usage in the FocusScope/Command and related elements (the wrapper containing FocusScope and the Command component instances) to use design-token or standard Tailwind scale tokens instead (e.g., replace pt-[15vh] with a density-aware token like p-card or a spacing token, replace max-w-[640px]/max-h-[320px] with responsive token-based width/height tokens or tailwind max-w-* and max-h-* scales, replace text-[10px] with the appropriate text-sm/xs token, and replace shadow-lg with the token var(--so-shadow-card-hover) or the project shadow token); ensure border and color tokens remain border-border or border-border-bright as per guidelines.
188-197: 🛠️ Refactor suggestion | 🟠 MajorExtract this grouped-list render out of the inline
.map().This block exceeds the repo's limit for complex JSX inside
.map(). Pull the visible-group rendering into a small helper/component so the recent-item filtering path stays readable and easier to test.As per coding guidelines, "Do NOT create complex (>8 line) JSX inside .map() - extract to a shared component."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/components/ui/command-palette.tsx` around lines 188 - 197, The inline JSX rendering of each grouped entry is too complex and should be extracted into a small helper component: create a new component/function (e.g., VisibleGroup or GroupedCommandList) that accepts props { groupName, items, recentIdSet, onSelect } and returns the Command.Group with heading, filtered items (items.filter(cmd => !recentIdSet.has(cmd.id))) and the mapped CommandItemRow elements using handleSelect; then replace the current [...grouped.entries()].map(([groupName, items]) => ( ... )) with a simple map that returns <VisibleGroup key={groupName} groupName={groupName} items={items} recentIdSet={recentIdSet} onSelect={handleSelect} /> to keep the .map body concise and testable while retaining Command.Group and CommandItemRow semantics.web/src/components/ui/error-boundary.tsx (1)
113-119: 🛠️ Refactor suggestion | 🟠 MajorReuse
Buttonin the component-level fallback.This is still the only fallback path hand-rolling a
<button>, so its behavior and styling can drift from the shared button states used by the page and section variants.As per coding guidelines, "ALWAYS reuse existing components from web/src/components/ui/ before creating new ones: StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/components/ui/error-boundary.tsx` around lines 113 - 119, Replace the hand-rolled <button> in the component-level fallback with the shared Button component to ensure consistent behavior and styling: locate the fallback JSX in web/src/components/ui/error-boundary.tsx (the code that renders the "Try Again" button and calls resetErrorBoundary) and swap the native button for the shared Button from web/src/components/ui/Button, passing the same props (type="button", onClick={resetErrorBoundary}) and the existing className/variant as needed to match the original styling/interaction; ensure you import Button at the top and remove the manual class names if the Button variant already covers them.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@web/src/__tests__/components/ui/confirm-dialog.test.tsx`:
- Around line 68-73: The current test for ConfirmDialog (rendering ConfirmDialog
with defaultProps and variant="destructive") is a false positive because Button
base classes already include destructive-related tokens; update the test to
render two variants and assert a real difference: render ConfirmDialog with
variant="destructive" and again with variant="default" (or omit variant), then
grab each confirm button (e.g., via screen.getByRole) and assert that the
destructive confirm button's className contains the destructive token while the
non-destructive confirm button's className does not (or simply assert the two
className values are not equal), referencing ConfirmDialog, defaultProps and the
confirm button element to locate the code.
In `@web/src/__tests__/components/ui/empty-state.test.tsx`:
- Around line 19-30: Tests for EmptyState use document.querySelector('svg')
which is global and brittle; change both tests to scope the SVG lookup to the
render container returned by render (e.g. use the returned container from
render(<EmptyState ...>) and call container.querySelector('svg')) so the query
only inspects the component's DOM; update the two specs in
web/src/__tests__/components/ui/empty-state.test.tsx that reference
document.querySelector to use the render container for the Search-icon and
no-icon assertions.
In `@web/src/__tests__/stores/toast.test.ts`:
- Around line 55-61: Replace the hardcoded 5000 in the test with the store's
exported DEFAULT_DURATION to avoid brittleness: import DEFAULT_DURATION from the
toast store at the top of the test file and change vi.advanceTimersByTime(5000)
to vi.advanceTimersByTime(DEFAULT_DURATION); this references the store's
constant (DEFAULT_DURATION) and keeps the test in sync with useToastStore.add
behavior.
In `@web/src/components/ui/command-palette.stories.tsx`:
- Around line 28-35: The effect currently only opens the palette when open is
true, which lets the global store leak between stories; update the useEffect
handling in the Command Palette story so it deterministically sets the store
open state on mount by calling _setOpen(open) (using the incoming prop `open`)
instead of only setting true, keep registering commands via
`registerCommands(commands)` and return the same cleanup that calls `cleanup()`
and `_setOpen(false)` on unmount; this ensures `open={false}` explicitly closes
the global palette on mount and prevents state leakage across stories.
In `@web/src/components/ui/confirm-dialog.tsx`:
- Line 1: The import for AlertDialog is using a non-standard path; update the
import statement that references AlertDialog to use the official Radix package
by importing from '@radix-ui/react-alert-dialog' instead of 'radix-ui' so the
component resolves correctly (look for the import of AlertDialog in
confirm-dialog.tsx and replace its module specifier).
In `@web/src/components/ui/error-boundary.tsx`:
- Around line 157-166: The outer div wrapper around ErrorBoundaryInner should be
opt-in: when className is falsy/empty, render ErrorBoundaryInner directly to
avoid changing layout semantics; when className is provided, render the div with
cn(className) and place ErrorBoundaryInner inside. Ensure you pass the same
props (FallbackComponent, onReset, level, children) to ErrorBoundaryInner in
both branches and do not alter prop names (ErrorBoundaryInner, className,
FallbackComponent, onReset, level).
- Around line 15-24: The ErrorBoundaryInner stays mounted across route changes
causing stale fallback UI; fix by forcing a remount or resetting state on
navigation: either add key={location.pathname} where ErrorBoundaryInner is
rendered (so it remounts per route), or implement componentDidUpdate inside
ErrorBoundaryInner to compare previous children/location and call
this.setState({ hasError: false, error: null }) and invoke onReset when children
change; update the page-level usage in AppLayout (where ErrorBoundaryInner wraps
Outlet/AnimatedPresence) accordingly so route navigation clears the error
without needing the retry button.
In `@web/src/components/ui/inline-edit.tsx`:
- Around line 8-21: Export the InlineEditProps interface and add a typed
accessible-name prop (e.g., ariaLabel?: string and/or ariaLabelledBy?: string)
to the interface; update the input/rendering code in InlineEdit (the edit-mode
<input> or the render path that creates it) to pass the provided
ariaLabel/ariaLabelledBy to the input element (and fall back to
placeholder/value only if no accessible name is provided), and ensure the public
component signature accepts InlineEditProps so callers can supply the accessible
name.
- Around line 71-100: In the save useCallback, short-circuit when the user
didn't change the value: before setting saveInProgressRef.current = true, check
if editValue === value and if so call setState('display') (and clear any error
if desired) and return; this avoids calling onSave for unchanged content and
prevents duplicate writes while keeping the existing saveInProgressRef,
setState, editValue, value and onSave logic intact.
- Around line 102-112: The handleKeyDown callback in inline-edit should ignore
Enter/Escape when IME composition is active; modify the useCallback handler
(handleKeyDown) to first check e.isComposing || (e.nativeEvent && (e.nativeEvent
as any).isComposing) and return early if true, then proceed to preventDefault
and call save() or cancel() as currently implemented; ensure the dependency
array ([save, cancel]) remains unchanged.
In `@web/src/components/ui/stagger-group.tsx`:
- Line 80: The usage cn(className) in the StaggerGroup component is redundant
because cn is given a single argument and just returns the same value; replace
the prop assignment className={cn(className)} with className={className} and
remove the unnecessary call to the cn helper (leave the import if still used
elsewhere, otherwise remove the import) to simplify the code.
In `@web/src/components/ui/toast.tsx`:
- Line 73: The current computation of visible toasts uses toasts.slice(0,
maxVisible) which keeps the oldest items and can drop newer notifications;
change the logic in the component where visible is computed (variable visible,
using toasts and maxVisible) to select the newest toasts instead (e.g., take the
last maxVisible entries from the toasts array, handling cases when maxVisible is
zero/negative or greater than toasts.length) so recent notifications are
retained.
- Around line 27-38: The toast currently always uses role="alert" which is
assertive; change the role on the motion.div to be conditional so only error
toasts use "alert" and non-error toasts use "status" (keep the existing
aria-live logic via the ariaLive variable). Locate the JSX for motion.div
(variants toastEntrance) and replace the hardcoded role="alert" with a
conditional role based on toast.variant (e.g., role={toast.variant === 'error' ?
'alert' : 'status'}).
In `@web/src/hooks/useCommandPalette.ts`:
- Around line 130-136: _reset currently clears commandGroups, openState, and
registrationCounter but does not notify mounted hooks, leaving their snapshots
stale; after updating the internal snapshot call the module's subscription
emitter so listeners update immediately—i.e., in _reset (after
updateCommandsSnapshot()) invoke the same notify/emit function used by the hook
subscription (or modify updateCommandsSnapshot to emit) so subscribers of the
command palette receive the new commands/isOpen state synchronously.
In `@web/src/hooks/useStatusTransition.ts`:
- Around line 10-16: The STATUS_COLOR_MAP contains an unused "warning" key that
doesn't match the possible values of StatusToken returned by getStatusColor();
update STATUS_COLOR_MAP in useStatusTransition.ts to either remove the "warning"
entry or add a clear comment explaining it's intentionally reserved for future
use so the map aligns with StatusToken (reference symbols: STATUS_COLOR_MAP,
StatusToken, getStatusColor, useStatusTransition).
---
Duplicate comments:
In `@web/src/components/ui/animated-presence.stories.tsx`:
- Around line 60-63: The exported story Default currently defines args but its
render function ignores them; either remove the unused args property from
Default or make render accept the story args and pass them into the component
(e.g., have render take args and forward them to TransitionDemo), ensuring the
unique symbols to update are the Default export's args field and the render
function and the TransitionDemo invocation so Storybook controls reflect the
rendered state.
In `@web/src/components/ui/command-palette.tsx`:
- Around line 46-58: The keyboard handler in useEffect (function handleKeyDown)
should ignore Cmd/Ctrl+K when the active element is editable to avoid stealing
focus from inputs: detect if the event target (or document.activeElement) is an
input, textarea, select, or an element with contentEditable="true" (or matches
'[contenteditable="true"]') and return early (do nothing) unless isOpen is
already true; keep using the existing toggle(), close(), and isOpen symbols and
ensure you still call preventDefault()/toggle when the guard permits activation
and preserve the Escape handling when isOpen is true.
- Around line 148-152: The component uses arbitrary/specific sizing and shadow
classes in the Command/FocusScope wrapper (e.g., pt-[15vh], max-w-[640px],
max-h-[320px], text-[10px], shadow-lg) which violate the design-token policy;
update the className usage in the FocusScope/Command and related elements (the
wrapper containing FocusScope and the Command component instances) to use
design-token or standard Tailwind scale tokens instead (e.g., replace pt-[15vh]
with a density-aware token like p-card or a spacing token, replace
max-w-[640px]/max-h-[320px] with responsive token-based width/height tokens or
tailwind max-w-* and max-h-* scales, replace text-[10px] with the appropriate
text-sm/xs token, and replace shadow-lg with the token
var(--so-shadow-card-hover) or the project shadow token); ensure border and
color tokens remain border-border or border-border-bright as per guidelines.
- Around line 188-197: The inline JSX rendering of each grouped entry is too
complex and should be extracted into a small helper component: create a new
component/function (e.g., VisibleGroup or GroupedCommandList) that accepts props
{ groupName, items, recentIdSet, onSelect } and returns the Command.Group with
heading, filtered items (items.filter(cmd => !recentIdSet.has(cmd.id))) and the
mapped CommandItemRow elements using handleSelect; then replace the current
[...grouped.entries()].map(([groupName, items]) => ( ... )) with a simple map
that returns <VisibleGroup key={groupName} groupName={groupName} items={items}
recentIdSet={recentIdSet} onSelect={handleSelect} /> to keep the .map body
concise and testable while retaining Command.Group and CommandItemRow semantics.
In `@web/src/components/ui/error-boundary.tsx`:
- Around line 113-119: Replace the hand-rolled <button> in the component-level
fallback with the shared Button component to ensure consistent behavior and
styling: locate the fallback JSX in web/src/components/ui/error-boundary.tsx
(the code that renders the "Try Again" button and calls resetErrorBoundary) and
swap the native button for the shared Button from web/src/components/ui/Button,
passing the same props (type="button", onClick={resetErrorBoundary}) and the
existing className/variant as needed to match the original styling/interaction;
ensure you import Button at the top and remove the manual class names if the
Button variant already covers them.
In `@web/src/components/ui/toast.stories.tsx`:
- Around line 58-63: The stories AllVariants and Stacked declare an args object
that is never used because both provide a custom render function; remove the
unused args property from each story (delete the args: { ... } block) and keep
the existing render implementation intact so the Story objects only include the
render field (and any other used fields). Target the Story exports named
AllVariants and Stacked and remove their args properties to eliminate dead code.
In `@web/src/hooks/useFlash.ts`:
- Around line 39-50: The triggerFlash handler doesn't reliably restart the
animation because calling setFlashing(true) while flashing is a no-op; modify
triggerFlash (which uses timerRef, setFlashing and totalMs) to force a restart
by first clearing the timerRef, then setFlashing(false) and on the next frame
(e.g. via requestAnimationFrame or setTimeout 0) setFlashing(true) before
starting the new timeout to setFlashing(false) after totalMs; ensure timerRef is
reset to null when cleared so repeated rapid calls reliably restart the flash.
🪄 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: 36bbd6bc-6f20-4fe1-a9f0-3c76482d9ca6
📒 Files selected for processing (43)
CLAUDE.mddocs/design/brand-and-ux.mdweb/src/__tests__/App.test.tsxweb/src/__tests__/components/ui/animated-presence.test.tsxweb/src/__tests__/components/ui/command-palette.test.tsxweb/src/__tests__/components/ui/confirm-dialog.test.tsxweb/src/__tests__/components/ui/empty-state.test.tsxweb/src/__tests__/components/ui/error-boundary.test.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxweb/src/__tests__/components/ui/skeleton.test.tsxweb/src/__tests__/components/ui/stagger-group.test.tsxweb/src/__tests__/components/ui/toast.test.tsxweb/src/__tests__/hooks/useCommandPalette.test.tsweb/src/__tests__/hooks/useFlash.test.tsweb/src/__tests__/hooks/useStatusTransition.test.tsweb/src/__tests__/lib/motion.test.tsweb/src/__tests__/stores/toast.test.tsweb/src/components/layout/AppLayout.tsxweb/src/components/layout/Sidebar.tsxweb/src/components/ui/animated-presence.stories.tsxweb/src/components/ui/animated-presence.tsxweb/src/components/ui/command-palette.stories.tsxweb/src/components/ui/command-palette.tsxweb/src/components/ui/confirm-dialog.stories.tsxweb/src/components/ui/confirm-dialog.tsxweb/src/components/ui/empty-state.stories.tsxweb/src/components/ui/empty-state.tsxweb/src/components/ui/error-boundary.stories.tsxweb/src/components/ui/error-boundary.tsxweb/src/components/ui/inline-edit.stories.tsxweb/src/components/ui/inline-edit.tsxweb/src/components/ui/skeleton.stories.tsxweb/src/components/ui/skeleton.tsxweb/src/components/ui/stagger-group.stories.tsxweb/src/components/ui/stagger-group.tsxweb/src/components/ui/toast.stories.tsxweb/src/components/ui/toast.tsxweb/src/hooks/useCommandPalette.tsweb/src/hooks/useFlash.tsweb/src/hooks/useStatusTransition.tsweb/src/lib/motion.tsweb/src/stores/toast.tsweb/src/styles/design-tokens.css
📜 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). (5)
- GitHub Check: Build Sandbox
- GitHub Check: Build Backend
- GitHub Check: Build Web
- GitHub Check: Dependency Review
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (11)
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
web/src/**/*.{ts,tsx}: ALWAYS reuse existing components from web/src/components/ui/ before creating new ones: StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button.
Use Tailwind semantic classes (text-foreground, bg-card, text-accent, text-success, bg-danger, etc.) or CSS variables (var(--so-accent)). NEVER hardcode hex values in .tsx/.ts files.
Use font-sans or font-mono (maps to Geist tokens). NEVER set fontFamily directly.
Use density-aware 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 recreate status dots inline - use StatusBadge. Do NOT build card-with-header layouts from scratch - use SectionCard. Do NOT create metric displays with 'text-metric font-bold' - use MetricCard. Do NOT render initials circles manually - use Avatar. Do NOT create complex (>8 line) JSX inside .map() - extract to a shared component. Do NOT use rgba() with hardcoded values - use design token variables.
A PostToolUse hook (scripts/check_web_design_system.py) runs automatically on every Edit/Write to web/src/ files. It catches hardcoded hex colors, rgba values, fontFamily declarations, new components without Storybook stories, duplicate patterns, and complex .map() blocks. Fix all violations before proceeding - do not suppress or ignore hook output.
React 19 is required. Use React hooks patterns and functional components throughout.
Files:
web/src/__tests__/App.test.tsxweb/src/__tests__/components/ui/empty-state.test.tsxweb/src/components/layout/AppLayout.tsxweb/src/__tests__/stores/toast.test.tsweb/src/__tests__/hooks/useCommandPalette.test.tsweb/src/components/layout/Sidebar.tsxweb/src/__tests__/components/ui/toast.test.tsxweb/src/__tests__/components/ui/confirm-dialog.test.tsxweb/src/__tests__/components/ui/error-boundary.test.tsxweb/src/__tests__/hooks/useStatusTransition.test.tsweb/src/__tests__/components/ui/animated-presence.test.tsxweb/src/__tests__/components/ui/stagger-group.test.tsxweb/src/components/ui/command-palette.stories.tsxweb/src/components/ui/stagger-group.stories.tsxweb/src/components/ui/empty-state.stories.tsxweb/src/components/ui/skeleton.stories.tsxweb/src/components/ui/toast.stories.tsxweb/src/components/ui/toast.tsxweb/src/components/ui/animated-presence.stories.tsxweb/src/__tests__/components/ui/command-palette.test.tsxweb/src/components/ui/confirm-dialog.stories.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxweb/src/hooks/useFlash.tsweb/src/components/ui/empty-state.tsxweb/src/components/ui/error-boundary.stories.tsxweb/src/components/ui/skeleton.tsxweb/src/components/ui/animated-presence.tsxweb/src/hooks/useCommandPalette.tsweb/src/lib/motion.tsweb/src/__tests__/hooks/useFlash.test.tsweb/src/components/ui/command-palette.tsxweb/src/__tests__/components/ui/skeleton.test.tsxweb/src/components/ui/confirm-dialog.tsxweb/src/stores/toast.tsweb/src/components/ui/inline-edit.stories.tsxweb/src/components/ui/inline-edit.tsxweb/src/hooks/useStatusTransition.tsweb/src/components/ui/stagger-group.tsxweb/src/__tests__/lib/motion.test.tsweb/src/components/ui/error-boundary.tsx
**/*
📄 CodeRabbit inference engine (CLAUDE.md)
NEVER use 'cd' in Bash commands - the working directory is already set to the project root. Use absolute paths or run commands directly. Do NOT prefix commands with 'cd C:/Users/Aurelio/synthorg &&'.
Files:
web/src/__tests__/App.test.tsxweb/src/__tests__/components/ui/empty-state.test.tsxweb/src/components/layout/AppLayout.tsxweb/src/__tests__/stores/toast.test.tsweb/src/styles/design-tokens.cssweb/src/__tests__/hooks/useCommandPalette.test.tsweb/src/components/layout/Sidebar.tsxweb/src/__tests__/components/ui/toast.test.tsxdocs/design/brand-and-ux.mdweb/src/__tests__/components/ui/confirm-dialog.test.tsxweb/src/__tests__/components/ui/error-boundary.test.tsxweb/src/__tests__/hooks/useStatusTransition.test.tsweb/src/__tests__/components/ui/animated-presence.test.tsxweb/src/__tests__/components/ui/stagger-group.test.tsxweb/src/components/ui/command-palette.stories.tsxweb/src/components/ui/stagger-group.stories.tsxCLAUDE.mdweb/src/components/ui/empty-state.stories.tsxweb/src/components/ui/skeleton.stories.tsxweb/src/components/ui/toast.stories.tsxweb/src/components/ui/toast.tsxweb/src/components/ui/animated-presence.stories.tsxweb/src/__tests__/components/ui/command-palette.test.tsxweb/src/components/ui/confirm-dialog.stories.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxweb/src/hooks/useFlash.tsweb/src/components/ui/empty-state.tsxweb/src/components/ui/error-boundary.stories.tsxweb/src/components/ui/skeleton.tsxweb/src/components/ui/animated-presence.tsxweb/src/hooks/useCommandPalette.tsweb/src/lib/motion.tsweb/src/__tests__/hooks/useFlash.test.tsweb/src/components/ui/command-palette.tsxweb/src/__tests__/components/ui/skeleton.test.tsxweb/src/components/ui/confirm-dialog.tsxweb/src/stores/toast.tsweb/src/components/ui/inline-edit.stories.tsxweb/src/components/ui/inline-edit.tsxweb/src/hooks/useStatusTransition.tsweb/src/components/ui/stagger-group.tsxweb/src/__tests__/lib/motion.test.tsweb/src/components/ui/error-boundary.tsx
web/src/**/*.test.tsx
📄 CodeRabbit inference engine (CLAUDE.md)
Run Vitest with coverage scoped to files changed vs origin/main.
Files:
web/src/__tests__/App.test.tsxweb/src/__tests__/components/ui/empty-state.test.tsxweb/src/__tests__/components/ui/toast.test.tsxweb/src/__tests__/components/ui/confirm-dialog.test.tsxweb/src/__tests__/components/ui/error-boundary.test.tsxweb/src/__tests__/components/ui/animated-presence.test.tsxweb/src/__tests__/components/ui/stagger-group.test.tsxweb/src/__tests__/components/ui/command-palette.test.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxweb/src/__tests__/components/ui/skeleton.test.tsx
web/src/__tests__/**/*.test.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Use Vitest for unit and property tests. Mirror src/ directory structure. Use fast-check for property-based testing (fc.assert + fc.property).
Files:
web/src/__tests__/stores/toast.test.tsweb/src/__tests__/hooks/useCommandPalette.test.tsweb/src/__tests__/hooks/useStatusTransition.test.tsweb/src/__tests__/hooks/useFlash.test.tsweb/src/__tests__/lib/motion.test.ts
web/src/styles/**/*.css
📄 CodeRabbit inference engine (CLAUDE.md)
Define design tokens using --so-* CSS custom properties as a single source of truth. Bridge with Tailwind theme configuration.
Files:
web/src/styles/design-tokens.css
docs/design/**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
docs/design/**/*.md: ALWAYS read the relevant docs/design/ page before implementing any feature or planning any issue. The design spec is the starting point for architecture, data models, and behavior.
If implementation deviates from the spec (better approach found, scope evolved, etc.), alert the user and explain why - user decides whether to proceed or update the spec. Do NOT silently diverge - every deviation needs explicit user approval.
When approved deviations occur, update the relevant docs/design/ page to reflect the new reality.
Files:
docs/design/brand-and-ux.md
**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.md: Every implementation plan must be presented to the user for accept/deny before coding starts. At every phase of planning and implementation, be critical - actively look for ways to improve the design in the spirit of what we're building (robustness, correctness, simplicity, future-proofing). Surface improvements as suggestions, not silent changes - user decides.
Prioritize issues by dependency order, not priority labels - unblocked dependencies come first.
Files:
docs/design/brand-and-ux.mdCLAUDE.md
web/src/components/ui/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
When creating new shared components, place them in web/src/components/ui/ with descriptive kebab-case filename. Create a .stories.tsx file alongside with all states (default, hover, loading, error, empty). Export props as TypeScript interface. Use design tokens exclusively - no hardcoded colors, fonts, or spacing. Import cn from
@/lib/utilsfor conditional class merging.
Files:
web/src/components/ui/command-palette.stories.tsxweb/src/components/ui/stagger-group.stories.tsxweb/src/components/ui/empty-state.stories.tsxweb/src/components/ui/skeleton.stories.tsxweb/src/components/ui/toast.stories.tsxweb/src/components/ui/toast.tsxweb/src/components/ui/animated-presence.stories.tsxweb/src/components/ui/confirm-dialog.stories.tsxweb/src/components/ui/empty-state.tsxweb/src/components/ui/error-boundary.stories.tsxweb/src/components/ui/skeleton.tsxweb/src/components/ui/animated-presence.tsxweb/src/components/ui/command-palette.tsxweb/src/components/ui/confirm-dialog.tsxweb/src/components/ui/inline-edit.stories.tsxweb/src/components/ui/inline-edit.tsxweb/src/components/ui/stagger-group.tsxweb/src/components/ui/error-boundary.tsx
web/src/**/*.stories.tsx
📄 CodeRabbit inference engine (CLAUDE.md)
web/src/**/*.stories.tsx: Create .stories.tsx files for all new components in web/src/components/ui/ with all possible states (default, hover, loading, error, empty) to be used in Storybook.
Use Storybook 10 ESM syntax. Import test utilities from 'storybook/test' (not@storybook/test) and 'storybook/actions' (not@storybook/addon-actions). Storybook essentials (backgrounds, controls, viewport, actions, toolbars, measure, outline) and interactions are built into core storybook.
Files:
web/src/components/ui/command-palette.stories.tsxweb/src/components/ui/stagger-group.stories.tsxweb/src/components/ui/empty-state.stories.tsxweb/src/components/ui/skeleton.stories.tsxweb/src/components/ui/toast.stories.tsxweb/src/components/ui/animated-presence.stories.tsxweb/src/components/ui/confirm-dialog.stories.tsxweb/src/components/ui/error-boundary.stories.tsxweb/src/components/ui/inline-edit.stories.tsx
web/src/hooks/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Create custom React hooks for auth, login lockout, WebSocket, polling, and optimistic updates.
Files:
web/src/hooks/useFlash.tsweb/src/hooks/useCommandPalette.tsweb/src/hooks/useStatusTransition.ts
web/src/stores/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Use Zustand for global state management. Separate stores by domain (auth, WebSocket, domain shells).
Files:
web/src/stores/toast.ts
🧠 Learnings (26)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.stories.tsx : Use Storybook 10 ESM syntax. Import test utilities from 'storybook/test' (not storybook/test) and 'storybook/actions' (not storybook/addon-actions). Storybook essentials (backgrounds, controls, viewport, actions, toolbars, measure, outline) and interactions are built into core storybook.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : When creating new shared components, place them in web/src/components/ui/ with descriptive kebab-case filename. Create a .stories.tsx file alongside with all states (default, hover, loading, error, empty). Export props as TypeScript interface. Use design tokens exclusively - no hardcoded colors, fonts, or spacing. Import cn from `@/lib/utils` for conditional class merging.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : React 19 is required. Use React hooks patterns and functional components throughout.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.stories.tsx : Create .stories.tsx files for all new components in web/src/components/ui/ with all possible states (default, hover, loading, error, empty) to be used in Storybook.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do NOT recreate status dots inline - use StatusBadge. Do NOT build card-with-header layouts from scratch - use SectionCard. Do NOT create metric displays with 'text-metric font-bold' - use MetricCard. Do NOT render initials circles manually - use Avatar. Do NOT create complex (>8 line) JSX inside .map() - extract to a shared component. Do NOT use rgba() with hardcoded values - use design token variables.
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.stories.tsx : Use Storybook 10 ESM syntax. Import test utilities from 'storybook/test' (not storybook/test) and 'storybook/actions' (not storybook/addon-actions). Storybook essentials (backgrounds, controls, viewport, actions, toolbars, measure, outline) and interactions are built into core storybook.
Applied to files:
web/src/__tests__/App.test.tsxweb/src/__tests__/components/ui/empty-state.test.tsxweb/src/__tests__/stores/toast.test.tsweb/src/__tests__/hooks/useCommandPalette.test.tsweb/src/__tests__/components/ui/toast.test.tsxdocs/design/brand-and-ux.mdweb/src/__tests__/components/ui/confirm-dialog.test.tsxweb/src/__tests__/components/ui/error-boundary.test.tsxweb/src/__tests__/hooks/useStatusTransition.test.tsweb/src/__tests__/components/ui/animated-presence.test.tsxweb/src/__tests__/components/ui/stagger-group.test.tsxweb/src/components/ui/command-palette.stories.tsxweb/src/components/ui/stagger-group.stories.tsxweb/src/components/ui/empty-state.stories.tsxweb/src/components/ui/skeleton.stories.tsxweb/src/components/ui/toast.stories.tsxweb/src/components/ui/animated-presence.stories.tsxweb/src/__tests__/components/ui/command-palette.test.tsxweb/src/components/ui/confirm-dialog.stories.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxweb/src/components/ui/error-boundary.stories.tsxweb/src/components/ui/skeleton.tsxweb/src/__tests__/hooks/useFlash.test.tsweb/src/__tests__/components/ui/skeleton.test.tsxweb/src/components/ui/inline-edit.stories.tsxweb/src/__tests__/lib/motion.test.ts
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.test.tsx : Run Vitest with coverage scoped to files changed vs origin/main.
Applied to files:
web/src/__tests__/App.test.tsxweb/src/__tests__/hooks/useCommandPalette.test.tsweb/src/__tests__/components/ui/toast.test.tsxweb/src/__tests__/components/ui/confirm-dialog.test.tsxweb/src/__tests__/components/ui/error-boundary.test.tsxweb/src/__tests__/components/ui/stagger-group.test.tsxweb/src/__tests__/components/ui/command-palette.test.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxweb/src/__tests__/hooks/useFlash.test.tsweb/src/__tests__/lib/motion.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__/App.test.tsxweb/src/__tests__/components/ui/empty-state.test.tsxweb/src/__tests__/stores/toast.test.tsweb/src/__tests__/hooks/useCommandPalette.test.tsweb/src/__tests__/components/ui/toast.test.tsxweb/src/__tests__/components/ui/confirm-dialog.test.tsxweb/src/__tests__/components/ui/error-boundary.test.tsxweb/src/__tests__/components/ui/animated-presence.test.tsxweb/src/__tests__/components/ui/stagger-group.test.tsxCLAUDE.mdweb/src/__tests__/components/ui/command-palette.test.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxweb/src/__tests__/hooks/useFlash.test.tsweb/src/__tests__/components/ui/skeleton.test.tsxweb/src/__tests__/lib/motion.test.ts
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.stories.tsx : Create .stories.tsx files for all new components in web/src/components/ui/ with all possible states (default, hover, loading, error, empty) to be used in Storybook.
Applied to files:
web/src/__tests__/components/ui/empty-state.test.tsxweb/src/__tests__/stores/toast.test.tsweb/src/__tests__/components/ui/toast.test.tsxweb/src/__tests__/hooks/useStatusTransition.test.tsweb/src/__tests__/components/ui/animated-presence.test.tsxweb/src/__tests__/components/ui/stagger-group.test.tsxweb/src/components/ui/command-palette.stories.tsxweb/src/components/ui/stagger-group.stories.tsxweb/src/components/ui/empty-state.stories.tsxweb/src/components/ui/skeleton.stories.tsxweb/src/components/ui/toast.stories.tsxweb/src/components/ui/toast.tsxweb/src/components/ui/animated-presence.stories.tsxweb/src/components/ui/confirm-dialog.stories.tsxweb/src/components/ui/empty-state.tsxweb/src/components/ui/error-boundary.stories.tsxweb/src/components/ui/skeleton.tsxweb/src/__tests__/components/ui/skeleton.test.tsxweb/src/components/ui/inline-edit.stories.tsxweb/src/components/ui/stagger-group.tsxweb/src/__tests__/lib/motion.test.ts
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : When creating new shared components, place them in web/src/components/ui/ with descriptive kebab-case filename. Create a .stories.tsx file alongside with all states (default, hover, loading, error, empty). Export props as TypeScript interface. Use design tokens exclusively - no hardcoded colors, fonts, or spacing. Import cn from `@/lib/utils` for conditional class merging.
Applied to files:
web/src/__tests__/components/ui/empty-state.test.tsxweb/src/__tests__/components/ui/toast.test.tsxdocs/design/brand-and-ux.mdweb/src/__tests__/components/ui/confirm-dialog.test.tsxweb/src/__tests__/hooks/useStatusTransition.test.tsweb/src/__tests__/components/ui/animated-presence.test.tsxweb/src/components/ui/command-palette.stories.tsxweb/src/components/ui/stagger-group.stories.tsxCLAUDE.mdweb/src/components/ui/empty-state.stories.tsxweb/src/components/ui/skeleton.stories.tsxweb/src/components/ui/toast.stories.tsxweb/src/components/ui/toast.tsxweb/src/components/ui/animated-presence.stories.tsxweb/src/__tests__/components/ui/command-palette.test.tsxweb/src/components/ui/confirm-dialog.stories.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxweb/src/components/ui/empty-state.tsxweb/src/components/ui/error-boundary.stories.tsxweb/src/components/ui/skeleton.tsxweb/src/components/ui/animated-presence.tsxweb/src/components/ui/command-palette.tsxweb/src/__tests__/components/ui/skeleton.test.tsxweb/src/components/ui/confirm-dialog.tsxweb/src/components/ui/inline-edit.stories.tsxweb/src/components/ui/inline-edit.tsxweb/src/components/ui/stagger-group.tsxweb/src/components/ui/error-boundary.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/__tests__/**/*.test.ts : Use Vitest for unit and property tests. Mirror src/ directory structure. Use fast-check for property-based testing (fc.assert + fc.property).
Applied to files:
web/src/__tests__/components/ui/empty-state.test.tsxweb/src/__tests__/stores/toast.test.tsweb/src/__tests__/hooks/useCommandPalette.test.tsweb/src/__tests__/components/ui/toast.test.tsxweb/src/__tests__/components/ui/confirm-dialog.test.tsxweb/src/__tests__/components/ui/error-boundary.test.tsxweb/src/__tests__/components/ui/stagger-group.test.tsxweb/src/__tests__/components/ui/command-palette.test.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxweb/src/__tests__/hooks/useFlash.test.tsweb/src/__tests__/components/ui/skeleton.test.tsxweb/src/__tests__/lib/motion.test.ts
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do NOT recreate status dots inline - use StatusBadge. Do NOT build card-with-header layouts from scratch - use SectionCard. Do NOT create metric displays with 'text-metric font-bold' - use MetricCard. Do NOT render initials circles manually - use Avatar. Do NOT create complex (>8 line) JSX inside .map() - extract to a shared component. Do NOT use rgba() with hardcoded values - use design token variables.
Applied to files:
web/src/components/layout/AppLayout.tsxweb/src/styles/design-tokens.cssdocs/design/brand-and-ux.mdweb/src/__tests__/hooks/useStatusTransition.test.tsweb/src/__tests__/components/ui/stagger-group.test.tsxCLAUDE.mdweb/src/components/ui/toast.stories.tsxweb/src/components/ui/toast.tsxweb/src/components/ui/animated-presence.stories.tsxweb/src/__tests__/components/ui/command-palette.test.tsxweb/src/components/ui/confirm-dialog.stories.tsxweb/src/hooks/useFlash.tsweb/src/components/ui/empty-state.tsxweb/src/components/ui/error-boundary.stories.tsxweb/src/components/ui/skeleton.tsxweb/src/lib/motion.tsweb/src/components/ui/command-palette.tsxweb/src/__tests__/components/ui/skeleton.test.tsxweb/src/hooks/useStatusTransition.tsweb/src/components/ui/stagger-group.tsxweb/src/components/ui/error-boundary.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : React 19 is required. Use React hooks patterns and functional components throughout.
Applied to files:
web/src/components/layout/AppLayout.tsxdocs/design/brand-and-ux.mdweb/src/__tests__/hooks/useStatusTransition.test.tsCLAUDE.mdweb/src/components/ui/toast.tsxweb/src/hooks/useFlash.tsweb/src/components/ui/skeleton.tsxweb/src/components/ui/inline-edit.tsxweb/src/hooks/useStatusTransition.tsweb/src/components/ui/error-boundary.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/pages/**/*.tsx : Pages should be lazy-loaded with one page per route using React Router.
Applied to files:
web/src/components/layout/AppLayout.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/stores/**/*.ts : Use Zustand for global state management. Separate stores by domain (auth, WebSocket, domain shells).
Applied to files:
web/src/__tests__/stores/toast.test.tsCLAUDE.mdweb/src/stores/toast.ts
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/styles/**/*.css : Define design tokens using --so-* CSS custom properties as a single source of truth. Bridge with Tailwind theme configuration.
Applied to files:
web/src/styles/design-tokens.cssCLAUDE.mdweb/src/components/ui/command-palette.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use token variables (var(--so-shadow-card-hover), border-border, border-bright) for shadows and borders.
Applied to files:
web/src/styles/design-tokens.cssweb/src/__tests__/hooks/useStatusTransition.test.tsCLAUDE.mdweb/src/components/ui/animated-presence.stories.tsxweb/src/components/ui/skeleton.tsxweb/src/components/ui/command-palette.tsxweb/src/hooks/useStatusTransition.ts
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : A PostToolUse hook (scripts/check_web_design_system.py) runs automatically on every Edit/Write to web/src/ files. It catches hardcoded hex colors, rgba values, fontFamily declarations, new components without Storybook stories, duplicate patterns, and complex .map() blocks. Fix all violations before proceeding - do not suppress or ignore hook output.
Applied to files:
web/src/__tests__/hooks/useCommandPalette.test.tsdocs/design/brand-and-ux.mdweb/src/__tests__/hooks/useStatusTransition.test.tsweb/src/components/ui/confirm-dialog.stories.tsxweb/src/hooks/useFlash.tsweb/src/__tests__/hooks/useFlash.test.tsweb/src/components/ui/command-palette.tsxweb/src/hooks/useStatusTransition.ts
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/hooks/**/*.ts : Create custom React hooks for auth, login lockout, WebSocket, polling, and optimistic updates.
Applied to files:
web/src/__tests__/hooks/useCommandPalette.test.tsdocs/design/brand-and-ux.mdweb/src/__tests__/hooks/useStatusTransition.test.tsCLAUDE.mdweb/src/hooks/useFlash.tsweb/src/components/ui/skeleton.tsxweb/src/hooks/useCommandPalette.tsweb/src/__tests__/hooks/useFlash.test.tsweb/src/hooks/useStatusTransition.ts
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to docs/design/**/*.md : If implementation deviates from the spec (better approach found, scope evolved, etc.), alert the user and explain why - user decides whether to proceed or update the spec. Do NOT silently diverge - every deviation needs explicit user approval.
Applied to files:
docs/design/brand-and-ux.md
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to docs/design/**/*.md : When approved deviations occur, update the relevant docs/design/ page to reflect the new reality.
Applied to files:
docs/design/brand-and-ux.md
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/.storybook/preview.tsx : Use 'definePreview' from 'storybook/react-vite' for type-safe preview configuration. Use 'parameters.backgrounds.options' (object keyed by name) + 'initialGlobals.backgrounds.value' for backgrounds API. Set 'parameters.a11y.test: "error" | "todo" | "off"' globally to enforce WCAG compliance on all stories.
Applied to files:
docs/design/brand-and-ux.mdweb/src/components/ui/command-palette.stories.tsxweb/src/components/ui/stagger-group.stories.tsxweb/src/components/ui/toast.stories.tsxweb/src/components/ui/animated-presence.stories.tsxweb/src/components/ui/confirm-dialog.stories.tsxweb/src/components/ui/error-boundary.stories.tsxweb/src/components/ui/inline-edit.stories.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : ALWAYS reuse existing components from web/src/components/ui/ before creating new ones: StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button.
Applied to files:
docs/design/brand-and-ux.mdCLAUDE.mdweb/src/components/ui/toast.tsxweb/src/components/ui/confirm-dialog.stories.tsxweb/src/components/ui/empty-state.tsxweb/src/components/ui/error-boundary.stories.tsxweb/src/components/ui/skeleton.tsxweb/src/components/ui/command-palette.tsxweb/src/components/ui/stagger-group.tsxweb/src/components/ui/error-boundary.tsx
📚 Learning: 2026-03-15T21:20:09.993Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:20:09.993Z
Learning: Applies to web/src/components/** : Vue components organized by feature (agents/, approvals/, budget/, common/, dashboard/, layout/, messages/, org-chart/, tasks/).
Applied to files:
docs/design/brand-and-ux.mdCLAUDE.md
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : 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:
docs/design/brand-and-ux.mdweb/src/__tests__/hooks/useStatusTransition.test.tsCLAUDE.mdweb/src/components/ui/animated-presence.stories.tsxweb/src/components/ui/skeleton.tsxweb/src/lib/motion.tsweb/src/components/ui/command-palette.tsxweb/src/components/ui/stagger-group.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use Tailwind semantic classes (text-foreground, bg-card, text-accent, text-success, bg-danger, etc.) or CSS variables (var(--so-accent)). NEVER hardcode hex values in .tsx/.ts files.
Applied to files:
docs/design/brand-and-ux.mdweb/src/__tests__/hooks/useStatusTransition.test.tsCLAUDE.mdweb/src/components/ui/animated-presence.stories.tsxweb/src/components/ui/skeleton.tsxweb/src/components/ui/command-palette.tsxweb/src/__tests__/components/ui/skeleton.test.tsxweb/src/hooks/useStatusTransition.ts
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/.storybook/main.ts : Use 'defineMain' from 'storybook/react-vite/node' and include explicit 'framework' field for type-safe Storybook configuration.
Applied to files:
web/src/components/ui/command-palette.stories.tsxweb/src/components/ui/stagger-group.stories.tsxweb/src/components/ui/empty-state.stories.tsxweb/src/components/ui/skeleton.stories.tsxweb/src/components/ui/toast.stories.tsxweb/src/components/ui/animated-presence.stories.tsxweb/src/components/ui/confirm-dialog.stories.tsxweb/src/components/ui/error-boundary.stories.tsxweb/src/components/ui/inline-edit.stories.tsx
📚 Learning: 2026-03-15T18:17:43.675Z
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).
Applied to files:
CLAUDE.md
📚 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 web/package.json : Web dashboard Node.js 20+; dependencies in web/package.json (Vue 3, PrimeVue, Tailwind CSS, Pinia, VueFlow, ECharts, Axios, vue-draggable-plus, Vitest, ESLint, vue-tsc)
Applied to files:
CLAUDE.md
📚 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:
CLAUDE.md
| it('destructive variant applies danger styling to confirm', () => { | ||
| render(<ConfirmDialog {...defaultProps} variant="destructive" />) | ||
| const confirmBtn = screen.getByRole('button', { name: /confirm/i }) | ||
| // Destructive buttons have the destructive variant class | ||
| expect(confirmBtn.className).toContain('destructive') | ||
| }) |
There was a problem hiding this comment.
destructive variant test is currently a false positive.
Line 72 checks for "destructive" in className, but Button base classes already include destructive-related tokens, so this can pass even for non-destructive variants.
✅ Suggested fix
it('destructive variant applies danger styling to confirm', () => {
render(<ConfirmDialog {...defaultProps} variant="destructive" />)
const confirmBtn = screen.getByRole('button', { name: /confirm/i })
- // Destructive buttons have the destructive variant class
- expect(confirmBtn.className).toContain('destructive')
+ expect(confirmBtn).toHaveAttribute('data-variant', 'destructive')
})🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/__tests__/components/ui/confirm-dialog.test.tsx` around lines 68 -
73, The current test for ConfirmDialog (rendering ConfirmDialog with
defaultProps and variant="destructive") is a false positive because Button base
classes already include destructive-related tokens; update the test to render
two variants and assert a real difference: render ConfirmDialog with
variant="destructive" and again with variant="default" (or omit variant), then
grab each confirm button (e.g., via screen.getByRole) and assert that the
destructive confirm button's className contains the destructive token while the
non-destructive confirm button's className does not (or simply assert the two
className values are not equal), referencing ConfirmDialog, defaultProps and the
confirm button element to locate the code.
| it('auto-dismisses after default duration', () => { | ||
| useToastStore.getState().add({ variant: 'info', title: 'Auto' }) | ||
| expect(useToastStore.getState().toasts).toHaveLength(1) | ||
|
|
||
| vi.advanceTimersByTime(5000) | ||
| expect(useToastStore.getState().toasts).toHaveLength(0) | ||
| }) |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider importing DEFAULT_DURATION if exported.
Line 59 uses 5000 which matches the store's DEFAULT_DURATION constant. If this constant is exported from the store, importing it would prevent test brittleness if the default changes.
♻️ Optional: Import timing constant
-import { useToastStore } from '@/stores/toast'
+import { useToastStore, DEFAULT_DURATION } from '@/stores/toast'
// ...
it('auto-dismisses after default duration', () => {
useToastStore.getState().add({ variant: 'info', title: 'Auto' })
expect(useToastStore.getState().toasts).toHaveLength(1)
- vi.advanceTimersByTime(5000)
+ vi.advanceTimersByTime(DEFAULT_DURATION)
expect(useToastStore.getState().toasts).toHaveLength(0)
})🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/__tests__/stores/toast.test.ts` around lines 55 - 61, Replace the
hardcoded 5000 in the test with the store's exported DEFAULT_DURATION to avoid
brittleness: import DEFAULT_DURATION from the toast store at the top of the test
file and change vi.advanceTimersByTime(5000) to
vi.advanceTimersByTime(DEFAULT_DURATION); this references the store's constant
(DEFAULT_DURATION) and keeps the test in sync with useToastStore.add behavior.
| @@ -0,0 +1,88 @@ | |||
| import { AlertDialog } from 'radix-ui' | |||
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check if 'radix-ui' is a declared dependency in package.json
rg -n '"radix-ui"' web/package.json
# Check for any `@radix-ui` dependencies that might indicate the expected import style
rg -n '@radix-ui' web/package.jsonRepository: Aureliolo/synthorg
Length of output: 90
🌐 Web query:
radix-ui npm package AlertDialog import
💡 Result:
To use the Radix UI AlertDialog component: 1. Install via npm: npm install @radix-ui/react-alert-dialog 2. Import and use: import { AlertDialog } from '@radix-ui/react-alert-dialog'; Or for individual parts (older style): import * as AlertDialog from '@radix-ui/react-alert-dialog'; The official Radix Primitives documentation shows the modern named export as the primary usage.
Citations:
- 1: https://www.npmjs.com/package/%40radix-ui%2Freact-alert-dialog
- 2: https://www.radix-ui.com/primitives/docs/components/alert-dialog
- 3: https://website-git-master.radix-ui.vercel.app/primitives/docs/components/alert-dialog
Use the standard Radix UI import path for AlertDialog.
The import from 'radix-ui' is non-standard. Official Radix UI components are imported from scoped packages like @radix-ui/react-alert-dialog. Update to:
import { AlertDialog } from '@radix-ui/react-alert-dialog'🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/components/ui/confirm-dialog.tsx` at line 1, The import for
AlertDialog is using a non-standard path; update the import statement that
references AlertDialog to use the official Radix package by importing from
'@radix-ui/react-alert-dialog' instead of 'radix-ui' so the component resolves
correctly (look for the import of AlertDialog in confirm-dialog.tsx and replace
its module specifier).
| const STATUS_COLOR_MAP: Record<StatusToken, string> = { | ||
| success: 'var(--so-success)', | ||
| accent: 'var(--so-accent)', | ||
| warning: 'var(--so-warning)', | ||
| danger: 'var(--so-danger)', | ||
| 'text-secondary': 'var(--so-text-secondary)', | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Unused warning key in STATUS_COLOR_MAP.
Based on the context snippet from web/src/lib/utils.ts, getStatusColor() only returns "success", "accent", "danger", or "text-secondary" for AgentRuntimeStatus. The warning key will never be accessed by this hook.
This is harmless but creates a slight mismatch between the type (StatusToken = ReturnType<typeof getStatusColor>) and the map's contents. Consider removing it for clarity, or if it's intentional for future expansion, add a comment.
♻️ Proposed cleanup
const STATUS_COLOR_MAP: Record<StatusToken, string> = {
success: 'var(--so-success)',
accent: 'var(--so-accent)',
- warning: 'var(--so-warning)',
danger: 'var(--so-danger)',
'text-secondary': 'var(--so-text-secondary)',
}📝 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.
| const STATUS_COLOR_MAP: Record<StatusToken, string> = { | |
| success: 'var(--so-success)', | |
| accent: 'var(--so-accent)', | |
| warning: 'var(--so-warning)', | |
| danger: 'var(--so-danger)', | |
| 'text-secondary': 'var(--so-text-secondary)', | |
| } | |
| const STATUS_COLOR_MAP: Record<StatusToken, string> = { | |
| success: 'var(--so-success)', | |
| accent: 'var(--so-accent)', | |
| danger: 'var(--so-danger)', | |
| 'text-secondary': 'var(--so-text-secondary)', | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/hooks/useStatusTransition.ts` around lines 10 - 16, The
STATUS_COLOR_MAP contains an unused "warning" key that doesn't match the
possible values of StatusToken returned by getStatusColor(); update
STATUS_COLOR_MAP in useStatusTransition.ts to either remove the "warning" entry
or add a clear comment explaining it's intentionally reserved for future use so
the map aligns with StatusToken (reference symbols: STATUS_COLOR_MAP,
StatusToken, getStatusColor, useStatusTransition).
…per div, IME - Toast: conditional role (alert for error, status for others), show newest toasts - InlineEdit: export props, skip save when unchanged, check isComposing for IME - ErrorBoundary: conditional wrapper div, gate componentDidCatch to dev, remove unused cn import - StaggerGroup: remove redundant cn(className), clean up unused import - CommandPalette stories: deterministic _setOpen to prevent story leaks - useCommandPalette: emit change in _reset() to update mounted hooks - Tests: scope SVG queries to container, update role assertions Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (1)
web/src/components/ui/inline-edit.tsx (1)
105-105:⚠️ Potential issue | 🟠 MajorAdd missing
valuedependency to fix the lint warning.The
savecallback referencesvalueat line 75 but omits it from the dependency array. This causes the stale closure issue flagged by the linter and could lead to incorrect unchanged-value comparisons if the parent re-renders with a newvaluewhile in edit mode.Proposed fix
- }, [editValue, onSave, validate, triggerFlash, state]) + }, [editValue, onSave, validate, triggerFlash, state, value])🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/components/ui/inline-edit.tsx` at line 105, The save callback references the prop/variable value but it is missing from its dependency array; update the dependency list on the hook that ends with "}, [editValue, onSave, validate, triggerFlash, state])" to include value (e.g. "..., state, value") so the save closure stays fresh and avoids stale-value comparisons for functions like save, and ensure the dependency order/format matches the project's linting style.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@web/src/__tests__/components/ui/inline-edit.test.tsx`:
- Around line 155-168: The test uses document.body.querySelector to find the
InlineEdit display, which is fragile; update the test that renders <InlineEdit
value={value} ... /> to destructure and use the render container (e.g., const {
container, unmount } = render(...)) and replace
document.body.querySelector('[data-inline-display]') with
container.querySelector('[data-inline-display]') so the lookup is scoped to the
render output.
- Around line 51-66: The test reveals a bug where saveInProgressRef isn't
cleared on validation failure; update the InlineEdit save path (the handler that
runs on Enter / triggers onSave — e.g., the internal handleSave/submit handler)
to set saveInProgressRef.current = false immediately when validation returns an
error (before returning) so subsequent attempts aren't blocked, and extend the
test to, after asserting 'Required' appears, type a valid value, press Enter and
assert onSave was called once; reference InlineEdit, validate prop, onSave, and
saveInProgressRef in your changes.
In `@web/src/components/ui/inline-edit.tsx`:
- Around line 43-48: The current render-time sync uses prevValueRef and calls
setEditValue(value) inside render when value !== prevValueRef.current and state
=== 'display'; move this logic into a useEffect to avoid setting state during
render: create a useEffect with [value, state] deps that checks if value !==
prevValueRef.current and state === 'display', calls setEditValue(value) and then
updates prevValueRef.current = value; keep prevValueRef and setEditValue names
intact so you only relocate the logic from render to the effect.
---
Duplicate comments:
In `@web/src/components/ui/inline-edit.tsx`:
- Line 105: The save callback references the prop/variable value but it is
missing from its dependency array; update the dependency list on the hook that
ends with "}, [editValue, onSave, validate, triggerFlash, state])" to include
value (e.g. "..., state, value") so the save closure stays fresh and avoids
stale-value comparisons for functions like save, and ensure the dependency
order/format matches the project's linting style.
🪄 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: 379b91fe-0b38-4222-8498-2e762979e0de
📒 Files selected for processing (9)
web/src/__tests__/components/ui/empty-state.test.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxweb/src/__tests__/components/ui/toast.test.tsxweb/src/components/ui/command-palette.stories.tsxweb/src/components/ui/error-boundary.tsxweb/src/components/ui/inline-edit.tsxweb/src/components/ui/stagger-group.tsxweb/src/components/ui/toast.tsxweb/src/hooks/useCommandPalette.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). (5)
- 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 (6)
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
web/src/**/*.{ts,tsx}: ALWAYS reuse existing components from web/src/components/ui/ before creating new ones: StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button.
Use Tailwind semantic classes (text-foreground, bg-card, text-accent, text-success, bg-danger, etc.) or CSS variables (var(--so-accent)). NEVER hardcode hex values in .tsx/.ts files.
Use font-sans or font-mono (maps to Geist tokens). NEVER set fontFamily directly.
Use density-aware 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 recreate status dots inline - use StatusBadge. Do NOT build card-with-header layouts from scratch - use SectionCard. Do NOT create metric displays with 'text-metric font-bold' - use MetricCard. Do NOT render initials circles manually - use Avatar. Do NOT create complex (>8 line) JSX inside .map() - extract to a shared component. Do NOT use rgba() with hardcoded values - use design token variables.
A PostToolUse hook (scripts/check_web_design_system.py) runs automatically on every Edit/Write to web/src/ files. It catches hardcoded hex colors, rgba values, fontFamily declarations, new components without Storybook stories, duplicate patterns, and complex .map() blocks. Fix all violations before proceeding - do not suppress or ignore hook output.
React 19 is required. Use React hooks patterns and functional components throughout.
Files:
web/src/__tests__/components/ui/empty-state.test.tsxweb/src/__tests__/components/ui/toast.test.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxweb/src/components/ui/command-palette.stories.tsxweb/src/components/ui/error-boundary.tsxweb/src/components/ui/stagger-group.tsxweb/src/components/ui/inline-edit.tsxweb/src/components/ui/toast.tsxweb/src/hooks/useCommandPalette.ts
**/*
📄 CodeRabbit inference engine (CLAUDE.md)
NEVER use 'cd' in Bash commands - the working directory is already set to the project root. Use absolute paths or run commands directly. Do NOT prefix commands with 'cd C:/Users/Aurelio/synthorg &&'.
Files:
web/src/__tests__/components/ui/empty-state.test.tsxweb/src/__tests__/components/ui/toast.test.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxweb/src/components/ui/command-palette.stories.tsxweb/src/components/ui/error-boundary.tsxweb/src/components/ui/stagger-group.tsxweb/src/components/ui/inline-edit.tsxweb/src/components/ui/toast.tsxweb/src/hooks/useCommandPalette.ts
web/src/**/*.test.tsx
📄 CodeRabbit inference engine (CLAUDE.md)
Run Vitest with coverage scoped to files changed vs origin/main.
Files:
web/src/__tests__/components/ui/empty-state.test.tsxweb/src/__tests__/components/ui/toast.test.tsxweb/src/__tests__/components/ui/inline-edit.test.tsx
web/src/components/ui/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
When creating new shared components, place them in web/src/components/ui/ with descriptive kebab-case filename. Create a .stories.tsx file alongside with all states (default, hover, loading, error, empty). Export props as TypeScript interface. Use design tokens exclusively - no hardcoded colors, fonts, or spacing. Import cn from
@/lib/utilsfor conditional class merging.
Files:
web/src/components/ui/command-palette.stories.tsxweb/src/components/ui/error-boundary.tsxweb/src/components/ui/stagger-group.tsxweb/src/components/ui/inline-edit.tsxweb/src/components/ui/toast.tsx
web/src/**/*.stories.tsx
📄 CodeRabbit inference engine (CLAUDE.md)
web/src/**/*.stories.tsx: Create .stories.tsx files for all new components in web/src/components/ui/ with all possible states (default, hover, loading, error, empty) to be used in Storybook.
Use Storybook 10 ESM syntax. Import test utilities from 'storybook/test' (not@storybook/test) and 'storybook/actions' (not@storybook/addon-actions). Storybook essentials (backgrounds, controls, viewport, actions, toolbars, measure, outline) and interactions are built into core storybook.
Files:
web/src/components/ui/command-palette.stories.tsx
web/src/hooks/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Create custom React hooks for auth, login lockout, WebSocket, polling, and optimistic updates.
Files:
web/src/hooks/useCommandPalette.ts
🧠 Learnings (14)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.stories.tsx : Use Storybook 10 ESM syntax. Import test utilities from 'storybook/test' (not storybook/test) and 'storybook/actions' (not storybook/addon-actions). Storybook essentials (backgrounds, controls, viewport, actions, toolbars, measure, outline) and interactions are built into core storybook.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : When creating new shared components, place them in web/src/components/ui/ with descriptive kebab-case filename. Create a .stories.tsx file alongside with all states (default, hover, loading, error, empty). Export props as TypeScript interface. Use design tokens exclusively - no hardcoded colors, fonts, or spacing. Import cn from `@/lib/utils` for conditional class merging.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.stories.tsx : Create .stories.tsx files for all new components in web/src/components/ui/ with all possible states (default, hover, loading, error, empty) to be used in Storybook.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : React 19 is required. Use React hooks patterns and functional components throughout.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do NOT recreate status dots inline - use StatusBadge. Do NOT build card-with-header layouts from scratch - use SectionCard. Do NOT create metric displays with 'text-metric font-bold' - use MetricCard. Do NOT render initials circles manually - use Avatar. Do NOT create complex (>8 line) JSX inside .map() - extract to a shared component. Do NOT use rgba() with hardcoded values - use design token variables.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/.storybook/preview.tsx : Use 'definePreview' from 'storybook/react-vite' for type-safe preview configuration. Use 'parameters.backgrounds.options' (object keyed by name) + 'initialGlobals.backgrounds.value' for backgrounds API. Set 'parameters.a11y.test: "error" | "todo" | "off"' globally to enforce WCAG compliance on all stories.
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.stories.tsx : Create .stories.tsx files for all new components in web/src/components/ui/ with all possible states (default, hover, loading, error, empty) to be used in Storybook.
Applied to files:
web/src/__tests__/components/ui/empty-state.test.tsxweb/src/__tests__/components/ui/toast.test.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxweb/src/components/ui/command-palette.stories.tsxweb/src/components/ui/stagger-group.tsxweb/src/components/ui/toast.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.stories.tsx : Use Storybook 10 ESM syntax. Import test utilities from 'storybook/test' (not storybook/test) and 'storybook/actions' (not storybook/addon-actions). Storybook essentials (backgrounds, controls, viewport, actions, toolbars, measure, outline) and interactions are built into core storybook.
Applied to files:
web/src/__tests__/components/ui/empty-state.test.tsxweb/src/__tests__/components/ui/toast.test.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxweb/src/components/ui/command-palette.stories.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : When creating new shared components, place them in web/src/components/ui/ with descriptive kebab-case filename. Create a .stories.tsx file alongside with all states (default, hover, loading, error, empty). Export props as TypeScript interface. Use design tokens exclusively - no hardcoded colors, fonts, or spacing. Import cn from `@/lib/utils` for conditional class merging.
Applied to files:
web/src/__tests__/components/ui/empty-state.test.tsxweb/src/__tests__/components/ui/toast.test.tsxweb/src/__tests__/components/ui/inline-edit.test.tsxweb/src/components/ui/command-palette.stories.tsxweb/src/components/ui/error-boundary.tsxweb/src/components/ui/stagger-group.tsxweb/src/components/ui/inline-edit.tsxweb/src/components/ui/toast.tsx
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to web/src/__tests__/**/*.{ts,js} : Dashboard testing: Vitest unit tests organized by feature under `web/src/__tests__/`. Use fast-check for property-based testing (`fc.assert` + `fc.property`).
Applied to files:
web/src/__tests__/components/ui/empty-state.test.tsxweb/src/__tests__/components/ui/toast.test.tsxweb/src/__tests__/components/ui/inline-edit.test.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/__tests__/**/*.test.ts : Use Vitest for unit and property tests. Mirror src/ directory structure. Use fast-check for property-based testing (fc.assert + fc.property).
Applied to files:
web/src/__tests__/components/ui/empty-state.test.tsxweb/src/__tests__/components/ui/toast.test.tsxweb/src/__tests__/components/ui/inline-edit.test.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do NOT recreate status dots inline - use StatusBadge. Do NOT build card-with-header layouts from scratch - use SectionCard. Do NOT create metric displays with 'text-metric font-bold' - use MetricCard. Do NOT render initials circles manually - use Avatar. Do NOT create complex (>8 line) JSX inside .map() - extract to a shared component. Do NOT use rgba() with hardcoded values - use design token variables.
Applied to files:
web/src/__tests__/components/ui/empty-state.test.tsxweb/src/components/ui/error-boundary.tsxweb/src/components/ui/stagger-group.tsxweb/src/components/ui/inline-edit.tsxweb/src/components/ui/toast.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.test.tsx : Run Vitest with coverage scoped to files changed vs origin/main.
Applied to files:
web/src/__tests__/components/ui/toast.test.tsxweb/src/__tests__/components/ui/inline-edit.test.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/.storybook/preview.tsx : Use 'definePreview' from 'storybook/react-vite' for type-safe preview configuration. Use 'parameters.backgrounds.options' (object keyed by name) + 'initialGlobals.backgrounds.value' for backgrounds API. Set 'parameters.a11y.test: "error" | "todo" | "off"' globally to enforce WCAG compliance on all stories.
Applied to files:
web/src/components/ui/command-palette.stories.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/.storybook/main.ts : Use 'defineMain' from 'storybook/react-vite/node' and include explicit 'framework' field for type-safe Storybook configuration.
Applied to files:
web/src/components/ui/command-palette.stories.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : React 19 is required. Use React hooks patterns and functional components throughout.
Applied to files:
web/src/components/ui/error-boundary.tsxweb/src/components/ui/inline-edit.tsxweb/src/components/ui/toast.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : ALWAYS reuse existing components from web/src/components/ui/ before creating new ones: StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button.
Applied to files:
web/src/components/ui/error-boundary.tsxweb/src/components/ui/toast.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/hooks/**/*.ts : Create custom React hooks for auth, login lockout, WebSocket, polling, and optimistic updates.
Applied to files:
web/src/hooks/useCommandPalette.ts
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : A PostToolUse hook (scripts/check_web_design_system.py) runs automatically on every Edit/Write to web/src/ files. It catches hardcoded hex colors, rgba values, fontFamily declarations, new components without Storybook stories, duplicate patterns, and complex .map() blocks. Fix all violations before proceeding - do not suppress or ignore hook output.
Applied to files:
web/src/hooks/useCommandPalette.ts
🪛 GitHub Check: Dashboard Lint
web/src/components/ui/inline-edit.tsx
[warning] 105-105:
React Hook useCallback has a missing dependency: 'value'. Either include it or remove the dependency array
🔇 Additional comments (28)
web/src/__tests__/components/ui/empty-state.test.tsx (1)
1-69: Well-structured test coverage for EmptyState.The test suite comprehensively covers all conditional rendering paths (icon, description, action) and layout behavior. Good use of semantic queries (
getByRole,queryByRole) and proper async handling withuserEvent.setup(). The SVG scoping feedback from the prior review has been addressed.web/src/components/ui/stagger-group.tsx (3)
5-23: Good public typing surface for shared UI primitives.Exporting both props interfaces keeps this component easy to consume from Storybook and other UI modules.
30-59: Stagger container implementation looks solid.
useMemoaround variants and theanimateon/off behavior are clean and predictable for both animated and static rendering paths.
67-85:StaggerItemcomposition is well-structured.Using shared
cardEntrancevariants withlayoutId/layoutforwarding keeps this primitive reusable for entrance and reorder scenarios.web/src/components/ui/toast.tsx (3)
1-17: LGTM!Clean imports and well-structured variant configuration using semantic design token classes (
border-l-success,border-l-danger, etc.).
24-63: LGTM!The
Toastcomponent correctly implements:
- Conditional
role(alertfor errors,statusotherwise) addressing the previous review feedback- Proper
aria-livevalues based on variant urgency- Accessible dismiss button with descriptive label
- Design token usage throughout
70-87: LGTM!The
ToastContainercorrectly:
- Uses
slice(-maxVisible)to retain the newest toasts, addressing the previous review feedback- Implements proper layering with
pointer-events-noneon container andpointer-events-autoon individual toasts- Uses
AnimatePresencewithmode="popLayout"for smooth layout transitionsweb/src/__tests__/components/ui/toast.test.tsx (3)
7-30: LGTM!Well-designed framer-motion mock that:
- Preserves accessibility attributes (
role,aria-live) required for testing- Simplifies DOM structure for deterministic assertions
- Uses proper ESLint suppression for the mock factory pattern
38-101: LGTM!Comprehensive
Toasttest coverage including:
- Content rendering (title/description)
- Dismiss callback invocation with correct ID
- Accessibility attribute assertions for all variants using
it.each- Conditional dismiss button visibility
103-131: LGTM!Solid
ToastContainerintegration tests with:
- Proper test isolation via
beforeEachcleanup usingdismissAll()- Store integration verification
- Coverage of both explicit and default
maxVisiblebehaviorweb/src/components/ui/error-boundary.tsx (6)
30-73: Add a reset mechanism when children change (e.g., route navigation).The boundary remains in error state until the retry button is clicked. When the app navigates to a new route, the component persists with stale fallback UI. Consider implementing
componentDidUpdateto detect children changes and reset:♻️ Proposed fix
class ErrorBoundaryInner extends Component< ErrorBoundaryProps & { FallbackComponent: React.ComponentType<ErrorFallbackProps> }, ErrorBoundaryState > { constructor(props: ErrorBoundaryProps & { FallbackComponent: React.ComponentType<ErrorFallbackProps> }) { super(props) this.state = { hasError: false, error: null } } static getDerivedStateFromError(error: Error): ErrorBoundaryState { return { hasError: true, error } } componentDidCatch(error: Error, info: ErrorInfo) { if (import.meta.env.DEV) { console.error('ErrorBoundary caught:', error, info.componentStack) } } + componentDidUpdate(prevProps: ErrorBoundaryProps & { FallbackComponent: React.ComponentType<ErrorFallbackProps> }) { + if (this.state.hasError && prevProps.children !== this.props.children) { + this.setState({ hasError: false, error: null }) + } + } + resetErrorBoundary = () => {Alternatively, callers can pass
key={location.pathname}at the usage site to force remount on navigation.
112-118: Use sharedButtoncomponent instead of raw<button>.The component-level fallback uses a raw
<button>while page and section levels correctly use the sharedButtoncomponent. This inconsistency violates coding guidelines.♻️ Proposed fix
<AlertTriangle className="size-4" aria-hidden="true" /> <span>{message}</span> - <button - type="button" - onClick={resetErrorBoundary} - className="text-accent underline underline-offset-2 hover:text-accent-foreground" - > + <Button + variant="link" + size="sm" + onClick={resetErrorBoundary} + className="h-auto p-0 text-accent underline underline-offset-2 hover:text-accent-foreground" + > Try Again - </button> + </Button>As per coding guidelines, "ALWAYS reuse existing components from web/src/components/ui/ before creating new ones: ... Button."
1-4: LGTM!Imports are appropriate. Class component is required for error boundaries (React still needs
getDerivedStateFromError), and the sharedButtoncomponent is correctly imported.
6-23: LGTM!Type definitions are well-structured with proper exports and JSDoc comments. The
ErrorBoundaryPropsexport issue from previous review has been addressed.
75-79: LGTM!Production-safe error messaging that hides internal details from end users while preserving full messages in development.
147-169: LGTM!The wrapper is now opt-in, rendering
ErrorBoundaryInnerdirectly when noclassNameis provided. This addresses the previous layout semantics concern.web/src/components/ui/inline-edit.tsx (2)
152-171: Input still lacks an accessible name for screen readers.The past review requested adding
inputAriaLabel/inputAriaLabelledByprops so callers can supply an accessible name. The display button hasaria-label(line 135), but the edit-mode<input>only hasaria-invalid/aria-errormessage—no programmatic label. This remains an accessibility gap for a shared UI component.
80-90:⚠️ Potential issue | 🔴 Critical
saveInProgressRefis never reset when validation fails, permanently blocking future saves.If
validate()returns an error, the earlyreturnat line 88 skips thefinallyblock, leavingsaveInProgressRef.current = trueforever. Subsequent save attempts will be blocked by the guard at line 73.🐛 Proposed fix
saveInProgressRef.current = true try { // Validate if (validate) { const validationError = validate(editValue) if (validationError) { setError(validationError) + saveInProgressRef.current = false return } }> Likely an incorrect or invalid review comment.web/src/__tests__/components/ui/inline-edit.test.tsx (2)
126-153: LGTM! Double-save prevention test is now properly structured.The test correctly uses a manually-controlled promise to verify that blur during an in-progress save doesn't trigger a second
onSavecall. This validates thesaveInProgressRefguard.
1-11: LGTM! Test setup and basic render test are correct.Imports are appropriate for RTL + userEvent + fast-check, and the initial render test correctly verifies display state behavior.
web/src/hooks/useCommandPalette.ts (5)
1-18: LGTM!The imports and
CommandIteminterface are well-defined. The interface provides comprehensive type coverage for command items including optional fields for icons, shortcuts, keywords, and scoping.
31-43: LGTM!The external store implementation correctly follows React's subscription pattern. The
commandsSnapshotapproach ensures referential stability foruseSyncExternalStore, andemitChangeproperly notifies all subscribers.
117-128: LGTM!The JSDoc note properly documents the memoization requirement for the
commandsarray, addressing the potential re-registration concern. The cleanup pattern is correct.
130-143: LGTM!The
_reset()function now correctly callsemitChange()after updating state, ensuring subscribers receive the reset notification. The underscore-prefixed exports follow a clear convention for testing utilities.
99-101:getServerSnapshotis not required —web/is a client-side React SPA built with Vite, not a server-side rendered application. TheuseCommandPalettehook runs only in the browser, never during server-side rendering. The separatesite/directory (which uses Astro) does not import or use components fromweb/src.> Likely an incorrect or invalid review comment.web/src/components/ui/command-palette.stories.tsx (3)
1-14: LGTM!Imports are correct. Meta and StoryObj types from
@storybook/reactare the appropriate Storybook 10 types for story configuration.
26-37: LGTM!The
PaletteSetuphelper now correctly initializes the palette open state deterministically with_setOpen(open ?? false), preventing state leakage between stories. The cleanup properly unregisters commands and closes the palette.
39-91: Good story coverage for the CommandPalette.The stories cover the key states: open with commands, empty results, mixed global/local scopes, and an interactive demo. The
Interactivestory correctly uses semantic Tailwind classes (text-muted-foreground).As per coding guidelines, loading/error states should be included when applicable. If the command palette supports async command loading in the future, consider adding those story variants.
…ps, test scoping - Move InlineEdit render-time value sync to useEffect (conventional pattern) - Add missing `value` to save callback dependency array (prevents stale closure) - Scope property test querySelector to render container instead of document.body - Add retry-after-validation test covering saveInProgressRef reset Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
♻️ Duplicate comments (1)
web/src/components/ui/inline-edit.tsx (1)
8-21:⚠️ Potential issue | 🟠 MajorExpose a typed accessible-name path for the edit input.
On Line 154, the textbox has no label/
aria-label/aria-labelledby, and the public props (Line 8 onward) do not let callers provide one. This keeps the shared component inaccessible in edit mode.♿ Minimal fix
export interface InlineEditProps { value: string onSave: (newValue: string) => Promise<void> + inputAriaLabel?: string + inputAriaLabelledBy?: string /** Validation function -- return error string or null. */ validate?: (value: string) => string | null @@ export function InlineEdit({ value, onSave, + inputAriaLabel, + inputAriaLabelledBy, validate, @@ <input @@ + aria-label={inputAriaLabel} + aria-labelledby={inputAriaLabelledBy} aria-invalid={error ? true : undefined} aria-errormessage={error ? errorId : undefined} />Also applies to: 154-173
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/components/ui/inline-edit.tsx` around lines 8 - 21, Add accessible name props to the component by extending InlineEditProps with either ariaLabel?: string and ariaLabelledby?: string (or a single ariaLabel prop) and then pass those through to the edit textbox element used when editing (the input/rendered textarea in the component's edit mode). Update the component to prefer aria-labelledby if provided, otherwise aria-label, and ensure the input element (the editable <input> used in edit mode) receives the chosen attribute(s) so it has an accessible name during editing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@web/src/components/ui/inline-edit.tsx`:
- Around line 8-21: Add accessible name props to the component by extending
InlineEditProps with either ariaLabel?: string and ariaLabelledby?: string (or a
single ariaLabel prop) and then pass those through to the edit textbox element
used when editing (the input/rendered textarea in the component's edit mode).
Update the component to prefer aria-labelledby if provided, otherwise
aria-label, and ensure the input element (the editable <input> used in edit
mode) receives the chosen attribute(s) so it has an accessible name during
editing.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: d04e9cf2-2f93-4f50-94a3-09f063d7ae34
📒 Files selected for processing (2)
web/src/__tests__/components/ui/inline-edit.test.tsxweb/src/components/ui/inline-edit.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). (5)
- GitHub Check: Build Sandbox
- GitHub Check: Build Backend
- GitHub Check: Build Web
- GitHub Check: Dependency Review
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (4)
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
web/src/**/*.{ts,tsx}: ALWAYS reuse existing components from web/src/components/ui/ before creating new ones: StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button.
Use Tailwind semantic classes (text-foreground, bg-card, text-accent, text-success, bg-danger, etc.) or CSS variables (var(--so-accent)). NEVER hardcode hex values in .tsx/.ts files.
Use font-sans or font-mono (maps to Geist tokens). NEVER set fontFamily directly.
Use density-aware 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 recreate status dots inline - use StatusBadge. Do NOT build card-with-header layouts from scratch - use SectionCard. Do NOT create metric displays with 'text-metric font-bold' - use MetricCard. Do NOT render initials circles manually - use Avatar. Do NOT create complex (>8 line) JSX inside .map() - extract to a shared component. Do NOT use rgba() with hardcoded values - use design token variables.
A PostToolUse hook (scripts/check_web_design_system.py) runs automatically on every Edit/Write to web/src/ files. It catches hardcoded hex colors, rgba values, fontFamily declarations, new components without Storybook stories, duplicate patterns, and complex .map() blocks. Fix all violations before proceeding - do not suppress or ignore hook output.
React 19 is required. Use React hooks patterns and functional components throughout.
Files:
web/src/__tests__/components/ui/inline-edit.test.tsxweb/src/components/ui/inline-edit.tsx
**/*
📄 CodeRabbit inference engine (CLAUDE.md)
NEVER use 'cd' in Bash commands - the working directory is already set to the project root. Use absolute paths or run commands directly. Do NOT prefix commands with 'cd C:/Users/Aurelio/synthorg &&'.
Files:
web/src/__tests__/components/ui/inline-edit.test.tsxweb/src/components/ui/inline-edit.tsx
web/src/**/*.test.tsx
📄 CodeRabbit inference engine (CLAUDE.md)
Run Vitest with coverage scoped to files changed vs origin/main.
Files:
web/src/__tests__/components/ui/inline-edit.test.tsx
web/src/components/ui/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
When creating new shared components, place them in web/src/components/ui/ with descriptive kebab-case filename. Create a .stories.tsx file alongside with all states (default, hover, loading, error, empty). Export props as TypeScript interface. Use design tokens exclusively - no hardcoded colors, fonts, or spacing. Import cn from
@/lib/utilsfor conditional class merging.
Files:
web/src/components/ui/inline-edit.tsx
🧠 Learnings (9)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.stories.tsx : Use Storybook 10 ESM syntax. Import test utilities from 'storybook/test' (not storybook/test) and 'storybook/actions' (not storybook/addon-actions). Storybook essentials (backgrounds, controls, viewport, actions, toolbars, measure, outline) and interactions are built into core storybook.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : When creating new shared components, place them in web/src/components/ui/ with descriptive kebab-case filename. Create a .stories.tsx file alongside with all states (default, hover, loading, error, empty). Export props as TypeScript interface. Use design tokens exclusively - no hardcoded colors, fonts, or spacing. Import cn from `@/lib/utils` for conditional class merging.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.stories.tsx : Create .stories.tsx files for all new components in web/src/components/ui/ with all possible states (default, hover, loading, error, empty) to be used in Storybook.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : React 19 is required. Use React hooks patterns and functional components throughout.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do NOT recreate status dots inline - use StatusBadge. Do NOT build card-with-header layouts from scratch - use SectionCard. Do NOT create metric displays with 'text-metric font-bold' - use MetricCard. Do NOT render initials circles manually - use Avatar. Do NOT create complex (>8 line) JSX inside .map() - extract to a shared component. Do NOT use rgba() with hardcoded values - use design token variables.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/.storybook/preview.tsx : Use 'definePreview' from 'storybook/react-vite' for type-safe preview configuration. Use 'parameters.backgrounds.options' (object keyed by name) + 'initialGlobals.backgrounds.value' for backgrounds API. Set 'parameters.a11y.test: "error" | "todo" | "off"' globally to enforce WCAG compliance on all stories.
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.stories.tsx : Use Storybook 10 ESM syntax. Import test utilities from 'storybook/test' (not storybook/test) and 'storybook/actions' (not storybook/addon-actions). Storybook essentials (backgrounds, controls, viewport, actions, toolbars, measure, outline) and interactions are built into core storybook.
Applied to files:
web/src/__tests__/components/ui/inline-edit.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/ui/inline-edit.test.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/__tests__/**/*.test.ts : Use Vitest for unit and property tests. Mirror src/ directory structure. Use fast-check for property-based testing (fc.assert + fc.property).
Applied to files:
web/src/__tests__/components/ui/inline-edit.test.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : When creating new shared components, place them in web/src/components/ui/ with descriptive kebab-case filename. Create a .stories.tsx file alongside with all states (default, hover, loading, error, empty). Export props as TypeScript interface. Use design tokens exclusively - no hardcoded colors, fonts, or spacing. Import cn from `@/lib/utils` for conditional class merging.
Applied to files:
web/src/__tests__/components/ui/inline-edit.test.tsxweb/src/components/ui/inline-edit.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.test.tsx : Run Vitest with coverage scoped to files changed vs origin/main.
Applied to files:
web/src/__tests__/components/ui/inline-edit.test.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.stories.tsx : Create .stories.tsx files for all new components in web/src/components/ui/ with all possible states (default, hover, loading, error, empty) to be used in Storybook.
Applied to files:
web/src/__tests__/components/ui/inline-edit.test.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : React 19 is required. Use React hooks patterns and functional components throughout.
Applied to files:
web/src/components/ui/inline-edit.tsx
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do NOT recreate status dots inline - use StatusBadge. Do NOT build card-with-header layouts from scratch - use SectionCard. Do NOT create metric displays with 'text-metric font-bold' - use MetricCard. Do NOT render initials circles manually - use Avatar. Do NOT create complex (>8 line) JSX inside .map() - extract to a shared component. Do NOT use rgba() with hardcoded values - use design token variables.
Applied to files:
web/src/components/ui/inline-edit.tsx
🪛 GitHub Check: Dashboard Lint
web/src/components/ui/inline-edit.tsx
[warning] 47-47:
Do not call the 'set' function 'setEditValue' of 'useState' synchronously in an effect. This can lead to unnecessary re-renders and performance issues
🔇 Additional comments (3)
web/src/components/ui/inline-edit.tsx (1)
73-127: Save-path concurrency guard is solid.The
saveInProgressRef+ blur guard correctly prevents Enter/blur double-submit and now safely allows retry after validation failures.web/src/__tests__/components/ui/inline-edit.test.tsx (2)
151-178: Great regression coverage for Enter+blur race.This test correctly models an in-flight save and verifies single invocation under blur pressure.
180-194: Nice use of property-based testing for display round-trip.This gives broad confidence beyond fixed examples and is a good fit for this component.
🤖 I have created a release *beep* *boop* --- #MAJOR CHANGES; We got a somewhat working webui :) ## [0.5.0](v0.4.9...v0.5.0) (2026-03-30) ### Features * add analytics trends and budget forecast API endpoints ([#798](#798)) ([16b61f5](16b61f5)) * add department policies to default templates ([#852](#852)) ([7a41548](7a41548)) * add remaining activity event types (task_started, tool_used, delegation, cost_incurred) ([#832](#832)) ([4252fac](4252fac)) * agent performance, activity, and history API endpoints ([#811](#811)) ([9b75c1d](9b75c1d)) * Agent Profiles and Detail pages (biography, career, performance) ([#874](#874)) ([62d7880](62d7880)) * app shell, Storybook, and CI/CD pipeline ([#819](#819)) ([d4dde90](d4dde90)) * Approvals page with risk grouping, urgency indicators, batch actions ([#889](#889)) ([4e9673d](4e9673d)) * Budget Panel page (P&L dashboard, breakdown charts, forecast) ([#890](#890)) ([b63b0f1](b63b0f1)) * build infrastructure layer (API client, auth, WebSocket) ([#815](#815)) ([9f01d3e](9f01d3e)) * CLI global options infrastructure, UI modes, exit codes, env vars ([#891](#891)) ([fef4fc5](fef4fc5)) * CodeMirror editor and theme preferences toggle ([#905](#905), [#807](#807)) ([#909](#909)) ([41fbedc](41fbedc)) * Company page (department/agent management) ([#888](#888)) ([cfb88b0](cfb88b0)) * comprehensive hint coverage across all CLI commands ([#900](#900)) ([937974e](937974e)) * config system extensions, per-command flags for init/start/stop/status/logs ([#895](#895)) ([32f83fe](32f83fe)) * configurable currency system replacing hardcoded USD ([#854](#854)) ([b372551](b372551)) * Dashboard page (metric cards, activity feed, budget burn) ([#861](#861)) ([7d519d5](7d519d5)) * department health, provider status, and activity feed endpoints ([#818](#818)) ([6d5f196](6d5f196)) * design tokens and core UI components ([#833](#833)) ([ed887f2](ed887f2)) * extend approval, meeting, and budget API responses ([#834](#834)) ([31472bf](31472bf)) * frontend polish -- real-time UX, accessibility, responsive, performance ([#790](#790), [#792](#792), [#791](#791), [#793](#793)) ([#917](#917)) ([f04a537](f04a537)) * implement human roles and access control levels ([#856](#856)) ([d6d8a06](d6d8a06)) * implement semantic conflict detection in workspace merge ([#860](#860)) ([d97283b](d97283b)) * interaction components and animation patterns ([#853](#853)) ([82d4b01](82d4b01)) * Login page + first-run bootstrap + Company page ([#789](#789), [#888](#888)) ([#896](#896)) ([8758e8d](8758e8d)) * Meetings page with timeline viz, token bars, contribution formatting ([#788](#788)) ([#904](#904)) ([b207f46](b207f46)) * Messages page with threading, channel badges, sender indicators ([#787](#787)) ([#903](#903)) ([28293ad](28293ad)) * Org Chart force-directed view and drag-drop reassignment ([#872](#872), [#873](#873)) ([#912](#912)) ([a68a938](a68a938)) * Org Chart page (living nodes, status, CRUD, department health) ([#870](#870)) ([0acbdae](0acbdae)) * per-command flags for remaining commands, auto-behavior wiring, help/discoverability ([#897](#897)) ([3f7afa2](3f7afa2)) * Providers page with backend rework -- health, CRUD, subscription auth ([#893](#893)) ([9f8dd98](9f8dd98)) * scaffold React + Vite + TypeScript + Tailwind project ([#799](#799)) ([bd151aa](bd151aa)) * Settings page with search, dependency indicators, grouped rendering ([#784](#784)) ([#902](#902)) ([a7b9870](a7b9870)) * Setup Wizard rebuild with template comparison, cost estimator, theme customization ([#879](#879)) ([ae8b50b](ae8b50b)) * setup wizard UX -- template filters, card metadata, provider form reuse ([#910](#910)) ([7f04676](7f04676)) * setup wizard UX overhaul -- mode choice, step reorder, provider fixes ([#907](#907)) ([ee964c4](ee964c4)) * structured ModelRequirement in template agent configs ([#795](#795)) ([7433548](7433548)) * Task Board page (rich Kanban, filtering, dependency viz) ([#871](#871)) ([04a19b0](04a19b0)) ### Bug Fixes * align frontend types with backend and debounce WS refetches ([#916](#916)) ([134c11b](134c11b)) * auto-cleanup targets newly pulled images instead of old ones ([#884](#884)) ([50e6591](50e6591)) * correct wipe backup-skip flow and harden error handling ([#808](#808)) ([c05860f](c05860f)) * improve provider setup in wizard, subscription auth, dashboard bugs ([#914](#914)) ([87bf8e6](87bf8e6)) * improve update channel detection and add config get command ([#814](#814)) ([6b137f0](6b137f0)) * resolve all ESLint warnings, add zero-warnings enforcement ([#899](#899)) ([079b46a](079b46a)) * subscription auth uses api_key, base URL optional for cloud providers ([#915](#915)) ([f0098dd](f0098dd)) ### Refactoring * semantic analyzer cleanup -- shared filtering, concurrency, extraction ([#908](#908)) ([81372bf](81372bf)) ### Documentation * brand identity and UX design system from [#765](#765) exploration ([#804](#804)) ([389a9f4](389a9f4)) * page structure and information architecture for v0.5.0 dashboard ([#809](#809)) ([f8d6d4a](f8d6d4a)) * write UX design guidelines with WCAG-verified color system ([#816](#816)) ([4a4594e](4a4594e)) ### Tests * add unit tests for agent hooks and page components ([#875](#875)) ([#901](#901)) ([1d81546](1d81546)) ### CI/CD * bump actions/deploy-pages from 4.0.5 to 5.0.0 in the major group ([#831](#831)) ([01c19de](01c19de)) * bump astral-sh/setup-uv from 7.6.0 to 8.0.0 in /.github/actions/setup-python-uv in the all group ([#920](#920)) ([5f6ba54](5f6ba54)) * bump codecov/codecov-action from 5.5.3 to 6.0.0 in the major group ([#868](#868)) ([f22a181](f22a181)) * bump github/codeql-action from 4.34.1 to 4.35.0 in the all group ([#883](#883)) ([87a4890](87a4890)) * bump sigstore/cosign-installer from 4.1.0 to 4.1.1 in the minor-and-patch group ([#830](#830)) ([7a69050](7a69050)) * bump the all group with 3 updates ([#923](#923)) ([ff27c8e](ff27c8e)) * bump wrangler from 4.76.0 to 4.77.0 in /.github in the minor-and-patch group ([#822](#822)) ([07d43eb](07d43eb)) * bump wrangler from 4.77.0 to 4.78.0 in /.github in the all group ([#882](#882)) ([f84118d](f84118d)) ### Maintenance * add design system enforcement hook and component inventory ([#846](#846)) ([15abc43](15abc43)) * add dev-only auth bypass for frontend testing ([#885](#885)) ([6cdcd8a](6cdcd8a)) * add pre-push rebase check hook ([#855](#855)) ([b637a04](b637a04)) * backend hardening -- eviction/size-caps and model validation ([#911](#911)) ([81253d9](81253d9)) * bump axios from 1.13.6 to 1.14.0 in /web in the all group across 1 directory ([#922](#922)) ([b1b0232](b1b0232)) * bump brace-expansion from 5.0.4 to 5.0.5 in /web ([#862](#862)) ([ba4a565](ba4a565)) * bump eslint-plugin-react-refresh from 0.4.26 to 0.5.2 in /web ([#801](#801)) ([7574bb5](7574bb5)) * bump faker from 40.11.0 to 40.11.1 in the minor-and-patch group ([#803](#803)) ([14d322e](14d322e)) * bump https://github.com/astral-sh/ruff-pre-commit from v0.15.7 to 0.15.8 ([#864](#864)) ([f52901e](f52901e)) * bump nginxinc/nginx-unprivileged from `6582a34` to `f99cc61` in /docker/web in the all group ([#919](#919)) ([df85e4f](df85e4f)) * bump nginxinc/nginx-unprivileged from `ccbac1a` to `6582a34` in /docker/web ([#800](#800)) ([f4e9450](f4e9450)) * bump node from `44bcbf4` to `71be405` in /docker/sandbox ([#827](#827)) ([91bec67](91bec67)) * bump node from `5209bca` to `cf38e1f` in /docker/web ([#863](#863)) ([66d6043](66d6043)) * bump picomatch in /site ([#842](#842)) ([5f20bcc](5f20bcc)) * bump recharts 2->3 and @types/node 22->25 in /web ([#802](#802)) ([a908800](a908800)) * Bump requests from 2.32.5 to 2.33.0 ([#843](#843)) ([41daf69](41daf69)) * bump smol-toml from 1.6.0 to 1.6.1 in /site ([#826](#826)) ([3e5dbe4](3e5dbe4)) * bump the all group with 3 updates ([#921](#921)) ([7bace0b](7bace0b)) * bump the minor-and-patch group across 1 directory with 2 updates ([#829](#829)) ([93e611f](93e611f)) * bump the minor-and-patch group across 1 directory with 3 updates ([#841](#841)) ([7010c8e](7010c8e)) * bump the minor-and-patch group across 1 directory with 3 updates ([#869](#869)) ([548cee5](548cee5)) * bump the minor-and-patch group in /site with 2 updates ([#865](#865)) ([9558101](9558101)) * bump the minor-and-patch group with 2 updates ([#867](#867)) ([4830706](4830706)) * consolidate Dependabot groups to 1 PR per ecosystem ([06d2556](06d2556)) * consolidate Dependabot groups to 1 PR per ecosystem ([#881](#881)) ([06d2556](06d2556)) * improve worktree skill with full dep sync and status enhancements ([#906](#906)) ([772c625](772c625)) * remove Vue remnants and document framework decision ([#851](#851)) ([bf2adf6](bf2adf6)) * update web dependencies and fix brace-expansion CVE ([#880](#880)) ([a7a0ed6](a7a0ed6)) * upgrade to Storybook 10 and TypeScript 6 ([#845](#845)) ([52d95f2](52d95f2)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Summary
prefers-reduced-motionTest plan
npm --prefix web run type-checkpassesnpm --prefix web run lintpasses (0 errors, 0 warnings)npm --prefix web run testpasses (482/482 tests, 46 test files)npm --prefix web run buildsucceedsnpm --prefix web run storybookCloses #776