Skip to content

feat: design tokens and core UI components#833

Merged
Aureliolo merged 6 commits intomainfrom
feat/design-tokens-core-ui
Mar 26, 2026
Merged

feat: design tokens and core UI components#833
Aureliolo merged 6 commits intomainfrom
feat/design-tokens-core-ui

Conversation

@Aureliolo
Copy link
Copy Markdown
Owner

Summary

  • Replace old brand-*/surface-* color palette with Warm Ops design system tokens
  • design-tokens.css is the single source of truth (--so-* variables); global.css references them via @theme inline so Tailwind utilities resolve at runtime -- enabling theme/density switching by overriding CSS variables
  • Add density axis tokens (balanced/dense/medium/sparse) with --so-density-* variables
  • Install @fontsource-variable/geist-mono for monospace data display
  • Implement 9 core components: Avatar, StatusBadge, Sparkline (with animated draw), StatPill, SectionCard, ProgressGauge, DeptHealthBar, MetricCard, AgentCard
  • Each component has Storybook stories (autodocs) and unit tests (295 total, all pass)
  • Migrate all existing layout components, pages, and router from old color tokens to new system
  • Add getStatusColor() and getHealthColor() shared utilities
  • Add bg-card-hover token to brand-and-ux.md color table

Test plan

  • npm --prefix web run type-check -- zero errors
  • npm --prefix web run lint -- zero errors
  • npm --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)
  • Visual spot-check: colors match Warm Ops spec, animations work, dark theme correct

Review coverage

Pre-reviewed by 3 agents (frontend-reviewer, issue-resolution-verifier, docs-consistency). 14 findings identified, all addressed:

  • Fixed division-by-zero in Sparkline (data.length=1) and MetricCard (progress.total=0)
  • Added animated draw to Sparkline (stroke-dasharray + fade-in with prefers-reduced-motion support)
  • Replaced hardcoded shadow/color values with design tokens
  • Added aria-label to MetricCard progressbar
  • Added borderColor prop to Avatar for department-colored borders
  • Added .density-medium level to design-tokens.css
  • Added bg-card-hover to brand-and-ux.md color table

Closes #775

🤖 Generated with Claude Code

Aureliolo and others added 2 commits March 26, 2026 08:48
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>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 26, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: dd69f3b8-11f6-455a-acb3-bb748ad64407

📥 Commits

Reviewing files that changed from the base of the PR and between 2e9d520 and 8be6149.

📒 Files selected for processing (2)
  • web/src/__tests__/components/layout/SidebarNavItem.test.tsx
  • web/src/__tests__/components/ui/metric-card.test.tsx
📜 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)
  • GitHub Check: Build Web
  • GitHub Check: Build Backend
  • 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/layout/SidebarNavItem.test.tsx
  • web/src/__tests__/components/ui/metric-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/layout/SidebarNavItem.test.tsx
  • web/src/__tests__/components/ui/metric-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
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...
📚 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.tsx
  • web/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/layout/SidebarNavItem.test.tsx
  • web/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} : 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/layout/SidebarNavItem.test.tsx
  • web/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/metric-card.test.tsx
🔇 Additional comments (6)
web/src/__tests__/components/layout/SidebarNavItem.test.tsx (4)

2-2: LGTM!

Correct import for fast-check to enable property-based testing as required by the coding guidelines.


57-65: LGTM — scoped query and token migration applied correctly.

The switch from document.querySelector to container.querySelector properly isolates the test from global DOM state. The dotColor value update from bg-success-500 to bg-success aligns with the design token migration.


67-74: LGTM!

Consistent application of the container-scoped query pattern for the negative test case.


76-98: LGTM — well-structured property-based tests.

Both property tests correctly exercise the badge boundary logic:

  • [100, 10000] validates the badge > 99 threshold shows "99+"
  • [1, 99] validates exact count display

The explicit unmount() calls between iterations prevent DOM accumulation and are essential for running assertions via the global screen helper. This follows the coding guideline for using fc.assert + fc.property.

