feat: design tokens and core UI components#833
Conversation
Replace the old brand/surface color palette with the Warm Ops design system. All --so-* CSS custom properties in design-tokens.css are the single source of truth; global.css references them via @theme inline so Tailwind utilities resolve at runtime. This enables theme/density switching by overriding CSS variables. Add 9 core components (Avatar, StatusBadge, Sparkline, StatPill, SectionCard, ProgressGauge, DeptHealthBar, MetricCard, AgentCard) each with Storybook stories and unit tests. Migrate all existing layout components and pages from old color tokens to the new system. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix division-by-zero in Sparkline when data has 1 element - Fix division-by-zero in MetricCard when progress.total is 0 - Add animated draw to Sparkline (stroke-dasharray + fade-in) - Replace hardcoded shadow in AgentCard with --so-shadow-card-hover token - Add aria-label to MetricCard progressbar - Replace hardcoded text-white with text-foreground in SidebarNavItem - Add borderColor prop to Avatar for department-colored borders - Add .density-medium level to design-tokens.css - Add bg-card-hover to brand-and-ux.md color token table Pre-reviewed by 3 agents, 14 findings addressed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (2)
📜 Recent review details⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
🧰 Additional context used📓 Path-based instructions (2)web/src/**/*.{ts,tsx}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
web/src/**/*.test.{ts,tsx}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
🧠 Learnings (5)📓 Common learnings📚 Learning: 2026-03-24T22:47:39.223ZApplied to files:
📚 Learning: 2026-03-20T08:28:32.845ZApplied to files:
📚 Learning: 2026-03-24T22:47:39.223ZApplied to files:
📚 Learning: 2026-03-24T22:47:39.223ZApplied to files:
🔇 Additional comments (6)
WalkthroughIntroduces a design-token system and integrates it into global styles (new Suggested labels
🚥 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 |
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly advances the application's UI by implementing a new, comprehensive design system based on Warm Ops tokens. It introduces a robust set of core UI components, each thoroughly tested and documented in Storybook, and establishes a clear, maintainable structure for styling through CSS variables. The changes ensure visual consistency, improve accessibility, and lay the groundwork for future theme and density customization, while also migrating existing UI elements to the new system. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
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. OpenSSF Scorecard
Scanned Files
|
There was a problem hiding this comment.
Code Review
This pull request introduces a comprehensive refactor of the UI's styling and component architecture. It establishes a new design token system via design-tokens.css, bridging these tokens to Tailwind CSS for dynamic theming and consistent styling across the application. Several new UI components, including AgentCard, Avatar, DeptHealthBar, MetricCard, ProgressGauge, SectionCard, Sparkline, StatPill, and StatusBadge, have been added, each with corresponding Storybook stories and unit tests. Existing layout components have been updated to utilize the new semantic styling. The review comments primarily focus on ensuring strict adherence to the new design system, specifically highlighting instances where arbitrary pixel values are used for font sizes in new UI components instead of predefined typography tokens, and an inconsistency in spacing within the design-tokens.css file.
web/src/components/ui/agent-card.tsx
Outdated
| <Avatar name={name} size="md" /> | ||
| <div className="min-w-0 flex-1"> | ||
| <div className="flex items-center gap-2"> | ||
| <span className="truncate text-[13px] font-semibold text-foreground"> |
There was a problem hiding this comment.
web/src/components/ui/agent-card.tsx
Outdated
| )} | ||
| {timestamp && ( | ||
| <div className="mt-0.5 text-right"> | ||
| <span className="font-mono text-[10px] text-muted-foreground">{timestamp}</span> |
web/src/components/ui/avatar.tsx
Outdated
| import { cn } from '@/lib/utils' | ||
|
|
||
| const SIZE_CLASSES = { | ||
| sm: 'size-6 text-[10px]', |
| > | ||
| {/* Top row: label + sparkline */} | ||
| <div className="flex items-start justify-between"> | ||
| <span className="text-[11px] uppercase tracking-[0.06em] text-muted-foreground"> |
| </div> | ||
|
|
||
| {/* Value */} | ||
| <div className="mt-1 font-mono text-[26px] font-bold leading-tight tracking-tight text-foreground"> |
| sm: { radius: 32, stroke: 6, valueSize: 'text-sm', labelSize: 'text-[10px]' }, | ||
| md: { radius: 48, stroke: 6, valueSize: 'text-lg', labelSize: 'text-[11px]' }, |
There was a problem hiding this comment.
web/src/components/ui/stat-pill.tsx
Outdated
| className, | ||
| )} | ||
| > | ||
| <span className="text-[11px] uppercase tracking-wide text-muted-foreground"> |
web/src/styles/design-tokens.css
Outdated
| } | ||
|
|
||
| .density-medium { | ||
| --so-density-card-padding: 14px; |
There was a problem hiding this comment.
The value 14px for --so-density-card-padding in the .density-medium class deviates from the 4px grid system mentioned on line 47. While this is documented in brand-and-ux.md, it introduces an inconsistency. To maintain the grid system, consider using a value from the spacing scale, like var(--so-space-3) (12px).
| --so-density-card-padding: 14px; | |
| --so-density-card-padding: var(--so-space-3); |
There was a problem hiding this comment.
Actionable comments posted: 16
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
web/src/__tests__/components/layout/SidebarNavItem.test.tsx (1)
1-75: 🧹 Nitpick | 🔵 TrivialConsider adding property-based tests for badge logic.
The badge capping logic (Lines 32-38) and count display would benefit from property-based testing to verify behavior across arbitrary numeric inputs.
As per coding guidelines: "Use fast-check for property-based testing in React (fc.assert + fc.property). Always include unit and property tests with Vitest."
♻️ Example property-based test for badge capping
import * as fc from 'fast-check' it('caps badge display at 99+ for any count > 99', () => { fc.assert( fc.property(fc.integer({ min: 100, max: 10000 }), (count) => { renderWithRouter( <SidebarNavItem to="/test" icon={Users} label="Test" collapsed={false} badge={count} />, ) expect(screen.getByText('99+')).toBeInTheDocument() cleanup() }), ) }) it('displays exact count for badge values 1-99', () => { fc.assert( fc.property(fc.integer({ min: 1, max: 99 }), (count) => { renderWithRouter( <SidebarNavItem to="/test" icon={Users} label="Test" collapsed={false} badge={count} />, ) expect(screen.getByText(String(count))).toBeInTheDocument() cleanup() }), ) })🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/__tests__/components/layout/SidebarNavItem.test.tsx` around lines 1 - 75, Add property-based tests using fast-check to validate SidebarNavItem's badge logic: import fast-check (fc) in the test file and add two fc.assert + fc.property cases that render <SidebarNavItem ... badge={count} /> for a range of counts; one property for counts >=100 asserting the rendered badge shows "99+", and one for counts 1–99 asserting the rendered badge shows the exact count string; ensure you call cleanup() between iterations and reference the existing renderWithRouter and screen utilities to locate rendering assertions.web/src/components/layout/SidebarNavItem.tsx (1)
44-51:⚠️ Potential issue | 🔴 CriticalFix badge text contrast on danger background to meet WCAG AA (4.5:1 required).
The current combination of
text-foreground(#e2e8f0) onbg-danger(#ef4444) produces a contrast ratio of 3.05:1, which fails WCAG AA accessibility standards for small text. Consider using a darker text color or adjusting the background color to achieve at least 4.5:1 contrast.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/components/layout/SidebarNavItem.tsx` around lines 44 - 51, In SidebarNavItem (the span that renders the badge inside the component), the combination of 'text-foreground' on 'bg-danger' fails WCAG AA; update the badge styling to use a higher-contrast text color (for example replace 'text-foreground' with a darker utility like 'text-neutral-900' or a dedicated 'text-on-danger' token) or adjust the background to a darker danger variant so the contrast ratio is at least 4.5:1; ensure the change is applied to the span that renders {badge > 99 ? '99+' : badge} and run a contrast check to confirm compliance.
🤖 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/agent-card.test.tsx`:
- Around line 4-71: The tests lack property-based coverage for initials
generation; add a fast-check test that asserts AgentCard's rendered avatar
initials match the component's initials logic across arbitrary names. Create a
test using fc.assert(fc.property(fc.string(), name => { render(<AgentCard
name={name} .../>); const expected = <compute expected initials by replicating
AgentCard's algorithm or call getInitials(name) if exported>;
expect(screen.getByText(expected)).toBeInTheDocument(); })), and include
edge-case generators/filters for empty strings, whitespace, single-word,
multi-word, hyphens and non-alpha characters; use fc and fc.property with clear
shrinking.
In `@web/src/__tests__/components/ui/avatar.test.tsx`:
- Around line 47-51: The test in avatar.test.tsx uses screen.getByRole('img', {
hidden: true }) unnecessarily; update the assertion in the "handles empty name
gracefully" test to call screen.getByRole('img') (remove the { hidden: true }
option) so the test asserts the visible Avatar rendered by render(<Avatar
name="" />) without implying it's hidden.
- Around line 4-52: Add a property-based test using fast-check that asserts
getInitials (or Avatar when using its exported helper) behaves correctly for
arbitrary non-empty names: import fc and write
fc.assert(fc.property(fc.string({minLength:1}), name => { const initials =
getInitials(name); const words = name.trim().split(/\s+/).filter(Boolean); const
expected = words.length === 1 ? words[0][0].toUpperCase() :
(words[0][0]+words[words.length-1][0]).toUpperCase(); return initials ===
expected; })); Place this new test in the same test file (avatar.test.tsx)
alongside existing tests, ensuring you import getInitials from the Avatar module
(or call Avatar rendering and extract text) and handle trimming/empty-word cases
as shown.
In `@web/src/__tests__/components/ui/dept-health-bar.test.tsx`:
- Around line 29-39: Add property-based tests for DeptHealthBar's health
clamping using fast-check: import * as fc from 'fast-check' and cleanup from
'@testing-library/react', then add three fc.assert(fc.property(...)) tests that
render DeptHealthBar (symbol: DeptHealthBar) and assert that values >100 render
"100%", negative values render "0%", and values 0–100 render the exact
`${health}%`; call cleanup() after each property invocation to unmount between
cases and keep existing unit tests intact.
In `@web/src/__tests__/components/ui/metric-card.test.tsx`:
- Around line 4-69: Add fast-check property-based tests alongside existing unit
tests to exercise MetricCard across ranges: use fc.assert + fc.property to (1)
render MetricCard with arbitrary numeric value (fc.integer or fc.float) and
assert screen.getByText(String(value)) is present (reference the MetricCard
component and the numeric value test), (2) supply arbitrary change.value
(fc.integer 0..100) with fc.constantFrom<'up'|'down'> and assert the badge text
matches the formatted prefix (`+` or `-`) plus `${value}%` (reference the change
prop and the change badge tests), and (3) test progress percentage computation
by generating pairs (current,total) with total > 0 and asserting the rendered
progress percent or progressbar attributes reflect
Math.round((current/total)*100) (reference the progress prop and progressbar
test); follow existing test patterns (render/unmount, use screen/container) and
keep property tests next to the corresponding unit tests.
In `@web/src/__tests__/components/ui/progress-gauge.test.tsx`:
- Around line 4-56: Add property-based tests using fast-check to verify clamping
and percentage computation for ProgressGauge: import fc and use
fc.assert(fc.property(...)) to generate random integers for value (fc.integer())
and positive max (fc.integer({min:1})), render <ProgressGauge value={value}
max={max} />, compute expectedClamped = Math.max(0, Math.min(value, max)) and
expectedPercent = Math.round((expectedClamped / max) * 100), then assert the
rendered percentage text `${expectedPercent}%` appears and the gauge role
(getByRole('meter')) has aria-valuenow equal to String(expectedClamped) and
aria-valuemin='0' aria-valuemax=String(max); include a separate property to test
behavior when max is missing/undefined by using a default max of 100 in the
expectation. Reference ProgressGauge, getByRole('meter'), and the percentage
text assertions when adding these tests.
In `@web/src/__tests__/components/ui/section-card.test.tsx`:
- Around line 5-51: Add a property-based test in the SectionCard test suite that
asserts any non-empty title string renders: use fast-check's fc.assert and
fc.property to generate titles (e.g., fc.string({minLength:1, maxLength:100})),
render SectionCard with the generated title, assert screen.getByText(title) is
in the document, and call cleanup() after each property check; reference the
existing test file and helpers (SectionCard, render, screen, cleanup) and use
fc.assert + fc.property to implement the check.
In `@web/src/__tests__/components/ui/sparkline.test.tsx`:
- Around line 4-61: Add property-based tests using fast-check in the existing
Sparkline test suite: import fc from 'fast-check' and use
fc.assert(fc.property(...)) to generate arbitrary arrays and numeric
widths/heights; for each generated case render(<Sparkline data={data} width={w}
height={h} />) and assert the invariants: if data.length > 1 then
container.querySelector('svg') and container.querySelector('polyline') exist;
svg has attributes width and height equal to the provided w/h; and computed
point coordinates (inspect polyline.getAttribute('points') and any circle cx/cy)
contain only finite numbers (no NaN/Infinity). Ensure tests reference the
Sparkline component, props data/width/height, and DOM queries 'svg', 'polyline',
and 'points' when implementing these property-based assertions.
In `@web/src/__tests__/components/ui/stat-pill.test.tsx`:
- Around line 4-35: Add a property-based test using fast-check to ensure
arbitrary label/value pairs render without errors: import fc and use
fc.assert(fc.property(...)) to generate random strings/numbers (e.g.,
fc.string(), fc.oneof(fc.string(), fc.integer())) and inside the property
render(<StatPill label={label} value={value} />) and assert no exceptions and
that the DOM contains the rendered label or value via screen.getByText; place
this new test in the existing StatPill test suite alongside the other it(...)
cases and use the existing render and screen utilities.
In `@web/src/components/ui/dept-health-bar.tsx`:
- Around line 57-61: The stats row always uses plural labels; update the JSX in
DeptHealthBar (the spans that render {agentCount} agents and {taskCount} tasks)
to pluralize correctly by switching to singular when count === 1 (e.g., use
conditional expressions or a small helper like pluralize(count, "agent",
"agents")/pluralize(count, "task", "tasks") and replace the literal strings so
it renders "1 agent" and "1 task" correctly).
- Around line 3-8: The BAR_COLOR_CLASSES mapping is typed as Record<string,
string> but should use the specific SemanticColor type returned by
getHealthColor() to ensure exhaustiveness; change the declaration of
BAR_COLOR_CLASSES to use Record<SemanticColor, string> (or declare it as a const
with keys typed to SemanticColor) and update any related usages so the compiler
enforces that every SemanticColor key is present and mismatches are caught at
compile time (reference symbols: BAR_COLOR_CLASSES and getHealthColor).
In `@web/src/components/ui/metric-card.stories.tsx`:
- Around line 72-100: The MetricGrid story defines an unused args object which
is ignored by its custom render function; remove the unused args property from
the MetricGrid Story definition (or alternatively update the render function to
accept and pass through args) so that the story is not misleading—locate the
MetricGrid export and either delete the args: { label: 'Tasks Today', value: 24
} entry or refactor the render to accept args and apply them to the MetricCard
components.
In `@web/src/components/ui/metric-card.tsx`:
- Around line 84-100: The ChangeBadge component currently renders only visual
cues and raw text (e.g., "+12%") which is not descriptive for screen readers;
update ChangeBadge to include an accessible label by adding an aria-label (or an
off-screen visually hidden span) that clearly describes the change like
"increase of 12 percent" or "decrease of 5 percent" based on the direction prop,
using the component's props (value and direction) to build the label; ensure you
apply the aria-label to the root <span> in ChangeBadge so assistive technologies
receive the full semantic description.
In `@web/src/components/ui/progress-gauge.stories.tsx`:
- Around line 46-56: The AllThresholds story defines an unused args property
(args: { value: 50 }) that is ignored because a custom render function returns
fixed ProgressGauge instances; remove the redundant args field from the
AllThresholds export (or alternatively update the render to use args.value if
you intend to use args) so the story no longer contains dead configuration—look
for the AllThresholds constant and its args entry and delete it (or wire
args.value into the render) to match the pattern used for MetricGrid.
In `@web/src/components/ui/progress-gauge.tsx`:
- Around line 30-32: The calculation of percentage in progress-gauge.tsx can
divide by zero when max is 0; change the logic so percentage is computed only
when max > 0 (e.g., percentage = max > 0 ? Math.round((clampedValue / max) *
100) : 0) and then pass that safe percentage into getHealthColor(percentage) and
any SVG math to avoid NaN propagation; update references to percentage,
clampedValue and max accordingly.
In `@web/src/styles/design-tokens.css`:
- Around line 96-100: Replace the hardcoded 14px in the .density-medium rule for
--so-density-card-padding with a spacing token to match the existing scale:
either use an existing token (e.g., --so-space-3 or --so-space-4) or add a new
token (e.g., --so-space-3-5 or --so-space-3_5) to the spacing scale and
reference that token here; update .density-medium to assign
--so-density-card-padding to the chosen spacing variable and, if adding a new
token, add it to the central spacing definitions and document the reason for the
intermediate value.
---
Outside diff comments:
In `@web/src/__tests__/components/layout/SidebarNavItem.test.tsx`:
- Around line 1-75: Add property-based tests using fast-check to validate
SidebarNavItem's badge logic: import fast-check (fc) in the test file and add
two fc.assert + fc.property cases that render <SidebarNavItem ... badge={count}
/> for a range of counts; one property for counts >=100 asserting the rendered
badge shows "99+", and one for counts 1–99 asserting the rendered badge shows
the exact count string; ensure you call cleanup() between iterations and
reference the existing renderWithRouter and screen utilities to locate rendering
assertions.
In `@web/src/components/layout/SidebarNavItem.tsx`:
- Around line 44-51: In SidebarNavItem (the span that renders the badge inside
the component), the combination of 'text-foreground' on 'bg-danger' fails WCAG
AA; update the badge styling to use a higher-contrast text color (for example
replace 'text-foreground' with a darker utility like 'text-neutral-900' or a
dedicated 'text-on-danger' token) or adjust the background to a darker danger
variant so the contrast ratio is at least 4.5:1; ensure the change is applied to
the span that renders {badge > 99 ? '99+' : badge} and run a contrast check to
confirm compliance.
🪄 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: ccbc588f-5256-4e04-ada7-fff65a05c00a
⛔ Files ignored due to path filters (1)
web/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (44)
docs/design/brand-and-ux.mdweb/.storybook/preview.tsxweb/package.jsonweb/src/__tests__/components/layout/SidebarNavItem.test.tsxweb/src/__tests__/components/ui/agent-card.test.tsxweb/src/__tests__/components/ui/avatar.test.tsxweb/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/__tests__/components/ui/metric-card.test.tsxweb/src/__tests__/components/ui/progress-gauge.test.tsxweb/src/__tests__/components/ui/section-card.test.tsxweb/src/__tests__/components/ui/sparkline.test.tsxweb/src/__tests__/components/ui/stat-pill.test.tsxweb/src/__tests__/components/ui/status-badge.test.tsxweb/src/components/layout/AppLayout.tsxweb/src/components/layout/PlaceholderPage.tsxweb/src/components/layout/Sidebar.tsxweb/src/components/layout/SidebarNavItem.tsxweb/src/components/layout/StatusBar.tsxweb/src/components/ui/agent-card.stories.tsxweb/src/components/ui/agent-card.tsxweb/src/components/ui/avatar.stories.tsxweb/src/components/ui/avatar.tsxweb/src/components/ui/dept-health-bar.stories.tsxweb/src/components/ui/dept-health-bar.tsxweb/src/components/ui/metric-card.stories.tsxweb/src/components/ui/metric-card.tsxweb/src/components/ui/progress-gauge.stories.tsxweb/src/components/ui/progress-gauge.tsxweb/src/components/ui/section-card.stories.tsxweb/src/components/ui/section-card.tsxweb/src/components/ui/sparkline.stories.tsxweb/src/components/ui/sparkline.tsxweb/src/components/ui/stat-pill.stories.tsxweb/src/components/ui/stat-pill.tsxweb/src/components/ui/status-badge.stories.tsxweb/src/components/ui/status-badge.tsxweb/src/lib/utils.tsweb/src/pages/LoginPage.tsxweb/src/pages/NotFoundPage.tsxweb/src/pages/SetupPage.tsxweb/src/router/guards.tsxweb/src/router/index.tsxweb/src/styles/design-tokens.cssweb/src/styles/global.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). (4)
- GitHub Check: Build Backend
- GitHub Check: Build Web
- GitHub Check: Analyze (python)
- GitHub Check: Dependency Review
🧰 Additional context used
📓 Path-based instructions (4)
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
web/src/**/*.{ts,tsx}: Use vendor-agnostic naming: NEVER use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, or tests. Use generic names: example-provider, example-large-001, example-medium-001, example-small-001
React 19 with shadcn/ui + Tailwind CSS 4. Use React hooks (auth, login lockout, WebSocket, polling, optimistic updates), Zustand stores, and lazy-loaded page components. Maintain component organization: ui/ (shadcn primitives), layout/ (app shell, sidebar, status bar), feature dirs for pages
Never skip, dismiss, or ignore flaky tests -- always fix them fully. Mock timing functions for determinism
Files:
web/src/components/layout/AppLayout.tsxweb/src/pages/SetupPage.tsxweb/src/router/index.tsxweb/src/components/layout/PlaceholderPage.tsxweb/src/router/guards.tsxweb/src/__tests__/components/layout/SidebarNavItem.test.tsxweb/src/pages/LoginPage.tsxweb/src/__tests__/components/ui/stat-pill.test.tsxweb/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/components/ui/dept-health-bar.stories.tsxweb/src/pages/NotFoundPage.tsxweb/src/__tests__/components/ui/section-card.test.tsxweb/src/__tests__/components/ui/avatar.test.tsxweb/src/components/ui/agent-card.stories.tsxweb/src/__tests__/components/ui/progress-gauge.test.tsxweb/src/__tests__/components/ui/sparkline.test.tsxweb/src/__tests__/components/ui/agent-card.test.tsxweb/src/components/layout/SidebarNavItem.tsxweb/src/__tests__/components/ui/status-badge.test.tsxweb/src/components/layout/Sidebar.tsxweb/src/__tests__/components/ui/metric-card.test.tsxweb/src/components/ui/stat-pill.tsxweb/src/lib/utils.tsweb/src/components/ui/avatar.stories.tsxweb/src/components/ui/avatar.tsxweb/src/components/ui/section-card.tsxweb/src/components/ui/agent-card.tsxweb/src/components/ui/dept-health-bar.tsxweb/src/components/ui/sparkline.stories.tsxweb/src/components/ui/status-badge.stories.tsxweb/src/components/ui/status-badge.tsxweb/src/components/ui/stat-pill.stories.tsxweb/src/components/layout/StatusBar.tsxweb/src/components/ui/metric-card.stories.tsxweb/src/components/ui/progress-gauge.tsxweb/src/components/ui/metric-card.tsxweb/src/components/ui/sparkline.tsxweb/src/components/ui/section-card.stories.tsxweb/src/components/ui/progress-gauge.stories.tsx
web/src/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use fast-check for property-based testing in React (
@fc.assert+@fc.property). Always include unit and property tests with Vitest. Coverage should scope to files changed vs origin/main
Files:
web/src/__tests__/components/layout/SidebarNavItem.test.tsxweb/src/__tests__/components/ui/stat-pill.test.tsxweb/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/__tests__/components/ui/section-card.test.tsxweb/src/__tests__/components/ui/avatar.test.tsxweb/src/__tests__/components/ui/progress-gauge.test.tsxweb/src/__tests__/components/ui/sparkline.test.tsxweb/src/__tests__/components/ui/agent-card.test.tsxweb/src/__tests__/components/ui/status-badge.test.tsxweb/src/__tests__/components/ui/metric-card.test.tsx
docs/**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
Documentation is built with Zensical (config: mkdocs.yml). Docs structure: docs/design/ (10 pages), docs/architecture/, docs/roadmap/, docs/security.md, docs/licensing.md, docs/reference/, docs/rest-api.md + docs/_generated/api-reference.html (generated by scripts/export_openapi.py), docs/api/ (auto-generated via mkdocstrings + Griffe AST-based). Landing page: site/ (Astro with /get/ CLI install page, contact form, SEO)
Files:
docs/design/brand-and-ux.md
docs/design/**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
Design spec pages are the mandatory starting point for architecture, data models, and behavior. Always read the relevant docs/design/ page before implementing features. If implementation deviates from spec, alert the user with explanation. Update docs/design/ pages when deviations are approved
Files:
docs/design/brand-and-ux.md
🧠 Learnings (9)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T22:47:39.223Z
Learning: Applies to web/src/**/*.{ts,tsx} : React 19 with shadcn/ui + Tailwind CSS 4. Use React hooks (auth, login lockout, WebSocket, polling, optimistic updates), Zustand stores, and lazy-loaded page components. Maintain component organization: ui/ (shadcn primitives), layout/ (app shell, sidebar, status bar), feature dirs for pages
📚 Learning: 2026-03-24T22:47:39.223Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T22:47:39.223Z
Learning: Applies to web/src/**/*.{ts,tsx} : React 19 with shadcn/ui + Tailwind CSS 4. Use React hooks (auth, login lockout, WebSocket, polling, optimistic updates), Zustand stores, and lazy-loaded page components. Maintain component organization: ui/ (shadcn primitives), layout/ (app shell, sidebar, status bar), feature dirs for pages
Applied to files:
web/src/components/layout/AppLayout.tsxweb/src/pages/SetupPage.tsxweb/src/router/index.tsxweb/package.jsonweb/src/components/layout/PlaceholderPage.tsxweb/src/router/guards.tsxweb/src/pages/LoginPage.tsxweb/src/__tests__/components/ui/stat-pill.test.tsxweb/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/components/ui/dept-health-bar.stories.tsxweb/src/__tests__/components/ui/section-card.test.tsxweb/src/components/ui/agent-card.stories.tsxweb/src/__tests__/components/ui/agent-card.test.tsxweb/src/components/layout/SidebarNavItem.tsxweb/src/__tests__/components/ui/status-badge.test.tsxweb/src/components/layout/Sidebar.tsxweb/src/components/ui/stat-pill.tsxweb/src/lib/utils.tsweb/src/components/ui/avatar.stories.tsxweb/src/components/ui/avatar.tsxweb/src/components/ui/section-card.tsxweb/src/components/ui/agent-card.tsxweb/src/components/ui/dept-health-bar.tsxweb/src/components/ui/status-badge.stories.tsxweb/src/components/ui/status-badge.tsxweb/src/components/layout/StatusBar.tsxweb/src/components/ui/progress-gauge.tsxweb/src/components/ui/metric-card.tsxweb/src/components/ui/sparkline.tsxweb/src/components/ui/section-card.stories.tsxweb/src/components/ui/progress-gauge.stories.tsx
📚 Learning: 2026-03-14T15:43:05.601Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T15:43:05.601Z
Learning: Applies to 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:
web/package.json
📚 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:
web/package.json
📚 Learning: 2026-03-24T22:47:39.224Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T22:47:39.224Z
Learning: All dependencies in pyproject.toml use pinned versions (==). Groups: test (pytest+plugins, hypothesis), dev (test+ruff, mypy, pre-commit, commitizen, pip-audit). Install: uv sync (installs everything, dev group default). Required deps: mem0ai (Mem0 memory backend), cryptography (Fernet encryption), faker (agent name generation). Web dashboard: Node.js 22+, React 19, react-router, shadcn/ui, Radix UI, Tailwind 4, Zustand, tanstack/react-query, xyflow/react, Recharts, Framer Motion, cmdk, Axios, Lucide React, Storybook, Vitest, vitest/coverage-v8, testing-library/react, fast-check, ESLint, eslint-react/eslint-plugin, eslint-plugin-security. CLI: Go 1.26+, Cobra, charmbracelet/huh, charmbracelet/lipgloss, sigstore-go, go-containerregistry, go-tuf
Applied to files:
web/package.json
📚 Learning: 2026-03-24T22:47:39.223Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T22:47:39.223Z
Learning: Applies to web/src/**/*.test.{ts,tsx} : Use fast-check for property-based testing in React (fc.assert + fc.property). Always include unit and property tests with Vitest. Coverage should scope to files changed vs origin/main
Applied to files:
web/src/__tests__/components/layout/SidebarNavItem.test.tsxweb/src/__tests__/components/ui/stat-pill.test.tsxweb/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/__tests__/components/ui/section-card.test.tsxweb/src/__tests__/components/ui/avatar.test.tsxweb/src/__tests__/components/ui/progress-gauge.test.tsxweb/src/__tests__/components/ui/sparkline.test.tsxweb/src/__tests__/components/ui/agent-card.test.tsxweb/src/__tests__/components/ui/status-badge.test.tsxweb/src/__tests__/components/ui/metric-card.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/stat-pill.test.tsxweb/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/__tests__/components/ui/section-card.test.tsxweb/src/__tests__/components/ui/avatar.test.tsxweb/src/__tests__/components/ui/progress-gauge.test.tsxweb/src/__tests__/components/ui/sparkline.test.tsxweb/src/__tests__/components/ui/agent-card.test.tsxweb/src/__tests__/components/ui/status-badge.test.tsxweb/src/__tests__/components/ui/metric-card.test.tsx
📚 Learning: 2026-03-24T22:47:39.223Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T22:47:39.223Z
Learning: Applies to web/src/**/*.{ts,tsx} : Never skip, dismiss, or ignore flaky tests -- always fix them fully. Mock timing functions for determinism
Applied to files:
web/src/__tests__/components/ui/progress-gauge.test.tsxweb/src/__tests__/components/ui/sparkline.test.tsxweb/src/__tests__/components/ui/status-badge.test.tsxweb/src/__tests__/components/ui/metric-card.test.tsx
📚 Learning: 2026-03-15T21:20:09.993Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:20:09.993Z
Learning: Applies to web/src/components/** : Vue components organized by feature (agents/, approvals/, budget/, common/, dashboard/, layout/, messages/, org-chart/, tasks/).
Applied to files:
web/src/components/ui/agent-card.tsxweb/src/components/ui/dept-health-bar.tsx
🪛 Stylelint (17.5.0)
web/src/styles/global.css
[error] 8-8: Unexpected unknown at-rule "@custom-variant" (scss/at-rule-no-unknown)
(scss/at-rule-no-unknown)
[error] 22-22: Unexpected unknown at-rule "@theme" (scss/at-rule-no-unknown)
(scss/at-rule-no-unknown)
🔇 Additional comments (44)
web/package.json (1)
19-19: LGTM!The
@fontsource-variable/geist-monodependency addition aligns with the PR objectives for self-hosted monospace font support, as documented in the brand-and-ux.md typography section.docs/design/brand-and-ux.md (1)
37-37: LGTM!The
bg-card-hovertoken is properly documented with the correct hex value (#181828), which matches the implementation indesign-tokens.css. This aligns with the sidebar hover styling updates mentioned in the PR.web/src/lib/utils.ts (1)
7-35: LGTM!The utility functions and types are well-designed:
AgentStatusandSemanticColortypes provide clear contractsgetStatusColoruses a const map for type-safe lookups, and the"text-secondary"fallback forofflineis handled by consumers (verified instatus-badge.tsxDOT_COLOR_CLASSES)getHealthColorthresholds are clear and match usage indept-health-bar.tsxandprogress-gauge.tsxweb/src/components/ui/stat-pill.tsx (1)
1-25: LGTM!Clean implementation following shadcn/ui patterns. The component correctly uses design tokens (
border-border,bg-card,text-muted-foreground,text-foreground) and thecn()utility for class composition.web/src/components/ui/section-card.tsx (1)
1-37: LGTM!Well-structured component with proper accessibility practices:
aria-hidden="true"correctly applied to decorative icon- Semantic
h3for title maintains heading hierarchy- Density-aware spacing tokens (
px-card,p-card) align with the design systemweb/src/components/ui/avatar.tsx (1)
1-44: LGTM!Solid implementation with good edge case handling:
getInitialscorrectly handles single/multi-word names and empty strings viafilter(Boolean)aria-label={name || undefined}avoids rendering an empty accessibility attribute- The
borderColorpattern with conditionalborder-2is cleanweb/src/components/ui/status-badge.tsx (1)
24-45: Looks solid for status rendering and accessibility metadata.
StatusBadgecleanly maps status to label/color and keeps the optional label/pulse behavior straightforward.web/src/components/ui/agent-card.tsx (1)
25-65: Good component composition and conditional rendering.The header/body split and optional task/timestamp branches are clear and maintainable.
web/src/components/ui/sparkline.tsx (1)
38-121: Sparkline rendering path is well-defended and clean.The early return for short datasets plus reduced-motion handling makes this implementation robust.
web/src/router/index.tsx (1)
33-33: Token class update in Suspense fallback looks correct.This keeps loading UI aligned with the new semantic palette.
web/src/pages/NotFoundPage.tsx (1)
7-8: NotFound token migration is consistent and safe.No logic or accessibility regressions from this class update.
web/src/pages/SetupPage.tsx (1)
3-4: Setup page token class migration looks good.The updated background/text token classes are aligned with the new design system.
web/.storybook/preview.tsx (1)
8-13: Storybook preview theming update is aligned and clean.Background and wrapper token classes now match the new semantic palette.
web/src/components/layout/PlaceholderPage.tsx (1)
5-5: LGTM!The migration from
text-surface-500to the semantictext-text-secondarytoken aligns with the design system update.web/src/__tests__/components/layout/SidebarNavItem.test.tsx (1)
56-65: LGTM on the token update.The test correctly reflects the semantic token change from
bg-success-500tobg-success.web/src/router/guards.tsx (2)
15-15: LGTM!Token migration to
text-text-secondaryis consistent with the design system update.
30-36: LGTM!The button styling update to
bg-primary,text-primary-foreground, andhover:bg-primary/80aligns with shadcn/ui conventions and the new token system.web/src/pages/LoginPage.tsx (1)
3-4: LGTM!The token migrations (
bg-background,text-text-secondary) are consistent with the design system update across the codebase.web/src/components/layout/AppLayout.tsx (2)
9-9: LGTM!Using
text-muted-foregroundaligns with shadcn/ui conventions for secondary text styling.
16-16: LGTM!The
bg-backgroundtoken is the standard semantic token for main container backgrounds.web/src/__tests__/components/ui/dept-health-bar.test.tsx (1)
41-46: LGTM on accessibility testing!Good coverage of the meter role and
aria-valuenowattribute verification.web/src/components/layout/SidebarNavItem.tsx (1)
32-33: LGTM!The token migrations for default, hover, and active states are consistent with the new design system. Using
hover:bg-card-hoveraligns with the newly documented token.web/src/components/layout/StatusBar.tsx (1)
1-69: LGTM!The token migration from the old
surface-*/brand-*palette to the new semantic tokens (bg-background,border-border,text-muted-foreground,bg-accent,bg-success,bg-warning) is correctly applied and consistent with the design system defined indesign-tokens.cssandglobal.css. TheDotcomponent properly usesaria-hidden="true"for accessibility.web/src/components/ui/agent-card.stories.tsx (1)
1-101: LGTM!The Storybook stories are well-structured with proper Meta/StoryObj typing, autodocs enabled, and comprehensive coverage of all status states (
active,idle,error,offline). TheLongTaskNamestory is a good edge case for text truncation testing, andAgentGridprovides useful side-by-side visual comparison.web/src/components/ui/avatar.stories.tsx (1)
1-64: LGTM!The Avatar stories provide comprehensive coverage of size variants (
sm,md,lg), name edge cases (single name, three-word name), and border color configurations. TheAllSizesandWithDepartmentBorderscomposition stories are useful for visual comparison during design review.web/src/__tests__/components/ui/status-badge.test.tsx (1)
1-64: LGTM!The tests thoroughly cover all four status states with their corresponding color classes, label visibility behavior, accessibility attributes (
aria-label), and the optional pulse animation. The exhaustive coverage of the finiteAgentStatusenum makes property-based testing less critical here.web/src/components/ui/dept-health-bar.stories.tsx (1)
1-50: LGTM!The stories provide excellent coverage of health state thresholds (0, 12, 30, 55, 92, 100) that likely correspond to the color breakpoints in
getHealthColor(). TheAllHealthLevelscomposition story is useful for visual regression testing and comparing the gradient of health states.web/src/components/layout/Sidebar.tsx (1)
29-30: LGTM! Consistent migration to semantic design tokens.The styling updates correctly migrate from the old
surface-*/brand-*palette to the new semantic token system (text-text-secondary,bg-card-hover,text-foreground,bg-success,text-muted-foreground,text-accent,border-border,bg-surface). All tokens are properly defined indesign-tokens.cssand wired throughglobal.css's@theme inlineblock.Also applies to: 65-75, 116-118, 159-159, 193-193, 207-207, 212-212, 226-239
web/src/components/ui/status-badge.stories.tsx (1)
1-61: LGTM! Comprehensive Storybook coverage for StatusBadge.The stories cover all status variants (
active,idle,error,offline) and feature combinations (label,pulse). TheAllStatusesstory provides a useful visual comparison of all states including pulsing variants.web/src/components/ui/sparkline.stories.tsx (1)
1-61: LGTM! Well-structured Storybook stories with good trend coverage.The stories effectively demonstrate various data patterns (rising, falling, volatile, flat) and edge cases (empty data, custom dimensions). Using
var(--so-*)tokens directly in the color props is appropriate for Storybook demonstrations.web/src/components/ui/section-card.stories.tsx (1)
1-58: LGTM! Good story coverage for SectionCard variants.The stories effectively demonstrate all component configurations: basic usage, icon integration, action slots, combined features, and nested content rendering. The
NestedContentstory appropriately uses a custom render function for complex children.web/src/components/ui/stat-pill.stories.tsx (1)
1-39: LGTM! Thorough story coverage including edge cases.The stories cover key value types (numeric, string, formatted large numbers) and the important
ZeroValueedge case where a falsy value should still render correctly. TheMultiplestory demonstrates typical dashboard layout usage.web/src/styles/design-tokens.css (1)
1-16: LGTM! Well-documented design token system with clear density axis.The token structure is well-organized with:
- Clear documentation explaining the single-source-of-truth pattern and theme switching mechanism
- Consistent
--so-*naming convention- Comprehensive density axis with documented levels matching
brand-and-ux.md- Proper forwarding through
global.css's@theme inlineblock (confirmed via context snippet)Also applies to: 18-72, 74-88, 90-94, 102-106
web/src/styles/global.css (4)
8-8: Static analysis false positive:@custom-variantis valid Tailwind CSS 4 syntax.The Stylelint error
scss/at-rule-no-unknownis a false positive.@custom-variantis a valid Tailwind CSS 4 directive for defining custom variants. Consider configuring Stylelint to recognize Tailwind-specific at-rules or switching to a Tailwind-aware linter configuration.
22-82: Static analysis false positive:@theme inlineis valid Tailwind CSS 4 syntax.The Stylelint
scss/at-rule-no-unknownerror is a false positive—@theme inlineis a Tailwind CSS 4 feature that generates utility classes resolving to CSS variables at runtime.The theme bridge is well-documented and correctly maps
--so-*design tokens to Tailwind utilities, enabling dynamic theme switching.
84-122: LGTM!Good consolidation of
:rootand.darkinto a single ruleset for a dark-only app. The comment clearly explains this design decision. All shadcn variables correctly reference--so-*tokens fromdesign-tokens.css.
124-147: LGTM!Scrollbar styling correctly uses the design token references (
--so-border-bright,--so-bg-surface,--so-text-muted). Both Firefox (scrollbar-color) and WebKit/Blink (::-webkit-scrollbar-*) are covered.web/src/components/ui/metric-card.stories.tsx (2)
1-11: LGTM!Storybook meta configuration follows the standard pattern with
satisfies Meta<typeof MetricCard>for type safety. Theautodocstag enables automatic documentation generation.
13-70: LGTM!Good coverage of story variants demonstrating different prop combinations: default state, sparkline data, positive/negative change indicators, progress bar, and string values.
web/src/components/ui/metric-card.tsx (3)
4-12: LGTM!Well-structured TypeScript interface with proper typing for all props. The
changeobject discriminates direction clearly, and optional props are correctly marked.
23-25: Good defensive handling of division-by-zero.The guard
progress.total > 0prevents NaN/Infinity when total is zero or negative, defaulting to0percent.
51-69: Good accessibility implementation for the progress bar.The progress bar includes proper ARIA attributes (
role="progressbar",aria-valuenow,aria-valuemin,aria-valuemax,aria-label), making it screen-reader accessible.web/src/components/ui/progress-gauge.stories.tsx (2)
1-16: LGTM!Good Storybook configuration with helpful
argTypescontrols. The range control forvalueand select forsizeenable interactive exploration in the Storybook UI.
18-44: LGTM!Good variety of stories covering threshold states (Low/Medium/High), size variants, custom max values, and labeled variants.
| describe('Avatar', () => { | ||
| it('renders initials from a two-word name', () => { | ||
| render(<Avatar name="Alice Smith" />) | ||
|
|
||
| expect(screen.getByText('AS')).toBeInTheDocument() | ||
| }) | ||
|
|
||
| it('renders single initial from a one-word name', () => { | ||
| render(<Avatar name="Alice" />) | ||
|
|
||
| expect(screen.getByText('A')).toBeInTheDocument() | ||
| }) | ||
|
|
||
| it('uses first and last initials for three-word names', () => { | ||
| render(<Avatar name="Alice Marie Smith" />) | ||
|
|
||
| expect(screen.getByText('AS')).toBeInTheDocument() | ||
| }) | ||
|
|
||
| it('has accessible aria-label with full name', () => { | ||
| render(<Avatar name="Alice Smith" />) | ||
|
|
||
| expect(screen.getByLabelText('Alice Smith')).toBeInTheDocument() | ||
| }) | ||
|
|
||
| it('applies small size classes', () => { | ||
| const { container } = render(<Avatar name="A" size="sm" />) | ||
|
|
||
| expect(container.firstChild).toHaveClass('size-6') | ||
| }) | ||
|
|
||
| it('applies medium size classes by default', () => { | ||
| const { container } = render(<Avatar name="A" />) | ||
|
|
||
| expect(container.firstChild).toHaveClass('size-8') | ||
| }) | ||
|
|
||
| it('applies large size classes', () => { | ||
| const { container } = render(<Avatar name="A" size="lg" />) | ||
|
|
||
| expect(container.firstChild).toHaveClass('size-10') | ||
| }) | ||
|
|
||
| it('handles empty name gracefully', () => { | ||
| render(<Avatar name="" />) | ||
|
|
||
| expect(screen.getByRole('img', { hidden: true })).toBeInTheDocument() | ||
| }) | ||
| }) |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider adding property-based tests with fast-check.
Per coding guidelines, property-based testing is recommended. A property test could verify that getInitials behaves correctly for arbitrary non-empty strings.
♻️ Suggested property-based test addition
import { render, screen } from '@testing-library/react'
+import * as fc from 'fast-check'
import { Avatar } from '@/components/ui/avatar'
describe('Avatar', () => {
// ... existing tests ...
+ it('renders without crashing for arbitrary names', () => {
+ fc.assert(
+ fc.property(fc.string(), (name) => {
+ const { unmount } = render(<Avatar name={name} />)
+ unmount()
+ }),
+ )
+ })
+
+ it('initials are at most 2 characters', () => {
+ fc.assert(
+ fc.property(fc.string({ minLength: 1 }), (name) => {
+ const { container, unmount } = render(<Avatar name={name} />)
+ const text = container.textContent ?? ''
+ expect(text.length).toBeLessThanOrEqual(2)
+ unmount()
+ }),
+ )
+ })
})As per coding guidelines: "Use fast-check for property-based testing in React (fc.assert + fc.property)."
📝 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.
| describe('Avatar', () => { | |
| it('renders initials from a two-word name', () => { | |
| render(<Avatar name="Alice Smith" />) | |
| expect(screen.getByText('AS')).toBeInTheDocument() | |
| }) | |
| it('renders single initial from a one-word name', () => { | |
| render(<Avatar name="Alice" />) | |
| expect(screen.getByText('A')).toBeInTheDocument() | |
| }) | |
| it('uses first and last initials for three-word names', () => { | |
| render(<Avatar name="Alice Marie Smith" />) | |
| expect(screen.getByText('AS')).toBeInTheDocument() | |
| }) | |
| it('has accessible aria-label with full name', () => { | |
| render(<Avatar name="Alice Smith" />) | |
| expect(screen.getByLabelText('Alice Smith')).toBeInTheDocument() | |
| }) | |
| it('applies small size classes', () => { | |
| const { container } = render(<Avatar name="A" size="sm" />) | |
| expect(container.firstChild).toHaveClass('size-6') | |
| }) | |
| it('applies medium size classes by default', () => { | |
| const { container } = render(<Avatar name="A" />) | |
| expect(container.firstChild).toHaveClass('size-8') | |
| }) | |
| it('applies large size classes', () => { | |
| const { container } = render(<Avatar name="A" size="lg" />) | |
| expect(container.firstChild).toHaveClass('size-10') | |
| }) | |
| it('handles empty name gracefully', () => { | |
| render(<Avatar name="" />) | |
| expect(screen.getByRole('img', { hidden: true })).toBeInTheDocument() | |
| }) | |
| }) | |
| import { render, screen } from '@testing-library/react' | |
| import * as fc from 'fast-check' | |
| import { Avatar } from '@/components/ui/avatar' | |
| describe('Avatar', () => { | |
| it('renders initials from a two-word name', () => { | |
| render(<Avatar name="Alice Smith" />) | |
| expect(screen.getByText('AS')).toBeInTheDocument() | |
| }) | |
| it('renders single initial from a one-word name', () => { | |
| render(<Avatar name="Alice" />) | |
| expect(screen.getByText('A')).toBeInTheDocument() | |
| }) | |
| it('uses first and last initials for three-word names', () => { | |
| render(<Avatar name="Alice Marie Smith" />) | |
| expect(screen.getByText('AS')).toBeInTheDocument() | |
| }) | |
| it('has accessible aria-label with full name', () => { | |
| render(<Avatar name="Alice Smith" />) | |
| expect(screen.getByLabelText('Alice Smith')).toBeInTheDocument() | |
| }) | |
| it('applies small size classes', () => { | |
| const { container } = render(<Avatar name="A" size="sm" />) | |
| expect(container.firstChild).toHaveClass('size-6') | |
| }) | |
| it('applies medium size classes by default', () => { | |
| const { container } = render(<Avatar name="A" />) | |
| expect(container.firstChild).toHaveClass('size-8') | |
| }) | |
| it('applies large size classes', () => { | |
| const { container } = render(<Avatar name="A" size="lg" />) | |
| expect(container.firstChild).toHaveClass('size-10') | |
| }) | |
| it('handles empty name gracefully', () => { | |
| render(<Avatar name="" />) | |
| expect(screen.getByRole('img', { hidden: true })).toBeInTheDocument() | |
| }) | |
| it('renders without crashing for arbitrary names', () => { | |
| fc.assert( | |
| fc.property(fc.string(), (name) => { | |
| const { unmount } = render(<Avatar name={name} />) | |
| unmount() | |
| }), | |
| ) | |
| }) | |
| it('initials are at most 2 characters', () => { | |
| fc.assert( | |
| fc.property(fc.string({ minLength: 1 }), (name) => { | |
| const { container, unmount } = render(<Avatar name={name} />) | |
| const text = container.textContent ?? '' | |
| expect(text.length).toBeLessThanOrEqual(2) | |
| unmount() | |
| }), | |
| ) | |
| }) | |
| }) |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/__tests__/components/ui/avatar.test.tsx` around lines 4 - 52, Add a
property-based test using fast-check that asserts getInitials (or Avatar when
using its exported helper) behaves correctly for arbitrary non-empty names:
import fc and write fc.assert(fc.property(fc.string({minLength:1}), name => {
const initials = getInitials(name); const words =
name.trim().split(/\s+/).filter(Boolean); const expected = words.length === 1 ?
words[0][0].toUpperCase() :
(words[0][0]+words[words.length-1][0]).toUpperCase(); return initials ===
expected; })); Place this new test in the same test file (avatar.test.tsx)
alongside existing tests, ensuring you import getInitials from the Avatar module
(or call Avatar rendering and extract text) and handle trimming/empty-word cases
as shown.
| describe('MetricCard', () => { | ||
| it('renders label text', () => { | ||
| render(<MetricCard label="Tasks Today" value={24} />) | ||
|
|
||
| expect(screen.getByText('Tasks Today')).toBeInTheDocument() | ||
| }) | ||
|
|
||
| it('renders numeric value', () => { | ||
| render(<MetricCard label="Tasks" value={24} />) | ||
|
|
||
| expect(screen.getByText('24')).toBeInTheDocument() | ||
| }) | ||
|
|
||
| it('renders string value', () => { | ||
| render(<MetricCard label="Spend" value="$12.50" />) | ||
|
|
||
| expect(screen.getByText('$12.50')).toBeInTheDocument() | ||
| }) | ||
|
|
||
| it('renders positive change badge with up indicator', () => { | ||
| render(<MetricCard label="Tasks" value={24} change={{ value: 12, direction: 'up' }} />) | ||
|
|
||
| expect(screen.getByText(/\+12%/)).toBeInTheDocument() | ||
| }) | ||
|
|
||
| it('renders negative change badge with down indicator', () => { | ||
| render(<MetricCard label="Tasks" value={24} change={{ value: 8, direction: 'down' }} />) | ||
|
|
||
| expect(screen.getByText(/-8%/)).toBeInTheDocument() | ||
| }) | ||
|
|
||
| it('renders sparkline when data is provided', () => { | ||
| const { container } = render( | ||
| <MetricCard label="Tasks" value={24} sparklineData={[1, 2, 3, 4]} />, | ||
| ) | ||
|
|
||
| expect(container.querySelector('svg')).toBeInTheDocument() | ||
| }) | ||
|
|
||
| it('does not render sparkline when no data', () => { | ||
| const { container } = render(<MetricCard label="Tasks" value={24} />) | ||
|
|
||
| expect(container.querySelector('svg')).not.toBeInTheDocument() | ||
| }) | ||
|
|
||
| it('renders progress bar when progress is provided', () => { | ||
| const { container } = render( | ||
| <MetricCard label="Tasks" value={24} progress={{ current: 24, total: 30 }} />, | ||
| ) | ||
|
|
||
| expect(container.querySelector('[role="progressbar"]')).toBeInTheDocument() | ||
| }) | ||
|
|
||
| it('renders sub-text when provided', () => { | ||
| render(<MetricCard label="Tasks" value={24} subText="of 30 completed" />) | ||
|
|
||
| expect(screen.getByText('of 30 completed')).toBeInTheDocument() | ||
| }) | ||
|
|
||
| it('applies custom className', () => { | ||
| const { container } = render( | ||
| <MetricCard label="Tasks" value={24} className="my-class" />, | ||
| ) | ||
|
|
||
| expect(container.firstChild).toHaveClass('my-class') | ||
| }) |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Add property-based tests for numeric value rendering and change badge formatting.
The unit tests provide good coverage of specific scenarios. However, per coding guidelines, property-based tests with fast-check should be included to verify behavior across a wider range of inputs.
Consider adding property tests for:
- Numeric
valuerendering with arbitrary numbers change.valueformatting for arbitrary percentagesprogresspercentage calculations with arbitrarycurrent/totalvalues
💡 Example property-based tests
import * as fc from 'fast-check'
it('renders any numeric value correctly', () => {
fc.assert(
fc.property(fc.integer({ min: -999999, max: 999999 }), (num) => {
const { unmount } = render(<MetricCard label="Test" value={num} />)
expect(screen.getByText(String(num))).toBeInTheDocument()
unmount()
}),
)
})
it('formats change badge for any percentage', () => {
fc.assert(
fc.property(
fc.integer({ min: 0, max: 100 }),
fc.constantFrom('up', 'down') as fc.Arbitrary<'up' | 'down'>,
(value, direction) => {
const { unmount } = render(
<MetricCard label="Test" value={0} change={{ value, direction }} />,
)
const prefix = direction === 'up' ? '+' : '-'
expect(screen.getByText(new RegExp(`${prefix}${value}%`))).toBeInTheDocument()
unmount()
},
),
)
})As per coding guidelines: "Use fast-check for property-based testing in React (fc.assert + fc.property). Always include unit and property tests with Vitest."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/__tests__/components/ui/metric-card.test.tsx` around lines 4 - 69,
Add fast-check property-based tests alongside existing unit tests to exercise
MetricCard across ranges: use fc.assert + fc.property to (1) render MetricCard
with arbitrary numeric value (fc.integer or fc.float) and assert
screen.getByText(String(value)) is present (reference the MetricCard component
and the numeric value test), (2) supply arbitrary change.value (fc.integer
0..100) with fc.constantFrom<'up'|'down'> and assert the badge text matches the
formatted prefix (`+` or `-`) plus `${value}%` (reference the change prop and
the change badge tests), and (3) test progress percentage computation by
generating pairs (current,total) with total > 0 and asserting the rendered
progress percent or progressbar attributes reflect
Math.round((current/total)*100) (reference the progress prop and progressbar
test); follow existing test patterns (render/unmount, use screen/container) and
keep property tests next to the corresponding unit tests.
| export const MetricGrid: Story = { | ||
| args: { label: 'Tasks Today', value: 24 }, | ||
| render: () => ( | ||
| <div className="grid grid-cols-2 gap-grid-gap max-w-lg"> | ||
| <MetricCard | ||
| label="Tasks Today" | ||
| value={24} | ||
| sparklineData={SAMPLE_DATA} | ||
| change={{ value: 12, direction: 'up' }} | ||
| /> | ||
| <MetricCard | ||
| label="Active Agents" | ||
| value={5} | ||
| sparklineData={[3, 4, 5, 5, 4, 5, 5]} | ||
| /> | ||
| <MetricCard | ||
| label="Daily Spend" | ||
| value="$12.50" | ||
| change={{ value: 8, direction: 'down' }} | ||
| /> | ||
| <MetricCard | ||
| label="Completion" | ||
| value="80%" | ||
| progress={{ current: 24, total: 30 }} | ||
| subText="of 30 completed" | ||
| /> | ||
| </div> | ||
| ), | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider removing unused args in MetricGrid story.
The args property on Line 73 is ignored since the custom render function doesn't use them. This could confuse readers expecting args to affect the rendered output.
♻️ Proposed fix
export const MetricGrid: Story = {
- args: { label: 'Tasks Today', value: 24 },
render: () => (📝 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 MetricGrid: Story = { | |
| args: { label: 'Tasks Today', value: 24 }, | |
| render: () => ( | |
| <div className="grid grid-cols-2 gap-grid-gap max-w-lg"> | |
| <MetricCard | |
| label="Tasks Today" | |
| value={24} | |
| sparklineData={SAMPLE_DATA} | |
| change={{ value: 12, direction: 'up' }} | |
| /> | |
| <MetricCard | |
| label="Active Agents" | |
| value={5} | |
| sparklineData={[3, 4, 5, 5, 4, 5, 5]} | |
| /> | |
| <MetricCard | |
| label="Daily Spend" | |
| value="$12.50" | |
| change={{ value: 8, direction: 'down' }} | |
| /> | |
| <MetricCard | |
| label="Completion" | |
| value="80%" | |
| progress={{ current: 24, total: 30 }} | |
| subText="of 30 completed" | |
| /> | |
| </div> | |
| ), | |
| } | |
| export const MetricGrid: Story = { | |
| render: () => ( | |
| <div className="grid grid-cols-2 gap-grid-gap max-w-lg"> | |
| <MetricCard | |
| label="Tasks Today" | |
| value={24} | |
| sparklineData={SAMPLE_DATA} | |
| change={{ value: 12, direction: 'up' }} | |
| /> | |
| <MetricCard | |
| label="Active Agents" | |
| value={5} | |
| sparklineData={[3, 4, 5, 5, 4, 5, 5]} | |
| /> | |
| <MetricCard | |
| label="Daily Spend" | |
| value="$12.50" | |
| change={{ value: 8, direction: 'down' }} | |
| /> | |
| <MetricCard | |
| label="Completion" | |
| value="80%" | |
| progress={{ current: 24, total: 30 }} | |
| subText="of 30 completed" | |
| /> | |
| </div> | |
| ), | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/components/ui/metric-card.stories.tsx` around lines 72 - 100, The
MetricGrid story defines an unused args object which is ignored by its custom
render function; remove the unused args property from the MetricGrid Story
definition (or alternatively update the render function to accept and pass
through args) so that the story is not misleading—locate the MetricGrid export
and either delete the args: { label: 'Tasks Today', value: 24 } entry or
refactor the render to accept args and apply them to the MetricCard components.
| export const AllThresholds: Story = { | ||
| args: { value: 50 }, | ||
| render: () => ( | ||
| <div className="flex items-end gap-8"> | ||
| <ProgressGauge value={10} label="Danger" /> | ||
| <ProgressGauge value={35} label="Warning" /> | ||
| <ProgressGauge value={60} label="Accent" /> | ||
| <ProgressGauge value={90} label="Success" /> | ||
| </div> | ||
| ), | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider removing unused args in AllThresholds story.
Same as in MetricGrid story—the args property is ignored by the custom render function.
♻️ Proposed fix
export const AllThresholds: Story = {
- args: { value: 50 },
render: () => (📝 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 AllThresholds: Story = { | |
| args: { value: 50 }, | |
| render: () => ( | |
| <div className="flex items-end gap-8"> | |
| <ProgressGauge value={10} label="Danger" /> | |
| <ProgressGauge value={35} label="Warning" /> | |
| <ProgressGauge value={60} label="Accent" /> | |
| <ProgressGauge value={90} label="Success" /> | |
| </div> | |
| ), | |
| } | |
| export const AllThresholds: Story = { | |
| render: () => ( | |
| <div className="flex items-end gap-8"> | |
| <ProgressGauge value={10} label="Danger" /> | |
| <ProgressGauge value={35} label="Warning" /> | |
| <ProgressGauge value={60} label="Accent" /> | |
| <ProgressGauge value={90} label="Success" /> | |
| </div> | |
| ), | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/components/ui/progress-gauge.stories.tsx` around lines 46 - 56, The
AllThresholds story defines an unused args property (args: { value: 50 }) that
is ignored because a custom render function returns fixed ProgressGauge
instances; remove the redundant args field from the AllThresholds export (or
alternatively update the render to use args.value if you intend to use args) so
the story no longer contains dead configuration—look for the AllThresholds
constant and its args entry and delete it (or wire args.value into the render)
to match the pattern used for MetricGrid.
…bbit
- Rename AgentStatus to AgentRuntimeStatus to disambiguate from API-layer
AgentStatus (different concept: runtime operational vs HR lifecycle)
- Fix ProgressGauge division by zero when max=0 (guard with Math.max(max, 1))
- Fix WCAG AA badge contrast: text-foreground on bg-danger -> text-white
- Tighten Record<string, string> color maps to Record<SemanticColor, string>
for compile-time exhaustiveness (status-badge, progress-gauge, dept-health-bar)
- Add typography scale tokens (--so-text-micro/compact/body-sm/metric) and
replace all arbitrary text-[Npx] classes with token-based utilities
- Replace hardcoded 14px in density-medium with var(--so-space-3-5) token
- Add aria-label to MetricCard ChangeBadge for screen readers
- Fix singular/plural in DeptHealthBar ("1 agent" vs "5 agents")
- Add missing test coverage: getStatusColor, getHealthColor, ProgressGauge
size variant, max=0, Sparkline animated=false, flat data, MetricCard
progress total=0, single sparklineData, Avatar whitespace/aria-label,
DeptHealthBar negative health
- Extract defaultProps in DeptHealthBar tests (DRY)
- Update CLAUDE.md: package structure, dependencies, styles description
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 (4)
web/src/__tests__/components/ui/sparkline.test.tsx (1)
4-75:⚠️ Potential issue | 🟠 MajorAdd required fast-check property tests for Sparkline invariants.
From Line 4 onward, coverage is example-based only. This still misses the required property-based tests (
fc.assert+fc.property) for input-space invariants (e.g., valid point generation and dimension consistency).#!/bin/bash # Verify whether this test file includes required fast-check property tests rg -n "fast-check|fc\\.assert|fc\\.property" web/src/__tests__/components/ui/sparkline.test.tsxAs per coding guidelines: "Use fast-check for property-based testing in React (fc.assert + fc.property). Always include unit and property tests with Vitest."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/__tests__/components/ui/sparkline.test.tsx` around lines 4 - 75, Add fast-check property tests that assert Sparkline invariants using fc.assert + fc.property: generate arrays of numbers (including empty/single) and random width/height, render <Sparkline data={...} width={w} height={h} /> and assert that for arrays length < 2 no SVG is rendered, for length >= 2 an SVG exists, the polyline point count equals data.length, and all rendered point coordinates lie within [0,w]×[0,h]; reference the Sparkline component and existing render + container queries (querySelector('svg'), querySelector('polyline'), querySelectorAll('circle')) to locate where to add these property tests.web/src/__tests__/components/ui/progress-gauge.test.tsx (1)
4-71:⚠️ Potential issue | 🟠 MajorAdd required fast-check properties for clamping and percentage computation.
From Line 4 onward, tests remain example-based. Property tests are still needed to validate percentage/clamping invariants across broad value/max inputs.
#!/bin/bash # Verify whether this test file includes required fast-check property tests rg -n "fast-check|fc\\.assert|fc\\.property" web/src/__tests__/components/ui/progress-gauge.test.tsxAs per coding guidelines: "Use fast-check for property-based testing in React (fc.assert + fc.property). Always include unit and property tests with Vitest."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/__tests__/components/ui/progress-gauge.test.tsx` around lines 4 - 71, The tests for ProgressGauge lack fast-check property tests to validate clamping and percentage invariants; add property-based tests using fast-check (fc.assert + fc.property) that generate pairs of value and max (including max=0, negative, and large values) and assert that the rendered percentage text and aria attributes from ProgressGauge always reflect: clamped value ∈ [0, max] (treat max<=0 as max=0 producing 100%), computed percentage = Math.round((clampedValue / effectiveMax) * 100) or the component's rounding behavior, and that aria-valuenow/aria-valuemin/aria-valuemax match expectations; use the same render/screen queries as existing tests to locate output and include a property for the small-size SVG dimensions as an additional invariant if desired.web/src/__tests__/components/ui/dept-health-bar.test.tsx (1)
4-75:⚠️ Potential issue | 🟠 MajorAdd required fast-check property tests for health clamping invariants.
From Line 4 onward, this is still example-driven only. Property checks across broad integer ranges (negative, in-range, >100) are still required.
#!/bin/bash # Verify whether this test file includes required fast-check property tests rg -n "fast-check|fc\\.assert|fc\\.property" web/src/__tests__/components/ui/dept-health-bar.test.tsxAs per coding guidelines: "Use fast-check for property-based testing in React (fc.assert + fc.property). Always include unit and property tests with Vitest."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/__tests__/components/ui/dept-health-bar.test.tsx` around lines 4 - 75, Add fast-check property tests to verify DeptHealthBar's health clamping invariants: import fc and use fc.assert(fc.property(...)) to generate integers across negative, 0..100, and >100 ranges and assert that the rendered percentage text and the meter's aria-valuenow equal Math.max(0, Math.min(100, health)). Target the DeptHealthBar component in the test file (tests already reference DeptHealthBar, render, and screen); create one property that checks arbitrary integer health values produce the clamped "X%" text and another that checks the meter role has aria-valuenow equal the same clamped value. Ensure tests run with Vitest by following existing test patterns (rendering component and using screen.getByText / getByRole).web/src/__tests__/components/ui/metric-card.test.tsx (1)
4-88:⚠️ Potential issue | 🟠 MajorAdd required fast-check properties for MetricCard value/change/progress invariants.
From Line 4 onward, tests are deterministic examples only; required property-based checks are still missing.
#!/bin/bash # Verify whether this test file includes required fast-check property tests rg -n "fast-check|fc\\.assert|fc\\.property" web/src/__tests__/components/ui/metric-card.test.tsxAs per coding guidelines: "Use fast-check for property-based testing in React (fc.assert + fc.property). Always include unit and property tests with Vitest."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/__tests__/components/ui/metric-card.test.tsx` around lines 4 - 88, The tests only include deterministic examples and are missing fast-check property-based tests for MetricCard invariants; add property tests in web/src/__tests__/components/ui/metric-card.test.tsx that import fast-check (fc) and use fc.assert + fc.property to validate: (1) MetricCard renders any numeric or string value prop correctly (reference MetricCard), (2) change prop invariants — direction is 'up'|'down' and formatted sign matches change.value (reference change prop), (3) progress prop handles total === 0 without throwing and sets aria-valuenow to 0 (reference progress prop and role="progressbar"), and (4) sparkline only renders when sparklineData has length > 1; implement each property by generating appropriate arbitrary inputs, rendering MetricCard inside the property body, and asserting the same DOM expectations as the unit tests, then call fc.assert for each property.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@web/src/__tests__/lib/utils.test.ts`:
- Around line 18-56: Add property-based tests for getHealthColor and
getStatusColor using fast-check: for getHealthColor use fc.assert with
fc.property(fc.integer()) and assert that values >=75 return 'success', values
>=50 and <75 return 'accent', values >=25 and <50 return 'warning', and values
<25 return 'danger' (test each range by filtering or using ranges to ensure no
gaps/overlaps); for getStatusColor use fc.assert with
fc.property(fc.constantFrom('active','idle','error','offline')) and assert each
literal maps to the expected color; use fc.pre or range constraints where
helpful and reference the getHealthColor and getStatusColor functions in
assertions.
In `@web/src/components/ui/metric-card.tsx`:
- Around line 23-25: The computed progressPct can be <0 or >100; update the
calculation for progressPct in metric-card.tsx (where it's derived from
progress.current/progress.total) to clamp the final value to the 0–100 range
(e.g., compute the percentage as before then wrap with Math.max(0, Math.min(100,
percent))) so aria-valuenow and bar widths never underflow or overflow; ensure
the fallback for no/zero total still results in 0.
In `@web/src/styles/global.css`:
- Line 8: Stylelint is flagging Tailwind v4 directives (`@custom-variant` and
`@theme`) as unknown at-rules; update your Stylelint configuration
(scss/at-rule-no-unknown rule) to add 'custom-variant' and 'theme' to
ignoreAtRules (also consider adding other Tailwind at-rules like 'layer',
'apply', 'source' if used) so the `@custom-variant` and `@theme` lines in global.css
are treated as valid.
---
Duplicate comments:
In `@web/src/__tests__/components/ui/dept-health-bar.test.tsx`:
- Around line 4-75: Add fast-check property tests to verify DeptHealthBar's
health clamping invariants: import fc and use fc.assert(fc.property(...)) to
generate integers across negative, 0..100, and >100 ranges and assert that the
rendered percentage text and the meter's aria-valuenow equal Math.max(0,
Math.min(100, health)). Target the DeptHealthBar component in the test file
(tests already reference DeptHealthBar, render, and screen); create one property
that checks arbitrary integer health values produce the clamped "X%" text and
another that checks the meter role has aria-valuenow equal the same clamped
value. Ensure tests run with Vitest by following existing test patterns
(rendering component and using screen.getByText / getByRole).
In `@web/src/__tests__/components/ui/metric-card.test.tsx`:
- Around line 4-88: The tests only include deterministic examples and are
missing fast-check property-based tests for MetricCard invariants; add property
tests in web/src/__tests__/components/ui/metric-card.test.tsx that import
fast-check (fc) and use fc.assert + fc.property to validate: (1) MetricCard
renders any numeric or string value prop correctly (reference MetricCard), (2)
change prop invariants — direction is 'up'|'down' and formatted sign matches
change.value (reference change prop), (3) progress prop handles total === 0
without throwing and sets aria-valuenow to 0 (reference progress prop and
role="progressbar"), and (4) sparkline only renders when sparklineData has
length > 1; implement each property by generating appropriate arbitrary inputs,
rendering MetricCard inside the property body, and asserting the same DOM
expectations as the unit tests, then call fc.assert for each property.
In `@web/src/__tests__/components/ui/progress-gauge.test.tsx`:
- Around line 4-71: The tests for ProgressGauge lack fast-check property tests
to validate clamping and percentage invariants; add property-based tests using
fast-check (fc.assert + fc.property) that generate pairs of value and max
(including max=0, negative, and large values) and assert that the rendered
percentage text and aria attributes from ProgressGauge always reflect: clamped
value ∈ [0, max] (treat max<=0 as max=0 producing 100%), computed percentage =
Math.round((clampedValue / effectiveMax) * 100) or the component's rounding
behavior, and that aria-valuenow/aria-valuemin/aria-valuemax match expectations;
use the same render/screen queries as existing tests to locate output and
include a property for the small-size SVG dimensions as an additional invariant
if desired.
In `@web/src/__tests__/components/ui/sparkline.test.tsx`:
- Around line 4-75: Add fast-check property tests that assert Sparkline
invariants using fc.assert + fc.property: generate arrays of numbers (including
empty/single) and random width/height, render <Sparkline data={...} width={w}
height={h} /> and assert that for arrays length < 2 no SVG is rendered, for
length >= 2 an SVG exists, the polyline point count equals data.length, and all
rendered point coordinates lie within [0,w]×[0,h]; reference the Sparkline
component and existing render + container queries (querySelector('svg'),
querySelector('polyline'), querySelectorAll('circle')) to locate where to add
these property tests.
🪄 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: fb413e56-b8ee-4e21-8972-96b22c6c308d
📒 Files selected for processing (18)
CLAUDE.mdweb/src/__tests__/components/ui/avatar.test.tsxweb/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/__tests__/components/ui/metric-card.test.tsxweb/src/__tests__/components/ui/progress-gauge.test.tsxweb/src/__tests__/components/ui/sparkline.test.tsxweb/src/__tests__/lib/utils.test.tsweb/src/components/layout/SidebarNavItem.tsxweb/src/components/ui/agent-card.tsxweb/src/components/ui/avatar.tsxweb/src/components/ui/dept-health-bar.tsxweb/src/components/ui/metric-card.tsxweb/src/components/ui/progress-gauge.tsxweb/src/components/ui/stat-pill.tsxweb/src/components/ui/status-badge.tsxweb/src/lib/utils.tsweb/src/styles/design-tokens.cssweb/src/styles/global.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 Sandbox
- GitHub Check: Build Web
- GitHub Check: Dependency Review
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (2)
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
web/src/**/*.{ts,tsx}: Use vendor-agnostic naming: NEVER use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, or tests. Use generic names: example-provider, example-large-001, example-medium-001, example-small-001
React 19 with shadcn/ui + Tailwind CSS 4. Use React hooks (auth, login lockout, WebSocket, polling, optimistic updates), Zustand stores, and lazy-loaded page components. Maintain component organization: ui/ (shadcn primitives), layout/ (app shell, sidebar, status bar), feature dirs for pages
Never skip, dismiss, or ignore flaky tests -- always fix them fully. Mock timing functions for determinism
Files:
web/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/components/layout/SidebarNavItem.tsxweb/src/__tests__/lib/utils.test.tsweb/src/__tests__/components/ui/avatar.test.tsxweb/src/__tests__/components/ui/metric-card.test.tsxweb/src/__tests__/components/ui/sparkline.test.tsxweb/src/__tests__/components/ui/progress-gauge.test.tsxweb/src/components/ui/progress-gauge.tsxweb/src/lib/utils.tsweb/src/components/ui/avatar.tsxweb/src/components/ui/dept-health-bar.tsxweb/src/components/ui/status-badge.tsxweb/src/components/ui/stat-pill.tsxweb/src/components/ui/metric-card.tsxweb/src/components/ui/agent-card.tsx
web/src/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use fast-check for property-based testing in React (
@fc.assert+@fc.property). Always include unit and property tests with Vitest. Coverage should scope to files changed vs origin/main
Files:
web/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/__tests__/lib/utils.test.tsweb/src/__tests__/components/ui/avatar.test.tsxweb/src/__tests__/components/ui/metric-card.test.tsxweb/src/__tests__/components/ui/sparkline.test.tsxweb/src/__tests__/components/ui/progress-gauge.test.tsx
🧠 Learnings (20)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T22:47:39.223Z
Learning: Applies to web/src/**/*.{ts,tsx} : React 19 with shadcn/ui + Tailwind CSS 4. Use React hooks (auth, login lockout, WebSocket, polling, optimistic updates), Zustand stores, and lazy-loaded page components. Maintain component organization: ui/ (shadcn primitives), layout/ (app shell, sidebar, status bar), feature dirs for pages
📚 Learning: 2026-03-24T22:47:39.223Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T22:47:39.223Z
Learning: Applies to web/src/**/*.test.{ts,tsx} : Use fast-check for property-based testing in React (fc.assert + fc.property). Always include unit and property tests with Vitest. Coverage should scope to files changed vs origin/main
Applied to files:
web/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/__tests__/lib/utils.test.tsweb/src/__tests__/components/ui/avatar.test.tsxweb/src/__tests__/components/ui/metric-card.test.tsxweb/src/__tests__/components/ui/sparkline.test.tsxweb/src/__tests__/components/ui/progress-gauge.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/dept-health-bar.test.tsxweb/src/__tests__/lib/utils.test.tsweb/src/__tests__/components/ui/avatar.test.tsxweb/src/__tests__/components/ui/metric-card.test.tsxweb/src/__tests__/components/ui/sparkline.test.tsxweb/src/__tests__/components/ui/progress-gauge.test.tsxCLAUDE.md
📚 Learning: 2026-03-24T22:47:39.223Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T22:47:39.223Z
Learning: Applies to web/src/**/*.{ts,tsx} : React 19 with shadcn/ui + Tailwind CSS 4. Use React hooks (auth, login lockout, WebSocket, polling, optimistic updates), Zustand stores, and lazy-loaded page components. Maintain component organization: ui/ (shadcn primitives), layout/ (app shell, sidebar, status bar), feature dirs for pages
Applied to files:
web/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/components/layout/SidebarNavItem.tsxweb/src/__tests__/components/ui/avatar.test.tsxweb/src/__tests__/components/ui/sparkline.test.tsxCLAUDE.mdweb/src/components/ui/progress-gauge.tsxweb/src/lib/utils.tsweb/src/components/ui/avatar.tsxweb/src/components/ui/dept-health-bar.tsxweb/src/components/ui/status-badge.tsxweb/src/components/ui/stat-pill.tsxweb/src/components/ui/agent-card.tsx
📚 Learning: 2026-03-24T22:47:39.223Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T22:47:39.223Z
Learning: Applies to web/src/**/*.{ts,tsx} : Never skip, dismiss, or ignore flaky tests -- always fix them fully. Mock timing functions for determinism
Applied to files:
web/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/__tests__/components/ui/metric-card.test.tsxweb/src/__tests__/components/ui/sparkline.test.tsxweb/src/__tests__/components/ui/progress-gauge.test.tsx
📚 Learning: 2026-03-14T15:43:05.601Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T15:43:05.601Z
Learning: Applies to 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-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-24T22:47:39.224Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T22:47:39.224Z
Learning: CI uses dorny/paths-filter for path filtering -- jobs run only when domain affected. CLI has separate workflow (cli.yml). Jobs (lint/type-check/test/python-audit/dockerfile-lint/dashboard) run in parallel -> ci-pass gate. Pages: extract version from pyproject.toml, OpenAPI export, Astro + Zensical docs build (no --strict), GitHub Pages deploy on main. PR Preview: Cloudflare Pages per PR (pr-<number>.synthorg-pr-preview.pages.dev), cleanup on close. Docker: build + Trivy/Grype scan + push to GHCR + cosign sign + SLSA L3 provenance. CVE triage: .github/.trivyignore.yaml, .github/.grype.yaml. CLI: cross-compile + govulncheck + fuzz via GoReleaser on v* tags. Dependabot: daily updates (uv/github-actions/npm/pre-commit/docker/gomod), grouped minor/patch, no auto-merge. Security: gitleaks (push/PR+weekly), zizmor (workflow), OSSF Scorecard (weekly), Socket.dev (PR supply-chain), ZAP DAST (weekly+manual, rules: .github/zap-rules.tsv). Coverage: Codecov (best-effort). Dependency review: licens...
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.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
📚 Learning: 2026-03-24T22:47:39.224Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T22:47:39.224Z
Learning: All dependencies in pyproject.toml use pinned versions (==). Groups: test (pytest+plugins, hypothesis), dev (test+ruff, mypy, pre-commit, commitizen, pip-audit). Install: uv sync (installs everything, dev group default). Required deps: mem0ai (Mem0 memory backend), cryptography (Fernet encryption), faker (agent name generation). Web dashboard: Node.js 22+, React 19, react-router, shadcn/ui, Radix UI, Tailwind 4, Zustand, tanstack/react-query, xyflow/react, Recharts, Framer Motion, cmdk, Axios, Lucide React, Storybook, Vitest, vitest/coverage-v8, testing-library/react, fast-check, ESLint, eslint-react/eslint-plugin, eslint-plugin-security. CLI: Go 1.26+, Cobra, charmbracelet/huh, charmbracelet/lipgloss, sigstore-go, go-containerregistry, go-tuf
Applied to files:
CLAUDE.md
📚 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 docker/{Dockerfile*,compose.yml} : Docker: Backend uses 3-stage build (builder → setup → distroless runtime), Chainguard Python, non-root (UID 65532), CIS-hardened. Web uses nginxinc/nginx-unprivileged, Vue 3 SPA with PrimeVue + Tailwind CSS, SPA routing, API/WebSocket proxy to backend.
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 docs/** : Docs source in docs/ (Markdown, built with Zensical); design spec in docs/design/ (7 pages: index, agents, organization, communication, engine, memory, operations)
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-24T22:47:39.223Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T22:47:39.223Z
Learning: Applies to docs/**/*.md : Documentation is built with Zensical (config: mkdocs.yml). Docs structure: docs/design/ (10 pages), docs/architecture/, docs/roadmap/, docs/security.md, docs/licensing.md, docs/reference/, docs/rest-api.md + docs/_generated/api-reference.html (generated by scripts/export_openapi.py), docs/api/ (auto-generated via mkdocstrings + Griffe AST-based). Landing page: site/ (Astro with /get/ CLI install page, contact form, SEO)
Applied to files:
CLAUDE.md
📚 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 pyproject.toml : Dependencies: all versions use == in pyproject.toml. Groups: test (pytest + plugins, hypothesis), dev (includes test + ruff, mypy, pre-commit, commitizen, pip-audit). Required: mem0ai (Mem0 memory backend — the default and currently only backend). Install: uv sync installs everything (dev group is default).
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-19T11:19:40.044Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:19:40.044Z
Learning: Applies to go.mod : Maintain Go 1.26+ requirement. Dependencies: Cobra (CLI framework), charmbracelet/huh and charmbracelet/lipgloss (UI), sigstore-go (code signing), go-containerregistry (container image verification), go-tuf (TUF client for Sigstore).
Applied to files:
CLAUDE.md
📚 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 cli/** : CLI: Go 1.26+, dependencies in cli/go.mod (Cobra, charmbracelet/huh).
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-24T22:47:39.223Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T22:47:39.223Z
Learning: Applies to cli/**/*.go : Go lint/test/build requires golangci-lint (via `go -C cli tool golangci-lint run`), govulncheck, and native fuzz testing (testing.F with -fuzz flag; seed corpus runs without -fuzz)
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-15T21:32:02.880Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to cli/**/*.go : Go CLI (Go 1.26+) uses Cobra for commands, charmbracelet/huh for interactive CLI, charmbracelet/lipgloss for styled output. Cross-platform builds (linux/darwin/windows × amd64/arm64). GoReleaser for releases with cosign keyless signing of checksums.txt. SLSA L3 provenance attestations via actions/attest-build-provenance.
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-15T21:32:02.880Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to cli/go.mod : Go CLI dependencies: Go 1.26+, Cobra (commands), charmbracelet/huh (interactive CLI), charmbracelet/lipgloss (styled output).
Applied to files:
CLAUDE.md
🪛 Stylelint (17.5.0)
web/src/styles/global.css
[error] 8-8: Unexpected unknown at-rule "@custom-variant" (scss/at-rule-no-unknown)
(scss/at-rule-no-unknown)
[error] 22-22: Unexpected unknown at-rule "@theme" (scss/at-rule-no-unknown)
(scss/at-rule-no-unknown)
🔇 Additional comments (21)
web/src/components/layout/SidebarNavItem.tsx (1)
32-33: Token migration for nav + badge styles looks consistent.The updated semantic classes align with the new token system without changing behavior.
Also applies to: 46-46
web/src/components/ui/stat-pill.tsx (1)
9-24: StatPill implementation is clean and token-aligned.Props and class merging are straightforward, and the structure is maintainable.
web/src/__tests__/components/ui/avatar.test.tsx (1)
4-62: Consider adding property-based tests with fast-check.The coding guidelines require property-based testing with fast-check. While the existing tests cover key scenarios well, adding property tests would strengthen coverage for edge cases in the
getInitialslogic.As per coding guidelines: "Use fast-check for property-based testing in React (fc.assert + fc.property)."
CLAUDE.md (2)
126-132: LGTM!Documentation updates accurately reflect the new design token system, semantic color mappers, and component organization introduced in this PR.
257-257: LGTM!Font dependencies correctly added to the web dashboard dependency list.
web/src/lib/utils.ts (2)
8-30: LGTM!Good design decisions:
- Clear separation between
AgentRuntimeStatus(UI) and API-layerAgentStatuswith explicit documentation about the mapping boundaryRecord<AgentRuntimeStatus, ...>enforces exhaustive coverage at compile time- The
"text-secondary"fallback for"offline"is properly typed in the union
32-42: LGTM!The
getHealthColorfunction has clear threshold documentation and straightforward implementation. The threshold order (>=75, >=50, >=25, else) is correct.web/src/components/ui/progress-gauge.tsx (2)
30-33: LGTM!The division-by-zero guard (
safeMax = Math.max(max, 1)) is correctly implemented, addressing the previous review concern. The percentage calculation is now safe for all inputs includingmax=0.
48-99: LGTM!Excellent accessibility implementation:
- Proper
role="meter"with complete ARIA attributesaria-labelincludes context whenlabelis provided- SVG arc geometry and transition styling are well-implemented
web/src/components/ui/agent-card.tsx (1)
24-66: LGTM!Well-structured component with:
- Clean composition of
AvatarandStatusBadge- Proper use of design tokens (
--so-shadow-card-hover)- Good handling of optional props with conditional rendering
- Appropriate truncation for long text content
web/src/components/ui/dept-health-bar.tsx (2)
1-8: LGTM!Type safety is properly enforced with
Record<SemanticColor, string>, ensuring exhaustive coverage of all semantic color keys.
57-61: LGTM!Singular/plural handling is correctly implemented for both agent and task counts, addressing the previous review feedback.
web/src/components/ui/status-badge.tsx (2)
10-16: LGTM!The
DOT_COLOR_CLASSESmap correctly types the unionSemanticColor | 'text-secondary'to handle theofflinestatus fallback, ensuring type-safe lookups fromgetStatusColor().
25-46: LGTM!Clean implementation with:
- Always-present
aria-labelfor accessibilitydata-slotattribute for external styling/testing hooks- Conditional
animate-pulsevia thepulsepropweb/src/components/ui/avatar.tsx (2)
9-16: LGTM!The
getInitialsfunction correctly handles all edge cases:
- Empty/whitespace names return
''- Single-word names return first initial
- Multi-word names return first + last initials
- The non-null assertions are safe due to the
filter(Boolean)check ensuring non-empty parts
25-43: LGTM!Well-implemented Avatar component:
role="img"is semantically appropriate for initials displayaria-label={name || undefined}correctly omits the attribute when name is empty- Flexible
borderColorprop accepts any Tailwind class string- Clean class composition with
cn()web/src/styles/design-tokens.css (2)
47-57: LGTM — Typography scale and intermediate spacing token added.The new typography scale tokens (
--so-text-micro,--so-text-compact,--so-text-body-sm,--so-text-metric) and the--so-space-3-5intermediate spacing token are well-integrated into the existing scale. The addition of--so-space-3-5also addresses the previous review feedback about the hardcoded14pxvalue in.density-medium.
91-113: LGTM — Density axis implementation is clean and consistent.All density classes now use token references (
var(--so-space-*)) consistently, addressing the earlier feedback about the hardcoded14px. The progression from dense → medium → balanced → sparse is logical and well-documented.web/src/styles/global.css (3)
22-48: LGTM — Theme bridge cleanly separates SynthOrg tokens from shadcn compatibility layer.The
@theme inlineapproach ensures Tailwind utilities resolve tovar()references at runtime, enabling theme/density switching without recompilation. The separation of concerns is clear:
- Lines 23-32: SynthOrg semantic tokens
- Lines 34-43: Typography (fonts and scale)
- Lines 45-48: Density-derived spacing
90-128: LGTM — shadcn bridge correctly implements dark-only theming.Using
:root, .darkensures both base utilities anddark:prefixed utilities resolve correctly for this dark-only app. The mapping is comprehensive, covering all shadcn primitives including the chart and sidebar color sets.
130-153: LGTM — Scrollbar styling uses token references consistently.Both Firefox (
scrollbar-color) and WebKit (::-webkit-scrollbar-*) scrollbar styles correctly reference--so-*tokens, ensuring they update with theme changes.
| @import "@fontsource-variable/geist"; | ||
| @import "@fontsource-variable/geist-mono"; | ||
|
|
||
| @custom-variant dark (&:is(.dark *)); |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Static analysis hints are false positives — Tailwind v4 syntax is valid.
The Stylelint errors for @custom-variant (line 8) and @theme (line 22) are false positives. These are valid Tailwind CSS v4 directives that Stylelint's scss/at-rule-no-unknown rule doesn't recognize. Consider adding these to Stylelint's ignoreAtRules configuration if the warnings are disruptive:
🔧 Stylelint config update
// .stylelintrc.js or stylelint.config.js
rules: {
'scss/at-rule-no-unknown': [
true,
{ ignoreAtRules: ['theme', 'custom-variant', 'layer', 'apply', 'source'] }
]
}Also applies to: 22-22
🧰 Tools
🪛 Stylelint (17.5.0)
[error] 8-8: Unexpected unknown at-rule "@custom-variant" (scss/at-rule-no-unknown)
(scss/at-rule-no-unknown)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/styles/global.css` at line 8, Stylelint is flagging Tailwind v4
directives (`@custom-variant` and `@theme`) as unknown at-rules; update your
Stylelint configuration (scss/at-rule-no-unknown rule) to add 'custom-variant'
and 'theme' to ignoreAtRules (also consider adding other Tailwind at-rules like
'layer', 'apply', 'source' if used) so the `@custom-variant` and `@theme` lines in
global.css are treated as valid.
Prevents aria-valuenow and bar width from overflowing when progress.current exceeds progress.total. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@web/src/components/ui/metric-card.tsx`:
- Around line 72-79: The layout can misalign when only change is provided
because the flex container uses justify-between but has a single child; update
the MetricCard rendering so the ChangeBadge is forced to the right when subText
is falsy—pass a className like "ml-auto" to ChangeBadge (i.e., render
<ChangeBadge className="ml-auto" {...change} /> when no subText) and update the
ChangeBadge component to accept an optional className prop and apply it to its
root element so the badge can be aligned via parent-provided utility classes.
🪄 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: 3b542215-b779-4d45-a225-163da7fbc942
📒 Files selected for processing (1)
web/src/components/ui/metric-card.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 (1)
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
web/src/**/*.{ts,tsx}: Use vendor-agnostic naming: NEVER use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, or tests. Use generic names: example-provider, example-large-001, example-medium-001, example-small-001
React 19 with shadcn/ui + Tailwind CSS 4. Use React hooks (auth, login lockout, WebSocket, polling, optimistic updates), Zustand stores, and lazy-loaded page components. Maintain component organization: ui/ (shadcn primitives), layout/ (app shell, sidebar, status bar), feature dirs for pages
Never skip, dismiss, or ignore flaky tests -- always fix them fully. Mock timing functions for determinism
Files:
web/src/components/ui/metric-card.tsx
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T22:47:39.223Z
Learning: Applies to web/src/**/*.{ts,tsx} : React 19 with shadcn/ui + Tailwind CSS 4. Use React hooks (auth, login lockout, WebSocket, polling, optimistic updates), Zustand stores, and lazy-loaded page components. Maintain component organization: ui/ (shadcn primitives), layout/ (app shell, sidebar, status bar), feature dirs for pages
🔇 Additional comments (2)
web/src/components/ui/metric-card.tsx (2)
14-82: LGTM!The
MetricCardcomponent is well-structured with:
- Proper progress percentage clamping to 0–100 (addressing the prior review finding)
- Correct ARIA attributes on the progress bar (
role,aria-valuenow,aria-valuemin,aria-valuemax,aria-label)- Defensive guard on sparkline data length (aligns with
Sparkline's internal validation)- Clean conditional rendering for optional sections
84-102: LGTM!The
ChangeBadgehelper component correctly implements:
- Accessible
aria-labeldescribing the direction and value for screen readers- Semantic color tokens (
success/danger) for visual distinction- Consistent formatting with
+/-prefix based on direction
Add property-based tests (fast-check) to all component test files: - utils: getStatusColor exhaustive mapping, getHealthColor threshold boundaries - Avatar: arbitrary names render without crashing, initials <= 2 chars - AgentCard: initials generation for arbitrary two-word names - DeptHealthBar: health clamping across full integer range - MetricCard: arbitrary numeric values, change badge formatting - ProgressGauge: percentage always clamped 0-100 for arbitrary value/max - SectionCard: arbitrary titles render without crashing - Sparkline: SVG renders for any data length > 1, custom dimensions - StatPill: arbitrary label/value pairs render without crashing - SidebarNavItem: badge capping at 99+ and exact display for 1-99 Fix ChangeBadge alignment: add ml-auto when subText is absent so the badge right-aligns consistently in the flex container. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@web/src/__tests__/components/layout/SidebarNavItem.test.tsx`:
- Around line 57-65: The test for SidebarNavItem currently uses
document.querySelector which can leak across tests; update the test that calls
renderWithRouter(<SidebarNavItem ... dotColor="bg-success" />) to use the render
result scope instead (e.g., capture the returned container or use
within(container)) and query that scoped container for the dot element (or
alternatively use a data-testid on the dot and assert with screen.getByTestId).
Replace the document.querySelector('.bg-success.rounded-full') assertion with a
scoped query so the dot lookup is isolated to the SidebarNavItem render.
In `@web/src/__tests__/components/ui/metric-card.test.tsx`:
- Around line 100-111: The test "formats change badge for any percentage
(property)" builds a dynamic RegExp to match the change badge text; replace that
with an exact string match using the literal text instead. In the property-based
test where MetricCard is rendered with change={{ value, direction }}, compute
the prefix (direction === 'up' ? '+' : '-') and assert getByText with the exact
string `${prefix}${value}%` (not new RegExp) so the matcher is a plain string
lookup for MetricCard's rendered badge.
🪄 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: 3083b3b6-9e76-4181-a5b6-8933aff3f714
📒 Files selected for processing (11)
web/src/__tests__/components/layout/SidebarNavItem.test.tsxweb/src/__tests__/components/ui/agent-card.test.tsxweb/src/__tests__/components/ui/avatar.test.tsxweb/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/__tests__/components/ui/metric-card.test.tsxweb/src/__tests__/components/ui/progress-gauge.test.tsxweb/src/__tests__/components/ui/section-card.test.tsxweb/src/__tests__/components/ui/sparkline.test.tsxweb/src/__tests__/components/ui/stat-pill.test.tsxweb/src/__tests__/lib/utils.test.tsweb/src/components/ui/metric-card.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 Web
- GitHub Check: Build Backend
- GitHub Check: Build Sandbox
- GitHub Check: Dependency Review
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (2)
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
web/src/**/*.{ts,tsx}: Use vendor-agnostic naming: NEVER use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, or tests. Use generic names: example-provider, example-large-001, example-medium-001, example-small-001
React 19 with shadcn/ui + Tailwind CSS 4. Use React hooks (auth, login lockout, WebSocket, polling, optimistic updates), Zustand stores, and lazy-loaded page components. Maintain component organization: ui/ (shadcn primitives), layout/ (app shell, sidebar, status bar), feature dirs for pages
Never skip, dismiss, or ignore flaky tests -- always fix them fully. Mock timing functions for determinism
Files:
web/src/__tests__/components/ui/stat-pill.test.tsxweb/src/__tests__/components/layout/SidebarNavItem.test.tsxweb/src/__tests__/components/ui/sparkline.test.tsxweb/src/__tests__/lib/utils.test.tsweb/src/__tests__/components/ui/progress-gauge.test.tsxweb/src/__tests__/components/ui/avatar.test.tsxweb/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/__tests__/components/ui/metric-card.test.tsxweb/src/__tests__/components/ui/section-card.test.tsxweb/src/components/ui/metric-card.tsxweb/src/__tests__/components/ui/agent-card.test.tsx
web/src/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use fast-check for property-based testing in React (
@fc.assert+@fc.property). Always include unit and property tests with Vitest. Coverage should scope to files changed vs origin/main
Files:
web/src/__tests__/components/ui/stat-pill.test.tsxweb/src/__tests__/components/layout/SidebarNavItem.test.tsxweb/src/__tests__/components/ui/sparkline.test.tsxweb/src/__tests__/lib/utils.test.tsweb/src/__tests__/components/ui/progress-gauge.test.tsxweb/src/__tests__/components/ui/avatar.test.tsxweb/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/__tests__/components/ui/metric-card.test.tsxweb/src/__tests__/components/ui/section-card.test.tsxweb/src/__tests__/components/ui/agent-card.test.tsx
🧠 Learnings (5)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T22:47:39.223Z
Learning: Applies to web/src/**/*.{ts,tsx} : React 19 with shadcn/ui + Tailwind CSS 4. Use React hooks (auth, login lockout, WebSocket, polling, optimistic updates), Zustand stores, and lazy-loaded page components. Maintain component organization: ui/ (shadcn primitives), layout/ (app shell, sidebar, status bar), feature dirs for pages
📚 Learning: 2026-03-24T22:47:39.223Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T22:47:39.223Z
Learning: Applies to web/src/**/*.test.{ts,tsx} : Use fast-check for property-based testing in React (fc.assert + fc.property). Always include unit and property tests with Vitest. Coverage should scope to files changed vs origin/main
Applied to files:
web/src/__tests__/components/ui/stat-pill.test.tsxweb/src/__tests__/components/layout/SidebarNavItem.test.tsxweb/src/__tests__/components/ui/sparkline.test.tsxweb/src/__tests__/lib/utils.test.tsweb/src/__tests__/components/ui/progress-gauge.test.tsxweb/src/__tests__/components/ui/avatar.test.tsxweb/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/__tests__/components/ui/metric-card.test.tsxweb/src/__tests__/components/ui/section-card.test.tsxweb/src/__tests__/components/ui/agent-card.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/stat-pill.test.tsxweb/src/__tests__/components/layout/SidebarNavItem.test.tsxweb/src/__tests__/components/ui/sparkline.test.tsxweb/src/__tests__/lib/utils.test.tsweb/src/__tests__/components/ui/progress-gauge.test.tsxweb/src/__tests__/components/ui/avatar.test.tsxweb/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/__tests__/components/ui/metric-card.test.tsxweb/src/__tests__/components/ui/section-card.test.tsxweb/src/__tests__/components/ui/agent-card.test.tsx
📚 Learning: 2026-03-24T22:47:39.223Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T22:47:39.223Z
Learning: Applies to web/src/**/*.{ts,tsx} : React 19 with shadcn/ui + Tailwind CSS 4. Use React hooks (auth, login lockout, WebSocket, polling, optimistic updates), Zustand stores, and lazy-loaded page components. Maintain component organization: ui/ (shadcn primitives), layout/ (app shell, sidebar, status bar), feature dirs for pages
Applied to files:
web/src/__tests__/components/ui/stat-pill.test.tsxweb/src/__tests__/components/layout/SidebarNavItem.test.tsxweb/src/__tests__/components/ui/progress-gauge.test.tsxweb/src/__tests__/components/ui/avatar.test.tsxweb/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/__tests__/components/ui/section-card.test.tsxweb/src/components/ui/metric-card.tsxweb/src/__tests__/components/ui/agent-card.test.tsx
📚 Learning: 2026-03-24T22:47:39.223Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T22:47:39.223Z
Learning: Applies to web/src/**/*.{ts,tsx} : Never skip, dismiss, or ignore flaky tests -- always fix them fully. Mock timing functions for determinism
Applied to files:
web/src/__tests__/components/ui/stat-pill.test.tsxweb/src/__tests__/components/layout/SidebarNavItem.test.tsxweb/src/__tests__/components/ui/sparkline.test.tsxweb/src/__tests__/lib/utils.test.tsweb/src/__tests__/components/ui/progress-gauge.test.tsxweb/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/__tests__/components/ui/metric-card.test.tsxweb/src/__tests__/components/ui/agent-card.test.tsx
🪛 GitHub Check: Dashboard Lint
web/src/__tests__/components/ui/metric-card.test.tsx
[warning] 110-110:
Found non-literal argument to RegExp Constructor
🔇 Additional comments (11)
web/src/__tests__/components/ui/stat-pill.test.tsx (1)
1-45: Well-structured test suite with proper property-based testing.The tests comprehensively cover StatPill functionality: label/value rendering, styling assertions, and className propagation. The property-based test using
fc.oneof(fc.string(), fc.integer())correctly exercises arbitrary inputs and properly callsunmount()to prevent resource leaks. This addresses the previous review feedback and aligns with the coding guidelines for fast-check usage.web/src/__tests__/components/ui/section-card.test.tsx (1)
1-61: Solid test coverage with proper property-based testing.The tests comprehensively cover SectionCard's props and behaviors: title rendering, children content, conditional icon rendering, action slot, and className forwarding. The property-based test on lines 53-59 satisfies the coding guidelines requirement for fast-check usage.
The smoke-test approach (
render+unmountwithout DOM assertions) is a pragmatic choice—it handles the empty string edge case that would breakgetByText('')while still validating component stability across arbitrary inputs.web/src/__tests__/components/layout/SidebarNavItem.test.tsx (1)
77-99: Great property coverage for badge invariants.These tests correctly validate both capped and exact badge rendering ranges with
fast-check, and the per-rununmount()prevents DOM buildup.Based on learnings: Applies to
web/src/**/*.test.{ts,tsx}: Use fast-check for property-based testing in React (fc.assert+fc.property). Always include unit and property tests with Vitest.web/src/__tests__/components/ui/sparkline.test.tsx (1)
77-104: Good property-based coverage for Sparkline invariants.The fast-check tests are well-scoped and complement the unit cases effectively.
web/src/__tests__/components/ui/agent-card.test.tsx (1)
73-95: Initials property test looks solid.The generated-name invariant is covered well and complements the example-based assertions.
web/src/__tests__/components/ui/avatar.test.tsx (1)
48-82: Nice edge-case and property test coverage.Empty/whitespace handling plus fast-check robustness tests are well implemented.
web/src/__tests__/components/ui/progress-gauge.test.tsx (1)
43-89: ProgressGauge tests are comprehensive and aligned with clamping behavior.The accessibility checks and bounded-percentage property test are both strong.
web/src/__tests__/lib/utils.test.ts (1)
36-89: Great addition of property tests for status/health color invariants.This substantially improves confidence in the threshold and mapping logic.
web/src/__tests__/components/ui/dept-health-bar.test.tsx (1)
77-105: Clamping property tests are well done.The three-range property split makes the health behavior explicit and reliable.
web/src/components/ui/metric-card.tsx (1)
23-25: MetricCard logic and accessibility updates look correct.Progress clamping, right-aligned change badge behavior, and badge aria-label handling are all in good shape.
Also applies to: 77-101
web/src/__tests__/components/ui/metric-card.test.tsx (1)
6-99: Overall test coverage for MetricCard is strong.The suite captures key rendering branches and edge cases (including
total=0) effectively.Also applies to: 112-116
- SidebarNavItem: use container.querySelector instead of document.querySelector for test isolation - MetricCard: replace RegExp constructor with exact string match to fix ESLint non-literal-regexp warning Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
🤖 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
brand-*/surface-*color palette with Warm Ops design system tokensdesign-tokens.cssis the single source of truth (--so-*variables);global.cssreferences them via@theme inlineso Tailwind utilities resolve at runtime -- enabling theme/density switching by overriding CSS variables--so-density-*variables@fontsource-variable/geist-monofor monospace data displaygetStatusColor()andgetHealthColor()shared utilitiesbg-card-hovertoken tobrand-and-ux.mdcolor tableTest plan
npm --prefix web run type-check-- zero errorsnpm --prefix web run lint-- zero errorsnpm --prefix web run test-- 295 tests pass (32 test files)npm --prefix web run storybook-- verify all 11 component stories render (Button, Sidebar + 9 new)Review coverage
Pre-reviewed by 3 agents (frontend-reviewer, issue-resolution-verifier, docs-consistency). 14 findings identified, all addressed:
Closes #775
🤖 Generated with Claude Code