web/src/__tests__/components/ui/metric-card.test.tsx (2)

5-89: Strong unit coverage for conditional MetricCard states.

These tests cleanly cover the main render branches and key edge cases (notably single-point sparkline suppression and progress.total = 0 behavior).


90-115: Property tests are correctly implemented and previous matcher issue is fixed.

Great use of fc.assert/fc.property, and Line 110’s exact string assertion avoids the prior dynamic-RegExp lint pitfall.

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."


Walkthrough

Introduces a design-token system and integrates it into global styles (new --so-* tokens, theme bridge, density tokens, and a mono font import). Adds multiple UI components with TypeScript props and Storybook stories: Avatar, AgentCard, StatusBadge, MetricCard, Sparkline, ProgressGauge, DeptHealthBar, SectionCard, and StatPill. Adds unit and property-based tests for components and utils, new exported types/functions (AgentRuntimeStatus, SemanticColor, getStatusColor, getHealthColor), updates layout/pages and Storybook preview to use the new semantic tokens, and updates package manifest to include a mono font package.

Suggested labels

autorelease: tagged

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 40.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately describes the primary change: introducing design tokens and implementing core UI components, which is the main focus of this substantial changeset.
Description check ✅ Passed The PR description is comprehensive and directly related to the changeset, covering the design system migration, new components, tests, and implementation details.
Linked Issues check ✅ Passed The PR successfully implements all requirements from issue #775: design tokens CSS custom properties, Tailwind theme configuration, all 9 core components (Avatar, StatusBadge, Sparkline, StatPill, SectionCard, ProgressGauge, DeptHealthBar, MetricCard, AgentCard) with Storybook stories and comprehensive unit tests (295 tests across 32 files).
Out of Scope Changes check ✅ Passed All changes align with the stated objectives: design token migration, core component implementation, Storybook integration, unit test coverage, and migration of existing components to the new token system. No out-of-scope changes detected.

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


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

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, 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

  • Design System Implementation: Replaced the old brand-*/surface-* color palette with a new Warm Ops design system, utilizing --so-* CSS variables as the single source of truth for styling.
  • Core UI Components: Introduced 9 new core UI components: Avatar, StatusBadge, Sparkline, StatPill, SectionCard, ProgressGauge, DeptHealthBar, MetricCard, and AgentCard, each with Storybook stories and comprehensive unit tests.
  • Density Axis Tokens: Added density axis tokens (--so-density-*) to allow for flexible control over card padding, section gaps, and grid gaps, supporting balanced, dense, medium, and sparse layouts.
  • Font Integration: Integrated @fontsource-variable/geist-mono to provide a consistent monospace font for data display across the application.
  • Color and Layout Migration: Migrated all existing layout components, pages, and router elements to use the new design system's color tokens, ensuring a unified visual experience.
  • Utility Functions: Added getStatusColor() and getHealthColor() shared utilities to standardize color mapping based on agent status and health percentages.
Using Gemini Code Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

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

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 26, 2026 08:25 — with GitHub Actions Inactive
@socket-security
Copy link
Copy Markdown

socket-security bot commented Mar 26, 2026

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 26, 2026

Dependency Review

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

Snapshot Warnings

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

OpenSSF Scorecard

PackageVersionScoreDetails
npm/@fontsource-variable/geist-mono 5.2.7 UnknownUnknown

Scanned Files

  • web/package-lock.json

Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

Code Review

This pull request 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.

<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">
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The font size text-[13px] is an arbitrary value. To maintain consistency with the design system, it's better to use predefined text size tokens from Tailwind's theme (e.g., text-xs or text-sm). If a new size is needed, it should be added to the theme configuration for better maintainability.

)}
{timestamp && (
<div className="mt-0.5 text-right">
<span className="font-mono text-[10px] text-muted-foreground">{timestamp}</span>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The font size text-[10px] is an arbitrary value. Consider using a standard text size from the design system or creating a new token for this size if it's used frequently. This will improve maintainability and consistency.

import { cn } from '@/lib/utils'

const SIZE_CLASSES = {
sm: 'size-6 text-[10px]',
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The font size text-[10px] is an arbitrary value. To maintain consistency with the design system, please use a predefined text size token or add a new one to the theme configuration if this size is used across multiple components.

>
{/* Top row: label + sparkline */}
<div className="flex items-start justify-between">
<span className="text-[11px] uppercase tracking-[0.06em] text-muted-foreground">
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

This component uses an arbitrary font size text-[11px]. Using values from the design system's typography scale would improve consistency. If this custom size is required by the design, it should be added as a token to the theme configuration.

</div>

{/* Value */}
<div className="mt-1 font-mono text-[26px] font-bold leading-tight tracking-tight text-foreground">
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The font size text-[26px] is an arbitrary value. To align with the design system, it's recommended to use a predefined text size token (like text-2xl or text-3xl) or add a new token to the theme's typography scale.

Comment on lines +11 to +12
sm: { radius: 32, stroke: 6, valueSize: 'text-sm', labelSize: 'text-[10px]' },
md: { radius: 48, stroke: 6, valueSize: 'text-lg', labelSize: 'text-[11px]' },
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The font sizes text-[10px] and text-[11px] are arbitrary values. To align with the design system, it's recommended to use predefined text size tokens. If these sizes are part of the design spec, they should be added to the theme's typography scale for consistency and maintainability.

className,
)}
>
<span className="text-[11px] uppercase tracking-wide text-muted-foreground">
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The font size text-[11px] is an arbitrary value. Please use a predefined text size token from the design system to ensure consistency. If this size is intentional, consider adding it as a new token to the theme.

}

.density-medium {
--so-density-card-padding: 14px;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The 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).

Suggested change
--so-density-card-padding: 14px;
--so-density-card-padding: var(--so-space-3);

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 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 | 🔵 Trivial

Consider 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 | 🔴 Critical

Fix badge text contrast on danger background to meet WCAG AA (4.5:1 required).

The current combination of text-foreground (#e2e8f0) on bg-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

📥 Commits

Reviewing files that changed from the base of the PR and between d4dde90 and 96a07b6.

⛔ Files ignored due to path filters (1)
  • web/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (44)
  • docs/design/brand-and-ux.md
  • web/.storybook/preview.tsx
  • web/package.json
  • web/src/__tests__/components/layout/SidebarNavItem.test.tsx
  • web/src/__tests__/components/ui/agent-card.test.tsx
  • web/src/__tests__/components/ui/avatar.test.tsx
  • web/src/__tests__/components/ui/dept-health-bar.test.tsx
  • web/src/__tests__/components/ui/metric-card.test.tsx
  • web/src/__tests__/components/ui/progress-gauge.test.tsx
  • web/src/__tests__/components/ui/section-card.test.tsx
  • web/src/__tests__/components/ui/sparkline.test.tsx
  • web/src/__tests__/components/ui/stat-pill.test.tsx
  • web/src/__tests__/components/ui/status-badge.test.tsx
  • web/src/components/layout/AppLayout.tsx
  • web/src/components/layout/PlaceholderPage.tsx
  • web/src/components/layout/Sidebar.tsx
  • web/src/components/layout/SidebarNavItem.tsx
  • web/src/components/layout/StatusBar.tsx
  • web/src/components/ui/agent-card.stories.tsx
  • web/src/components/ui/agent-card.tsx
  • web/src/components/ui/avatar.stories.tsx
  • web/src/components/ui/avatar.tsx
  • web/src/components/ui/dept-health-bar.stories.tsx
  • web/src/components/ui/dept-health-bar.tsx
  • web/src/components/ui/metric-card.stories.tsx
  • web/src/components/ui/metric-card.tsx
  • web/src/components/ui/progress-gauge.stories.tsx
  • web/src/components/ui/progress-gauge.tsx
  • web/src/components/ui/section-card.stories.tsx
  • web/src/components/ui/section-card.tsx
  • web/src/components/ui/sparkline.stories.tsx
  • web/src/components/ui/sparkline.tsx
  • web/src/components/ui/stat-pill.stories.tsx
  • web/src/components/ui/stat-pill.tsx
  • web/src/components/ui/status-badge.stories.tsx
  • web/src/components/ui/status-badge.tsx
  • web/src/lib/utils.ts
  • web/src/pages/LoginPage.tsx
  • web/src/pages/NotFoundPage.tsx
  • web/src/pages/SetupPage.tsx
  • web/src/router/guards.tsx
  • web/src/router/index.tsx
  • web/src/styles/design-tokens.css
  • web/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.tsx
  • web/src/pages/SetupPage.tsx
  • web/src/router/index.tsx
  • web/src/components/layout/PlaceholderPage.tsx
  • web/src/router/guards.tsx
  • web/src/__tests__/components/layout/SidebarNavItem.test.tsx
  • web/src/pages/LoginPage.tsx
  • web/src/__tests__/components/ui/stat-pill.test.tsx
  • web/src/__tests__/components/ui/dept-health-bar.test.tsx
  • web/src/components/ui/dept-health-bar.stories.tsx
  • web/src/pages/NotFoundPage.tsx
  • web/src/__tests__/components/ui/section-card.test.tsx
  • web/src/__tests__/components/ui/avatar.test.tsx
  • web/src/components/ui/agent-card.stories.tsx
  • web/src/__tests__/components/ui/progress-gauge.test.tsx
  • web/src/__tests__/components/ui/sparkline.test.tsx
  • web/src/__tests__/components/ui/agent-card.test.tsx
  • web/src/components/layout/SidebarNavItem.tsx
  • web/src/__tests__/components/ui/status-badge.test.tsx
  • web/src/components/layout/Sidebar.tsx
  • web/src/__tests__/components/ui/metric-card.test.tsx
  • web/src/components/ui/stat-pill.tsx
  • web/src/lib/utils.ts
  • web/src/components/ui/avatar.stories.tsx
  • web/src/components/ui/avatar.tsx
  • web/src/components/ui/section-card.tsx
  • web/src/components/ui/agent-card.tsx
  • web/src/components/ui/dept-health-bar.tsx
  • web/src/components/ui/sparkline.stories.tsx
  • web/src/components/ui/status-badge.stories.tsx
  • web/src/components/ui/status-badge.tsx
  • web/src/components/ui/stat-pill.stories.tsx
  • web/src/components/layout/StatusBar.tsx
  • web/src/components/ui/metric-card.stories.tsx
  • web/src/components/ui/progress-gauge.tsx
  • web/src/components/ui/metric-card.tsx
  • web/src/components/ui/sparkline.tsx
  • web/src/components/ui/section-card.stories.tsx
  • web/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.tsx
  • web/src/__tests__/components/ui/stat-pill.test.tsx
  • web/src/__tests__/components/ui/dept-health-bar.test.tsx
  • web/src/__tests__/components/ui/section-card.test.tsx
  • web/src/__tests__/components/ui/avatar.test.tsx
  • web/src/__tests__/components/ui/progress-gauge.test.tsx
  • web/src/__tests__/components/ui/sparkline.test.tsx
  • web/src/__tests__/components/ui/agent-card.test.tsx
  • web/src/__tests__/components/ui/status-badge.test.tsx
  • web/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.tsx
  • web/src/pages/SetupPage.tsx
  • web/src/router/index.tsx
  • web/package.json
  • web/src/components/layout/PlaceholderPage.tsx
  • web/src/router/guards.tsx
  • web/src/pages/LoginPage.tsx
  • web/src/__tests__/components/ui/stat-pill.test.tsx
  • web/src/__tests__/components/ui/dept-health-bar.test.tsx
  • web/src/components/ui/dept-health-bar.stories.tsx
  • web/src/__tests__/components/ui/section-card.test.tsx
  • web/src/components/ui/agent-card.stories.tsx
  • web/src/__tests__/components/ui/agent-card.test.tsx
  • web/src/components/layout/SidebarNavItem.tsx
  • web/src/__tests__/components/ui/status-badge.test.tsx
  • web/src/components/layout/Sidebar.tsx
  • web/src/components/ui/stat-pill.tsx
  • web/src/lib/utils.ts
  • web/src/components/ui/avatar.stories.tsx
  • web/src/components/ui/avatar.tsx
  • web/src/components/ui/section-card.tsx
  • web/src/components/ui/agent-card.tsx
  • web/src/components/ui/dept-health-bar.tsx
  • web/src/components/ui/status-badge.stories.tsx
  • web/src/components/ui/status-badge.tsx
  • web/src/components/layout/StatusBar.tsx
  • web/src/components/ui/progress-gauge.tsx
  • web/src/components/ui/metric-card.tsx
  • web/src/components/ui/sparkline.tsx
  • web/src/components/ui/section-card.stories.tsx
  • web/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.tsx
  • web/src/__tests__/components/ui/stat-pill.test.tsx
  • web/src/__tests__/components/ui/dept-health-bar.test.tsx
  • web/src/__tests__/components/ui/section-card.test.tsx
  • web/src/__tests__/components/ui/avatar.test.tsx
  • web/src/__tests__/components/ui/progress-gauge.test.tsx
  • web/src/__tests__/components/ui/sparkline.test.tsx
  • web/src/__tests__/components/ui/agent-card.test.tsx
  • web/src/__tests__/components/ui/status-badge.test.tsx
  • web/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.tsx
  • web/src/__tests__/components/ui/dept-health-bar.test.tsx
  • web/src/__tests__/components/ui/section-card.test.tsx
  • web/src/__tests__/components/ui/avatar.test.tsx
  • web/src/__tests__/components/ui/progress-gauge.test.tsx
  • web/src/__tests__/components/ui/sparkline.test.tsx
  • web/src/__tests__/components/ui/agent-card.test.tsx
  • web/src/__tests__/components/ui/status-badge.test.tsx
  • web/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.tsx
  • web/src/__tests__/components/ui/sparkline.test.tsx
  • web/src/__tests__/components/ui/status-badge.test.tsx
  • web/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.tsx
  • web/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-mono dependency 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-hover token is properly documented with the correct hex value (#181828), which matches the implementation in design-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:

  • AgentStatus and SemanticColor types provide clear contracts
  • getStatusColor uses a const map for type-safe lookups, and the "text-secondary" fallback for offline is handled by consumers (verified in status-badge.tsx DOT_COLOR_CLASSES)
  • getHealthColor thresholds are clear and match usage in dept-health-bar.tsx and progress-gauge.tsx
web/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 the cn() 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 h3 for title maintains heading hierarchy
  • Density-aware spacing tokens (px-card, p-card) align with the design system
web/src/components/ui/avatar.tsx (1)

1-44: LGTM!

Solid implementation with good edge case handling:

  • getInitials correctly handles single/multi-word names and empty strings via filter(Boolean)
  • aria-label={name || undefined} avoids rendering an empty accessibility attribute
  • The borderColor pattern with conditional border-2 is clean
web/src/components/ui/status-badge.tsx (1)

24-45: Looks solid for status rendering and accessibility metadata.

StatusBadge cleanly 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-500 to the semantic text-text-secondary token 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-500 to bg-success.

web/src/router/guards.tsx (2)

15-15: LGTM!

Token migration to text-text-secondary is consistent with the design system update.


30-36: LGTM!

The button styling update to bg-primary, text-primary-foreground, and hover:bg-primary/80 aligns 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-foreground aligns with shadcn/ui conventions for secondary text styling.


16-16: LGTM!

The bg-background token 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-valuenow attribute 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-hover aligns 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 in design-tokens.css and global.css. The Dot component properly uses aria-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). The LongTaskName story is a good edge case for text truncation testing, and AgentGrid provides 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. The AllSizes and WithDepartmentBorders composition 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 finite AgentStatus enum 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(). The AllHealthLevels composition 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 in design-tokens.css and wired through global.css's @theme inline block.

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). The AllStatuses story 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 NestedContent story 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 ZeroValue edge case where a falsy value should still render correctly. The Multiple story 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 inline block (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-variant is valid Tailwind CSS 4 syntax.

The Stylelint error scss/at-rule-no-unknown is a false positive. @custom-variant is 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 inline is valid Tailwind CSS 4 syntax.

The Stylelint scss/at-rule-no-unknown error is a false positive—@theme inline is 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 :root and .dark into a single ruleset for a dark-only app. The comment clearly explains this design decision. All shadcn variables correctly reference --so-* tokens from design-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. The autodocs tag 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 change object discriminates direction clearly, and optional props are correctly marked.


23-25: Good defensive handling of division-by-zero.

The guard progress.total > 0 prevents NaN/Infinity when total is zero or negative, defaulting to 0 percent.


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 argTypes controls. The range control for value and select for size enable 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.

Comment on lines +4 to +52
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()
})
})
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

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.

Suggested change
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.

Comment on lines +4 to +69
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')
})
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

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 value rendering with arbitrary numbers
  • change.value formatting for arbitrary percentages
  • progress percentage calculations with arbitrary current/total values
💡 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.

Comment on lines +72 to +100
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>
),
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

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.

Suggested change
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.

Comment on lines +46 to +56
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>
),
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

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.

Suggested change
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>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (4)
web/src/__tests__/components/ui/sparkline.test.tsx (1)

4-75: ⚠️ Potential issue | 🟠 Major

Add 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.tsx

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/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 | 🟠 Major

Add 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.tsx

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/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 | 🟠 Major

Add 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.tsx

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/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 | 🟠 Major

Add 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.tsx

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 - 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

📥 Commits

Reviewing files that changed from the base of the PR and between 96a07b6 and 40d31a8.

📒 Files selected for processing (18)
  • CLAUDE.md
  • web/src/__tests__/components/ui/avatar.test.tsx
  • web/src/__tests__/components/ui/dept-health-bar.test.tsx
  • web/src/__tests__/components/ui/metric-card.test.tsx
  • web/src/__tests__/components/ui/progress-gauge.test.tsx
  • web/src/__tests__/components/ui/sparkline.test.tsx
  • web/src/__tests__/lib/utils.test.ts
  • web/src/components/layout/SidebarNavItem.tsx
  • web/src/components/ui/agent-card.tsx
  • web/src/components/ui/avatar.tsx
  • web/src/components/ui/dept-health-bar.tsx
  • web/src/components/ui/metric-card.tsx
  • web/src/components/ui/progress-gauge.tsx
  • web/src/components/ui/stat-pill.tsx
  • web/src/components/ui/status-badge.tsx
  • web/src/lib/utils.ts
  • web/src/styles/design-tokens.css
  • web/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.tsx
  • web/src/components/layout/SidebarNavItem.tsx
  • web/src/__tests__/lib/utils.test.ts
  • web/src/__tests__/components/ui/avatar.test.tsx
  • web/src/__tests__/components/ui/metric-card.test.tsx
  • web/src/__tests__/components/ui/sparkline.test.tsx
  • web/src/__tests__/components/ui/progress-gauge.test.tsx
  • web/src/components/ui/progress-gauge.tsx
  • web/src/lib/utils.ts
  • web/src/components/ui/avatar.tsx
  • web/src/components/ui/dept-health-bar.tsx
  • web/src/components/ui/status-badge.tsx
  • web/src/components/ui/stat-pill.tsx
  • web/src/components/ui/metric-card.tsx
  • web/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.tsx
  • web/src/__tests__/lib/utils.test.ts
  • web/src/__tests__/components/ui/avatar.test.tsx
  • web/src/__tests__/components/ui/metric-card.test.tsx
  • web/src/__tests__/components/ui/sparkline.test.tsx
  • web/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.tsx
  • web/src/__tests__/lib/utils.test.ts
  • web/src/__tests__/components/ui/avatar.test.tsx
  • web/src/__tests__/components/ui/metric-card.test.tsx
  • web/src/__tests__/components/ui/sparkline.test.tsx
  • web/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.tsx
  • web/src/__tests__/lib/utils.test.ts
  • web/src/__tests__/components/ui/avatar.test.tsx
  • web/src/__tests__/components/ui/metric-card.test.tsx
  • web/src/__tests__/components/ui/sparkline.test.tsx
  • web/src/__tests__/components/ui/progress-gauge.test.tsx
  • 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 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.tsx
  • web/src/components/layout/SidebarNavItem.tsx
  • web/src/__tests__/components/ui/avatar.test.tsx
  • web/src/__tests__/components/ui/sparkline.test.tsx
  • CLAUDE.md
  • web/src/components/ui/progress-gauge.tsx
  • web/src/lib/utils.ts
  • web/src/components/ui/avatar.tsx
  • web/src/components/ui/dept-health-bar.tsx
  • web/src/components/ui/status-badge.tsx
  • web/src/components/ui/stat-pill.tsx
  • web/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.tsx
  • web/src/__tests__/components/ui/metric-card.test.tsx
  • web/src/__tests__/components/ui/sparkline.test.tsx
  • web/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 getInitials logic.

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-layer AgentStatus with explicit documentation about the mapping boundary
  • Record<AgentRuntimeStatus, ...> enforces exhaustive coverage at compile time
  • The "text-secondary" fallback for "offline" is properly typed in the union

32-42: LGTM!

The getHealthColor function 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 including max=0.


48-99: LGTM!

Excellent accessibility implementation:

  • Proper role="meter" with complete ARIA attributes
  • aria-label includes context when label is 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 Avatar and StatusBadge
  • 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_CLASSES map correctly types the union SemanticColor | 'text-secondary' to handle the offline status fallback, ensuring type-safe lookups from getStatusColor().


25-46: LGTM!

Clean implementation with:

  • Always-present aria-label for accessibility
  • data-slot attribute for external styling/testing hooks
  • Conditional animate-pulse via the pulse prop
web/src/components/ui/avatar.tsx (2)

9-16: LGTM!

The getInitials function 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 display
  • aria-label={name || undefined} correctly omits the attribute when name is empty
  • Flexible borderColor prop 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-5 intermediate spacing token are well-integrated into the existing scale. The addition of --so-space-3-5 also addresses the previous review feedback about the hardcoded 14px value 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 hardcoded 14px. 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 inline approach ensures Tailwind utilities resolve to var() 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, .dark ensures both base utilities and dark: 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 *));
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

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>
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 26, 2026 09:14 — with GitHub Actions Inactive
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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

📥 Commits

Reviewing files that changed from the base of the PR and between 40d31a8 and fd090a9.

📒 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 MetricCard component 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 ChangeBadge helper component correctly implements:

  • Accessible aria-label describing 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>
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 26, 2026 09:28 — with GitHub Actions Inactive
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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

📥 Commits

Reviewing files that changed from the base of the PR and between fd090a9 and 2e9d520.

📒 Files selected for processing (11)
  • web/src/__tests__/components/layout/SidebarNavItem.test.tsx
  • web/src/__tests__/components/ui/agent-card.test.tsx
  • web/src/__tests__/components/ui/avatar.test.tsx
  • web/src/__tests__/components/ui/dept-health-bar.test.tsx
  • web/src/__tests__/components/ui/metric-card.test.tsx
  • web/src/__tests__/components/ui/progress-gauge.test.tsx
  • web/src/__tests__/components/ui/section-card.test.tsx
  • web/src/__tests__/components/ui/sparkline.test.tsx
  • web/src/__tests__/components/ui/stat-pill.test.tsx
  • web/src/__tests__/lib/utils.test.ts
  • 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 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.tsx
  • web/src/__tests__/components/layout/SidebarNavItem.test.tsx
  • web/src/__tests__/components/ui/sparkline.test.tsx
  • web/src/__tests__/lib/utils.test.ts
  • web/src/__tests__/components/ui/progress-gauge.test.tsx
  • web/src/__tests__/components/ui/avatar.test.tsx
  • web/src/__tests__/components/ui/dept-health-bar.test.tsx
  • web/src/__tests__/components/ui/metric-card.test.tsx
  • web/src/__tests__/components/ui/section-card.test.tsx
  • web/src/components/ui/metric-card.tsx
  • web/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.tsx
  • web/src/__tests__/components/layout/SidebarNavItem.test.tsx
  • web/src/__tests__/components/ui/sparkline.test.tsx
  • web/src/__tests__/lib/utils.test.ts
  • web/src/__tests__/components/ui/progress-gauge.test.tsx
  • web/src/__tests__/components/ui/avatar.test.tsx
  • web/src/__tests__/components/ui/dept-health-bar.test.tsx
  • web/src/__tests__/components/ui/metric-card.test.tsx
  • web/src/__tests__/components/ui/section-card.test.tsx
  • web/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.tsx
  • web/src/__tests__/components/layout/SidebarNavItem.test.tsx
  • web/src/__tests__/components/ui/sparkline.test.tsx
  • web/src/__tests__/lib/utils.test.ts
  • web/src/__tests__/components/ui/progress-gauge.test.tsx
  • web/src/__tests__/components/ui/avatar.test.tsx
  • web/src/__tests__/components/ui/dept-health-bar.test.tsx
  • web/src/__tests__/components/ui/metric-card.test.tsx
  • web/src/__tests__/components/ui/section-card.test.tsx
  • web/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.tsx
  • web/src/__tests__/components/layout/SidebarNavItem.test.tsx
  • web/src/__tests__/components/ui/sparkline.test.tsx
  • web/src/__tests__/lib/utils.test.ts
  • web/src/__tests__/components/ui/progress-gauge.test.tsx
  • web/src/__tests__/components/ui/avatar.test.tsx
  • web/src/__tests__/components/ui/dept-health-bar.test.tsx
  • web/src/__tests__/components/ui/metric-card.test.tsx
  • web/src/__tests__/components/ui/section-card.test.tsx
  • web/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.tsx
  • web/src/__tests__/components/layout/SidebarNavItem.test.tsx
  • web/src/__tests__/components/ui/progress-gauge.test.tsx
  • web/src/__tests__/components/ui/avatar.test.tsx
  • web/src/__tests__/components/ui/dept-health-bar.test.tsx
  • web/src/__tests__/components/ui/section-card.test.tsx
  • web/src/components/ui/metric-card.tsx
  • web/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.tsx
  • web/src/__tests__/components/layout/SidebarNavItem.test.tsx
  • web/src/__tests__/components/ui/sparkline.test.tsx
  • web/src/__tests__/lib/utils.test.ts
  • web/src/__tests__/components/ui/progress-gauge.test.tsx
  • web/src/__tests__/components/ui/dept-health-bar.test.tsx
  • web/src/__tests__/components/ui/metric-card.test.tsx
  • web/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 calls unmount() 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 + unmount without DOM assertions) is a pragmatic choice—it handles the empty string edge case that would break getByText('') 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-run unmount() 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>
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 26, 2026 10:24 — with GitHub Actions Inactive
@Aureliolo Aureliolo merged commit ed887f2 into main Mar 26, 2026
31 of 32 checks passed
@Aureliolo Aureliolo deleted the feat/design-tokens-core-ui branch March 26, 2026 13:21
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 26, 2026 13:21 — with GitHub Actions Inactive
Aureliolo added a commit that referenced this pull request Mar 30, 2026
🤖 I have created a release *beep* *boop*
---
#MAJOR CHANGES; We got a somewhat working webui :)

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


### Features

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


### Bug Fixes

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


### Refactoring

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


### Documentation

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


### Tests

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


### CI/CD

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


### Maintenance

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

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

---------

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: design tokens and core UI components

1 participant