Skip to content

feat: app shell, Storybook, and CI/CD pipeline#819

Merged
Aureliolo merged 7 commits intomainfrom
feat/770-app-shell-storybook
Mar 24, 2026
Merged

feat: app shell, Storybook, and CI/CD pipeline#819
Aureliolo merged 7 commits intomainfrom
feat/770-app-shell-storybook

Conversation

@Aureliolo
Copy link
Copy Markdown
Owner

Summary

  • App Shell: React Router v7 with createBrowserRouter, lazy-loaded routes for all 20+ pages from page-structure.md, 4 route guards (AuthGuard, SetupGuard, GuestGuard, SetupCompleteGuard), collapsible sidebar (220px/56px icon rail) with primary + workspace nav sections, full-width status bar skeleton matching mockup v2 layout, 404 not-found page
  • Storybook: @storybook/react v8 + Vite builder, Tailwind CSS 4 integration via viteFinal, dark theme config, @storybook/addon-a11y + interactions, initial stories for Button and Sidebar
  • CI/CD: New dashboard-storybook-build job in ci.yml, web/.npmrc for consistent legacy-peer-deps resolution across CI/Docker/local dev
  • Testing: 216 tests passing across 23 files -- route guard tests, sidebar tests, SidebarNavItem tests, setup store tests, test utilities

Pre-reviewed by 5 agents, 15 findings addressed

Agent Findings
frontend-reviewer 4 (broken CSS tokens, NavLink end prop, Zustand selectors, cn() usage)
docs-consistency 4 (CLAUDE.md package structure, storybook commands, CI description)
infra-reviewer 2 (.npmrc instead of CLI flags, audit consistency)
test-quality-reviewer 4 (setup store tests, guard fetch test, SidebarNavItem tests, logout test)
issue-resolution-verifier 1 (all 5 deliverables RESOLVED)

Test plan

  • npm --prefix web run dev -- navigate to localhost:5173, verify redirect to /login
  • Sidebar: all 10 nav items visible, collapse/expand toggles, persists across reload
  • Route changes on nav click, active state highlights correctly
  • /nonexistent shows 404 page with back-to-dashboard link
  • npm --prefix web run storybook -- serves at localhost:6006, Button + Sidebar stories render
  • npm --prefix web run lint && npm --prefix web run type-check && npm --prefix web run test -- all pass
  • npm --prefix web run build -- production build succeeds
  • npm --prefix web run storybook:build -- storybook static build succeeds

Closes #770

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 24, 2026

Walkthrough

Adds an application shell and dashboard developer tooling. Introduces React Router with lazy-loaded routes and four guards (AuthGuard, SetupGuard, GuestGuard, SetupCompleteGuard), an AppLayout with Sidebar and StatusBar, PlaceholderPage, SidebarNavItem, many placeholder page components and NotFound, a centralized ROUTES constant and AppRouter, and a typed Zustand setup store exposing fetchSetupStatus. Adds Storybook config, stories, npm scripts and web/.npmrc, test utilities and extensive Vitest/RTL tests, a .gitignore entry for storybook-static, a Dockerfile change to include web/.npmrc during install, and a CI job to build Storybook wired into the ci-pass gate.

Suggested labels

autorelease: tagged

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 19.15% 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 title 'feat: app shell, Storybook, and CI/CD pipeline' clearly and concisely summarizes the three main components added in this changeset: the app shell, Storybook integration, and CI/CD updates. It is specific and directly related to the primary changes.
Description check ✅ Passed The description comprehensively outlines the changeset across app shell, Storybook, CI/CD, and testing components, clearly relating to the code changes and providing context on pre-review findings and test plan.
Linked Issues check ✅ Passed The pull request fulfills all primary objectives from issue #770: app shell with React Router, lazy-loaded routes, 4 route guards, sidebar with collapse behavior, status bar, 404 page, Storybook with Tailwind and dark theme, addon-a11y and interactions, and CI/CD pipeline updates with dashboard-storybook-build job and .npmrc configuration.
Out of Scope Changes check ✅ Passed All changes directly support the stated objectives: app shell components, routing infrastructure, Storybook configuration, CI/CD pipeline updates, comprehensive test coverage, and documentation updates are all within scope of issue #770 Phase 1.3 deliverables.

✏️ 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 establishes the foundational user interface and routing infrastructure for the web dashboard, integrating a complete application shell with dynamic navigation and robust authentication/setup guards. It also introduces Storybook for component development and enhances the CI/CD pipeline to support these new frontend capabilities, significantly advancing the project's frontend development readiness.

Highlights

  • App Shell: Implemented React Router v7 with createBrowserRouter, including lazy-loaded routes for over 20 pages, 4 route guards (AuthGuard, SetupGuard, GuestGuard, SetupCompleteGuard), a collapsible sidebar with primary and workspace navigation, a full-width status bar skeleton, and a 404 not-found page.
  • Storybook Integration: Integrated @storybook/react v8 with Vite builder, configured Tailwind CSS 4, added dark theme support, and included @storybook/addon-a11y and interactions. Initial stories for Button and Sidebar components were also added.
  • CI/CD Pipeline Enhancements: Introduced a new dashboard-storybook-build job in ci.yml and added web/.npmrc to ensure consistent legacy-peer-deps resolution across CI, Docker, and local development environments.
  • Comprehensive Testing: Added 216 new tests across 23 files, covering route guards, sidebar functionality, SidebarNavItem, and setup store logic, ensuring robust coverage for the new features.
Ignored Files
  • Ignored by pattern: .github/workflows/** (1)
    • .github/workflows/ci.yml
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.

@socket-security
Copy link
Copy Markdown

socket-security bot commented Mar 24, 2026

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addednpm/​@​types/​grecaptcha@​3.0.91001007180100
Addednpm/​@​astrojs/​sitemap@​3.7.11001008292100
Addednpm/​tailwindcss@​4.2.21001008498100
Addednpm/​astro@​6.0.8981008898100
Addednpm/​wrangler@​4.76.0981009296100

View full report

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 24, 2026

Dependency Review

The following issues were found:

  • ✅ 0 vulnerable package(s)
  • ✅ 0 package(s) with incompatible licenses
  • ✅ 0 package(s) with invalid SPDX license definitions
  • ✅ 0 package(s) with unknown licenses.
  • ⚠️ 8 packages with OpenSSF Scorecard issues.

View full job summary

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 significantly refactors the web dashboard's routing and UI structure. It introduces React Router for navigation, implementing authentication and setup guards to manage access. A new AppLayout component is added, featuring a Sidebar and StatusBar, along with numerous placeholder page components for various routes. Storybook is integrated for UI component development, including configuration, dependencies, and initial stories for Sidebar and Button. Testing utilities for router components are introduced, and existing tests are updated. The .gitignore and CLAUDE.md documentation are updated to reflect Storybook integration. A new Zustand store for managing application setup status is also implemented. Review comments suggest improvements such as using decorators for Storybook initial state setup, extracting duplicated loading UI into a shared component, removing redundant CSS classes, and improving type safety in test store updates.

const user = userEvent.setup()
const logoutSpy = vi.fn()
setup()
useAuthStore.setState({ logout: logoutSpy } as never)
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 as never type assertion is used here to bypass TypeScript's type checking for a partial store update. While this works for the test, it's not type-safe and can make the test brittle if the store's structure changes.

A safer approach is to spread the existing state to ensure the full type is satisfied, only overriding the properties relevant to the test.

Suggested change
useAuthStore.setState({ logout: logoutSpy } as never)
useAuthStore.setState({ ...useAuthStore.getState(), logout: logoutSpy })

Comment on lines +6 to +9
interface RenderWithRouterOptions {
initialEntries?: string[]
routes?: RouteObject[]
}
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 routes property in the RenderWithRouterOptions interface is not used within the renderWithRouter function. The function's implementation is designed to wrap a single element in a wildcard route. The separate renderRoutes function is correctly designed to handle a full array of RouteObjects.

To avoid confusion and make the interface accurately reflect its usage, the routes property should be removed.

interface RenderWithRouterOptions {
  initialEntries?: string[]
}

Comment on lines +38 to +54
export const Expanded: Story = {
play: () => {
localStorage.setItem('sidebar_collapsed', 'false')
},
}

export const Collapsed: Story = {
play: () => {
localStorage.setItem('sidebar_collapsed', 'true')
},
decorators: [
(Story) => {
localStorage.setItem('sidebar_collapsed', 'true')
return <Story />
},
],
}
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 play functions in the Expanded and Collapsed stories are being used to set up initial state in localStorage. The play function is intended for simulating user interactions after a story renders. For setting up initial state or context, decorators are the more appropriate and idiomatic Storybook feature.

The Collapsed story is also redundant, as it sets the same localStorage key in both the play function and a decorator.

To improve consistency and correctness, I recommend using decorators for both stories and removing the play functions.

Suggested change
export const Expanded: Story = {
play: () => {
localStorage.setItem('sidebar_collapsed', 'false')
},
}
export const Collapsed: Story = {
play: () => {
localStorage.setItem('sidebar_collapsed', 'true')
},
decorators: [
(Story) => {
localStorage.setItem('sidebar_collapsed', 'true')
return <Story />
},
],
}
export const Expanded: Story = {
decorators: [
(Story) => {
localStorage.setItem('sidebar_collapsed', 'false')
return <Story />
},
],
}
export const Collapsed: Story = {
decorators: [
(Story) => {
localStorage.setItem('sidebar_collapsed', 'true')
return <Story />
},
],
}

Comment on lines +206 to +208
<div className="text-sm font-medium text-surface-700 truncate">
{user.username}
</div>
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 truncate class on this div is redundant because its parent div on line 205 already has the truncate class. CSS utility classes for truncation apply to the container, so applying it again on a direct child has no additional effect and adds unnecessary clutter.

Suggested change
<div className="text-sm font-medium text-surface-700 truncate">
{user.username}
</div>
<div className="text-sm font-medium text-surface-700">
{user.username}
</div>

Comment on lines +37 to +43
if (setupComplete === null || loading) {
return (
<div className="flex h-screen items-center justify-center">
<div className="text-surface-500 text-sm">Loading...</div>
</div>
)
}
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 loading indicator UI is duplicated in SetupCompleteGuard (lines 87-93) and is also very similar to the suspense fallbacks used elsewhere (e.g., in router/index.tsx).

To improve maintainability and ensure a consistent loading experience across the application, this UI should be extracted into a shared component, for example a FullScreenLoader.

You could create a new component like this:

// src/components/common/FullScreenLoader.tsx
export function FullScreenLoader() {
  return (
    <div className="flex h-screen items-center justify-center">
      <div className="text-surface-500 text-sm">Loading...</div>
    </div>
  );
}

Then you could use <FullScreenLoader /> here and in SetupCompleteGuard to reduce duplication.

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

🤖 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/package.json`:
- Around line 43-48: The Storybook deps have mismatched minor versions; update
all Storybook packages to the same version (e.g., set
"@storybook/addon-essentials", "@storybook/addon-interactions", and
"@storybook/test" to "^8.6.18" to match "@storybook/addon-a11y",
"@storybook/react", and "@storybook/react-vite") so versions are consistent and
avoid peer dependency warnings; modify package.json dependencies for those
package names and run install to refresh lockfile.

In `@web/src/__tests__/App.test.tsx`:
- Around line 50-56: The test "redirects unauthenticated users to login" uses
the fragile query screen.getByText('Login'); replace it with a more specific
selector to avoid accidental matches: locate the login page by a unique role or
test id (e.g., getByRole('heading', { name: /login/i }) or
getByTestId('login-page') or a form-specific query) inside the same waitFor
block after render(<App />); update the assertion in the test to assert that
this specific element (heading/form/testid) is in the document.
- Around line 67-72: The test currently couples behavior to layout by asserting
exact counts from screen.getAllByText('Dashboard') and
screen.getAllByText('SynthOrg'); instead, scope assertions to semantic queries
or specific containers: replace the count checks with role-based lookups (e.g.,
getByRole/getAllByRole with accessible name 'Dashboard' for nav links or
headings) or use within(container) to target the sidebar/statusbar elements and
assert each element exists (toBeInTheDocument) rather than asserting lengths;
update the assertions around the Dashboard and SynthOrg checks in App.test.tsx
to use these semantic/scoped queries so layout changes won't break the test.

In `@web/src/__tests__/components/layout/Sidebar.test.tsx`:
- Around line 121-130: The test sets the logout spy after rendering, which can
cause flakiness because Sidebar captures logout via useAuthStore during render;
move the line that injects the spy (useAuthStore.setState({ logout: logoutSpy }
as never)) to before calling setup() so the component receives the spy when
rendered (keep logoutSpy = vi.fn() and the user.click/assert unchanged); also
consider resetting store state after the test if other tests depend on it.

In `@web/src/components/layout/AppLayout.tsx`:
- Around line 6-12: The PageLoadingFallback component should expose its loading
state to assistive tech: update PageLoadingFallback to include an ARIA live
region/role so screen readers announce progress (e.g., add role="status" and
aria-live="polite" to the container or include a visually hidden element with
the "Loading..." text) and optionally include an accessible spinner (decorative
SVG with aria-hidden="true") for sighted users; modify the PageLoadingFallback
function to wrap or replace the current div/span with these attributes and
ensure only non-redundant text is announced.

In `@web/src/components/layout/Sidebar.stories.tsx`:
- Around line 44-54: Remove the redundant play hook from the Collapsed story:
the decorator already sets localStorage.setItem('sidebar_collapsed', 'true')
before render, so delete the play property on the Collapsed export (the play: ()
=> { localStorage.setItem('sidebar_collapsed', 'true') }) and keep the decorator
that sets the same key; ensure only the decorators array remains on the
Collapsed Story definition.
- Around line 38-42: The Expanded story's play hook sets localStorage after
initial render which doesn't affect Sidebar's initial useState initializer;
either remove the play hook from export const Expanded or switch to the same
approach used by the Collapsed story (add a decorator that sets localStorage
before render so Sidebar reads the intended value on first render). Locate the
Expanded story (export const Expanded) and either delete its play function or
implement a decorator analogous to Collapsed that sets 'sidebar_collapsed' to
'false' before the component mounts.

In `@web/src/components/layout/Sidebar.tsx`:
- Around line 212-218: The logout button currently relies on title="Logout"
which isn't reliably announced by assistive tech; update the button (the element
using onClick={logout} and rendering <LogOut ... />) to include an accessible
label (e.g., add aria-label="Logout" or aria-label based on collapsed state) so
screen readers can announce the action when the sidebar is collapsed; keep the
existing title and visual icon intact but ensure aria-label is present on the
button element.
- Around line 160-169: In the Sidebar component add an explicit aria-label to
the Notifications button: update the <button> (the one rendering <Bell /> and
using the collapsed prop) to include aria-label="Notifications" so screen
readers get the label when collapsed (keep the visible
<span>Notifications</span> when !collapsed for sighted users).

In `@web/src/pages/OrgEditPage.tsx`:
- Around line 1-7: Extract a reusable PlaceholderPage component and replace the
duplicated centered-layout in OrgEditPage: create a new component (e.g.,
PlaceholderPage) that renders the common wrapper div and accepts children or a
title prop, then update the OrgEditPage function to return <PlaceholderPage
title="Edit Organization" /> (or pass the <h1> as children) instead of the
inline div; ensure the new component preserves the exact classes and typography
used in OrgEditPage so other route stubs can import and reuse it.

In `@web/src/router/guards.tsx`:
- Around line 37-43: Extract the duplicated loading markup used in SetupGuard
and SetupCompleteGuard into a single reusable component (e.g.,
FullScreenLoading) so both guards return <FullScreenLoading /> when
setupComplete === null or loading; add the function/component near the top of
this file or in a shared UI file, export if needed, and replace the duplicated
JSX in the SetupGuard and SetupCompleteGuard render paths to keep behavior and
styling identical.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 84ffa9e9-7d4c-4b82-a96c-db63898c1474

📥 Commits

Reviewing files that changed from the base of the PR and between 9f01d3e and 8f86f70.

⛔ Files ignored due to path filters (1)
  • web/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (45)
  • .github/workflows/ci.yml
  • .gitignore
  • CLAUDE.md
  • web/.npmrc
  • web/.storybook/main.ts
  • web/.storybook/preview.tsx
  • web/package.json
  • web/src/App.tsx
  • web/src/__tests__/App.test.tsx
  • web/src/__tests__/components/layout/Sidebar.test.tsx
  • web/src/__tests__/components/layout/SidebarNavItem.test.tsx
  • web/src/__tests__/router/guards.test.tsx
  • web/src/__tests__/stores/setup.test.ts
  • web/src/__tests__/test-utils.tsx
  • web/src/components/layout/AppLayout.tsx
  • web/src/components/layout/Sidebar.stories.tsx
  • web/src/components/layout/Sidebar.tsx
  • web/src/components/layout/SidebarNavItem.tsx
  • web/src/components/layout/StatusBar.tsx
  • web/src/components/ui/button.stories.tsx
  • web/src/pages/AgentDetailPage.tsx
  • web/src/pages/AgentsPage.tsx
  • web/src/pages/ApprovalsPage.tsx
  • web/src/pages/BudgetForecastPage.tsx
  • web/src/pages/BudgetPage.tsx
  • web/src/pages/DashboardPage.tsx
  • web/src/pages/LoginPage.tsx
  • web/src/pages/MeetingDetailPage.tsx
  • web/src/pages/MeetingsPage.tsx
  • web/src/pages/MessagesPage.tsx
  • web/src/pages/NotFoundPage.tsx
  • web/src/pages/OrgChartPage.tsx
  • web/src/pages/OrgEditPage.tsx
  • web/src/pages/ProviderDetailPage.tsx
  • web/src/pages/ProvidersPage.tsx
  • web/src/pages/SettingsNamespacePage.tsx
  • web/src/pages/SettingsPage.tsx
  • web/src/pages/SetupPage.tsx
  • web/src/pages/TaskBoardPage.tsx
  • web/src/pages/TaskDetailPage.tsx
  • web/src/router/guards.tsx
  • web/src/router/index.tsx
  • web/src/router/routes.ts
  • web/src/stores/auth.ts
  • web/src/stores/setup.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build Sandbox
  • GitHub Check: Analyze (python)
  • GitHub Check: Dependency Review
🧰 Additional context used
📓 Path-based instructions (3)
web/**

📄 CodeRabbit inference engine (CLAUDE.md)

Use Node.js 22+ for the web dashboard. Dependencies in web/package.json

Files:

  • web/src/pages/BudgetForecastPage.tsx
  • web/src/pages/AgentsPage.tsx
  • web/src/pages/ApprovalsPage.tsx
  • web/src/pages/OrgChartPage.tsx
  • web/src/pages/MeetingsPage.tsx
  • web/src/pages/AgentDetailPage.tsx
  • web/src/pages/DashboardPage.tsx
  • web/src/pages/MeetingDetailPage.tsx
  • web/src/pages/OrgEditPage.tsx
  • web/src/pages/TaskDetailPage.tsx
  • web/src/pages/MessagesPage.tsx
  • web/src/pages/NotFoundPage.tsx
  • web/package.json
  • web/src/pages/SetupPage.tsx
  • web/src/pages/ProvidersPage.tsx
  • web/src/pages/SettingsPage.tsx
  • web/src/pages/ProviderDetailPage.tsx
  • web/src/pages/SettingsNamespacePage.tsx
  • web/src/stores/auth.ts
  • web/src/pages/BudgetPage.tsx
  • web/src/pages/LoginPage.tsx
  • web/src/App.tsx
  • web/src/pages/TaskBoardPage.tsx
  • web/src/__tests__/components/layout/Sidebar.test.tsx
  • web/src/components/layout/AppLayout.tsx
  • web/src/components/layout/StatusBar.tsx
  • web/src/__tests__/components/layout/SidebarNavItem.test.tsx
  • web/src/components/layout/Sidebar.tsx
  • web/src/components/layout/Sidebar.stories.tsx
  • web/src/router/index.tsx
  • web/src/stores/setup.ts
  • web/src/__tests__/test-utils.tsx
  • web/src/components/layout/SidebarNavItem.tsx
  • web/src/__tests__/App.test.tsx
  • web/src/components/ui/button.stories.tsx
  • web/src/__tests__/stores/setup.test.ts
  • web/src/__tests__/router/guards.test.tsx
  • web/src/router/routes.ts
  • web/src/router/guards.tsx
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

web/src/**/*.{ts,tsx}: React 19 + shadcn/ui + Tailwind CSS 4 for the web dashboard
Use Zustand for state management in React
Use ESLint with @eslint-react/eslint-plugin and eslint-plugin-security for React linting
Web dashboard dependencies include Axios client, React Router, Recharts for charting, Framer Motion for animations, cmdk for command palette, Lucide React for icons, @tanstack/react-query for server state

Files:

  • web/src/pages/BudgetForecastPage.tsx
  • web/src/pages/AgentsPage.tsx
  • web/src/pages/ApprovalsPage.tsx
  • web/src/pages/OrgChartPage.tsx
  • web/src/pages/MeetingsPage.tsx
  • web/src/pages/AgentDetailPage.tsx
  • web/src/pages/DashboardPage.tsx
  • web/src/pages/MeetingDetailPage.tsx
  • web/src/pages/OrgEditPage.tsx
  • web/src/pages/TaskDetailPage.tsx
  • web/src/pages/MessagesPage.tsx
  • web/src/pages/NotFoundPage.tsx
  • web/src/pages/SetupPage.tsx
  • web/src/pages/ProvidersPage.tsx
  • web/src/pages/SettingsPage.tsx
  • web/src/pages/ProviderDetailPage.tsx
  • web/src/pages/SettingsNamespacePage.tsx
  • web/src/stores/auth.ts
  • web/src/pages/BudgetPage.tsx
  • web/src/pages/LoginPage.tsx
  • web/src/App.tsx
  • web/src/pages/TaskBoardPage.tsx
  • web/src/__tests__/components/layout/Sidebar.test.tsx
  • web/src/components/layout/AppLayout.tsx
  • web/src/components/layout/StatusBar.tsx
  • web/src/__tests__/components/layout/SidebarNavItem.test.tsx
  • web/src/components/layout/Sidebar.tsx
  • web/src/components/layout/Sidebar.stories.tsx
  • web/src/router/index.tsx
  • web/src/stores/setup.ts
  • web/src/__tests__/test-utils.tsx
  • web/src/components/layout/SidebarNavItem.tsx
  • web/src/__tests__/App.test.tsx
  • web/src/components/ui/button.stories.tsx
  • web/src/__tests__/stores/setup.test.ts
  • web/src/__tests__/router/guards.test.tsx
  • web/src/router/routes.ts
  • web/src/router/guards.tsx
web/src/__tests__/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

web/src/__tests__/**/*.{ts,tsx}: Use Vitest for React unit tests with @vitest/coverage-v8 for coverage tracking (coverage scoped to files changed vs origin/main)
Use fast-check for property-based testing in React (fc.assert + fc.property)

Files:

  • web/src/__tests__/components/layout/Sidebar.test.tsx
  • web/src/__tests__/components/layout/SidebarNavItem.test.tsx
  • web/src/__tests__/test-utils.tsx
  • web/src/__tests__/App.test.tsx
  • web/src/__tests__/stores/setup.test.ts
  • web/src/__tests__/router/guards.test.tsx
🧠 Learnings (29)
📓 Common learnings
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.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T12:00:18.113Z
Learning: Commits: <type>: <description> — types: feat, fix, refactor, docs, test, chore, perf, ci. Enforced by commitizen (commit-msg hook). Signed commits: required on main via branch protection — all commits must be GPG/SSH signed.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T19:31:05.964Z
Learning: Applies to web/src/**/*.{ts,tsx} : React 19 + shadcn/ui + Tailwind CSS 4 for the web dashboard
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T19:31:05.964Z
Learning: Applies to web/src/**/*.{ts,tsx} : Web dashboard dependencies include Axios client, React Router, Recharts for charting, Framer Motion for animations, cmdk for command palette, Lucide React for icons, tanstack/react-query for server state
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Applies to web/** : Web dashboard: Node.js 20+, dependencies in web/package.json (Vue 3, PrimeVue, Tailwind CSS, Pinia, VueFlow, ECharts, Axios, vue-draggable-plus, Vitest, fast-check, ESLint, vue-tsc).
📚 Learning: 2026-03-24T19:31:05.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T19:31:05.964Z
Learning: Applies to web/src/**/*.{ts,tsx} : React 19 + shadcn/ui + Tailwind CSS 4 for the web dashboard

Applied to files:

  • web/src/pages/BudgetForecastPage.tsx
  • .gitignore
  • web/src/pages/AgentsPage.tsx
  • web/src/pages/ApprovalsPage.tsx
  • web/src/pages/OrgChartPage.tsx
  • web/src/pages/MeetingsPage.tsx
  • web/src/pages/AgentDetailPage.tsx
  • web/.storybook/preview.tsx
  • web/src/pages/DashboardPage.tsx
  • web/src/pages/MeetingDetailPage.tsx
  • web/src/pages/TaskDetailPage.tsx
  • web/src/pages/MessagesPage.tsx
  • web/src/pages/NotFoundPage.tsx
  • web/src/pages/SetupPage.tsx
  • web/src/pages/ProvidersPage.tsx
  • web/src/pages/SettingsPage.tsx
  • web/src/pages/ProviderDetailPage.tsx
  • web/src/pages/SettingsNamespacePage.tsx
  • web/src/pages/BudgetPage.tsx
  • web/.storybook/main.ts
  • web/src/pages/LoginPage.tsx
  • web/src/App.tsx
  • web/src/pages/TaskBoardPage.tsx
  • CLAUDE.md
  • web/src/__tests__/components/layout/Sidebar.test.tsx
  • web/src/components/layout/AppLayout.tsx
  • web/src/components/layout/StatusBar.tsx
  • web/src/__tests__/components/layout/SidebarNavItem.test.tsx
  • web/src/components/layout/Sidebar.tsx
  • web/src/router/index.tsx
  • web/src/__tests__/test-utils.tsx
  • web/src/components/layout/SidebarNavItem.tsx
  • web/src/__tests__/App.test.tsx
  • web/src/router/routes.ts
  • web/src/router/guards.tsx
📚 Learning: 2026-03-15T18:17:43.675Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Applies to web/** : Web dashboard: Node.js 20+, dependencies in web/package.json (Vue 3, PrimeVue, Tailwind CSS, Pinia, VueFlow, ECharts, Axios, vue-draggable-plus, Vitest, fast-check, ESLint, vue-tsc).

Applied to files:

  • .gitignore
  • web/src/pages/DashboardPage.tsx
  • web/package.json
  • web/src/App.tsx
  • CLAUDE.md
📚 Learning: 2026-03-14T15:43:05.601Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T15:43:05.601Z
Learning: Applies to web/package.json : Web dashboard Node.js 20+; dependencies in web/package.json (Vue 3, PrimeVue, Tailwind CSS, Pinia, VueFlow, ECharts, Axios, vue-draggable-plus, Vitest, ESLint, vue-tsc)

Applied to files:

  • .gitignore
  • web/.npmrc
  • web/package.json
  • web/.storybook/main.ts
  • CLAUDE.md
📚 Learning: 2026-03-24T19:31:05.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T19:31:05.964Z
Learning: Applies to web/src/__tests__/**/*.{ts,tsx} : Use Vitest for React unit tests with `vitest/coverage-v8` for coverage tracking (coverage scoped to files changed vs origin/main)

Applied to files:

  • .gitignore
  • web/package.json
  • web/.storybook/main.ts
  • web/src/__tests__/components/layout/Sidebar.test.tsx
  • web/src/__tests__/components/layout/SidebarNavItem.test.tsx
  • web/src/__tests__/test-utils.tsx
  • web/src/__tests__/App.test.tsx
  • web/src/__tests__/stores/setup.test.ts
  • web/src/__tests__/router/guards.test.tsx
📚 Learning: 2026-03-24T19:31:05.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T19:31:05.964Z
Learning: Applies to web/src/**/*.{ts,tsx} : Web dashboard dependencies include Axios client, React Router, Recharts for charting, Framer Motion for animations, cmdk for command palette, Lucide React for icons, tanstack/react-query for server state

Applied to files:

  • .gitignore
  • web/src/pages/ApprovalsPage.tsx
  • web/src/pages/DashboardPage.tsx
  • web/package.json
  • web/src/App.tsx
  • web/src/pages/TaskBoardPage.tsx
  • CLAUDE.md
  • web/src/components/layout/AppLayout.tsx
  • web/src/components/layout/StatusBar.tsx
  • web/src/components/layout/Sidebar.tsx
  • web/src/router/index.tsx
  • web/src/__tests__/test-utils.tsx
  • web/src/__tests__/App.test.tsx
  • web/src/router/routes.ts
  • web/src/router/guards.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:

  • .gitignore
  • web/package.json
  • CLAUDE.md
  • web/src/__tests__/components/layout/Sidebar.test.tsx
  • web/src/__tests__/components/layout/SidebarNavItem.test.tsx
  • web/src/__tests__/test-utils.tsx
  • web/src/__tests__/App.test.tsx
  • web/src/__tests__/stores/setup.test.ts
  • web/src/__tests__/router/guards.test.tsx
📚 Learning: 2026-03-24T19:31:05.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T19:31:05.964Z
Learning: Applies to web/** : Use Node.js 22+ for the web dashboard. Dependencies in `web/package.json`

Applied to files:

  • .gitignore
  • web/.npmrc
📚 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:

  • .gitignore
  • CLAUDE.md
📚 Learning: 2026-03-24T19:31:05.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T19:31:05.964Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use ESLint with `eslint-react/eslint-plugin` and `eslint-plugin-security` for React linting

Applied to files:

  • web/package.json
📚 Learning: 2026-03-24T19:31:05.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T19:31:05.964Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use Zustand for state management in React

Applied to files:

  • web/src/stores/auth.ts
  • CLAUDE.md
  • web/src/router/index.tsx
  • web/src/stores/setup.ts
  • web/src/__tests__/test-utils.tsx
📚 Learning: 2026-03-15T18:17:43.675Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Applies to .github/workflows/**/*.yml : Path filtering: dorny/paths-filter detects Python/dashboard/docker changes; jobs only run when their domain is affected. CLI has its own workflow (cli.yml).

Applied to files:

  • .github/workflows/ci.yml
  • CLAUDE.md
📚 Learning: 2026-03-24T19:31:05.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T19:31:05.964Z
Learning: CI path filtering via `dorny/paths-filter`—jobs only run when their domain is affected. CLI has its own workflow (`cli.yml`).

Applied to files:

  • .github/workflows/ci.yml
  • 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 .github/workflows/*.yml : Dependabot: daily updates for uv + github-actions + npm + pre-commit + docker + gomod, grouped minor/patch, no auto-merge. Use `/review-dep-pr` to review Dependabot PRs before merging.

Applied to files:

  • .github/workflows/ci.yml
  • 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 .github/workflows/docker.yml : Docker workflow: builds backend + web + sandbox images, pushes to GHCR, signs with cosign. SLSA L3 provenance attestations via actions/attest-build-provenance. Scans: Trivy (CRITICAL = hard fail, HIGH = warn) + Grype (critical cutoff) + CIS Docker Benchmark v1.6.0 compliance (informational). CVE triage via .github/.trivyignore.yaml and .github/.grype.yaml. Images only pushed after scans pass. Triggers on push to main and version tags (v*).

Applied to files:

  • .github/workflows/ci.yml
  • CLAUDE.md
📚 Learning: 2026-03-15T12:00:18.113Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T12:00:18.113Z
Learning: Applies to .github/workflows/docker.yml : CI Docker: build → scan → push to GHCR + cosign sign + SLSA L3 provenance via attest-build-provenance (images only pushed after Trivy/Grype scans pass).

Applied to files:

  • .github/workflows/ci.yml
  • 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 .github/workflows/cli.yml : CLI workflow: Go lint (golangci-lint + go vet) + test (-race -coverprofile) + build (cross-compile: linux/darwin/windows × amd64/arm64) + govulncheck + fuzz testing (main-only, 30s/target, continue-on-error, matrix over 4 packages). cli-pass gate includes fuzz as informational. GoReleaser release on v* tags. Cosign keyless signing of checksums.txt. SLSA L3 provenance attestations. Sigstore bundle (.sigstore.json) attached. Post-release appends checksums/verification/provenance to draft release notes.

Applied to files:

  • .github/workflows/ci.yml
  • 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: CLI workflow (`.github/workflows/cli.yml`) runs Go lint (golangci-lint + go vet) + test (race, coverage) + build (cross-compile matrix) + vulnerability check (govulncheck) + fuzz testing. Cross-compiles for linux/darwin/windows × amd64/arm64. GoReleaser release on v* tags with cosign keyless signing and SLSA L3 attestations.

Applied to files:

  • .github/workflows/ci.yml
  • CLAUDE.md
📚 Learning: 2026-03-16T19:52:03.656Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T19:52:03.656Z
Learning: Applies to cli/**/*.go : Lint CLI Go code with golangci-lint and go vet; test with go test -race; check vulnerabilities with govulncheck

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-19T11:30:29.217Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:30:29.217Z
Learning: Applies to cli/**/*.go : Run Go lint via `golangci-lint run`, vet via `go vet`, tests via `go test ./...`, and fuzz via `go test -fuzz=FuzzTarget -fuzztime=30s`

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 cli/**/*.go : Lint Go code with `golangci-lint` and `go vet`. Run tests with `-race` flag to detect race conditions.

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-24T19:31:05.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T19:31:05.964Z
Learning: Applies to cli/**/*.go : Use Cobra for CLI commands, charmbracelet/huh for interactive prompts, charmbracelet/lipgloss for UI styling, sigstore-go for binary verification, go-containerregistry for container image handling, go-tuf for TUF updates

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-24T19:31:05.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T19:31:05.964Z
Learning: Docker + CLI workflows: build + Trivy/Grype scan + push to GHCR + cosign sign + SLSA L3 provenance. CLI also includes govulncheck + fuzz via `testing.F`.

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-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-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: Pre-push hooks: mypy type-check + pytest unit tests + golangci-lint + go vet + go test (CLI, conditional on cli/**/*.go) (fast gate before push, skipped in pre-commit.ci — dedicated CI jobs already run these).

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-24T19:31:05.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T19:31:05.964Z
Learning: Security scanning: gitleaks (push/PR + weekly), zizmor (workflow analysis), OSSF Scorecard (weekly), Socket.dev (PR supply chain), ZAP DAST (weekly + manual, rules: `.github/zap-rules.tsv`)

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-24T19:31:05.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T19:31:05.964Z
Learning: Applies to web/src/__tests__/**/*.{ts,tsx} : Use fast-check for property-based testing in React (`fc.assert` + `fc.property`)

Applied to files:

  • web/src/__tests__/components/layout/Sidebar.test.tsx
  • web/src/__tests__/components/layout/SidebarNavItem.test.tsx
  • web/src/__tests__/test-utils.tsx
  • web/src/__tests__/App.test.tsx
  • web/src/__tests__/stores/setup.test.ts
  • web/src/__tests__/router/guards.test.tsx
🧬 Code graph analysis (13)
web/src/pages/NotFoundPage.tsx (1)
web/src/components/ui/button.stories.tsx (1)
  • Link (43-45)
web/src/App.tsx (1)
web/src/router/index.tsx (1)
  • AppRouter (115-117)
web/src/__tests__/components/layout/Sidebar.test.tsx (3)
web/src/stores/auth.ts (2)
  • useAuthStore (61-204)
  • setup (150-152)
web/src/__tests__/test-utils.tsx (1)
  • renderWithRouter (17-26)
web/src/components/layout/Sidebar.tsx (1)
  • Sidebar (27-226)
web/src/components/layout/AppLayout.tsx (3)
web/src/components/layout/StatusBar.tsx (1)
  • StatusBar (3-43)
web/src/components/layout/Sidebar.tsx (1)
  • Sidebar (27-226)
scripts/export_openapi.py (1)
  • main (90-118)
web/src/__tests__/components/layout/SidebarNavItem.test.tsx (2)
web/src/__tests__/test-utils.tsx (1)
  • renderWithRouter (17-26)
web/src/components/layout/SidebarNavItem.tsx (1)
  • SidebarNavItem (15-57)
web/src/components/layout/Sidebar.tsx (4)
web/src/hooks/useAuth.ts (1)
  • useAuth (10-31)
web/src/stores/auth.ts (2)
  • logout (200-202)
  • useAuthStore (61-204)
web/src/components/layout/SidebarNavItem.tsx (1)
  • SidebarNavItem (15-57)
web/src/router/routes.ts (1)
  • ROUTES (7-28)
web/src/components/layout/Sidebar.stories.tsx (2)
web/src/components/layout/Sidebar.tsx (1)
  • Sidebar (27-226)
web/src/stores/auth.ts (1)
  • useAuthStore (61-204)
web/src/router/index.tsx (2)
web/src/components/layout/AppLayout.tsx (1)
  • AppLayout (14-28)
web/src/router/guards.tsx (4)
  • GuestGuard (55-63)
  • SetupCompleteGuard (69-100)
  • AuthGuard (11-19)
  • SetupGuard (26-50)
web/src/stores/setup.ts (1)
web/src/api/endpoints/setup.ts (1)
  • getSetupStatus (19-22)
web/src/__tests__/App.test.tsx (3)
web/src/stores/auth.ts (1)
  • useAuthStore (61-204)
web/src/stores/setup.ts (1)
  • useSetupStore (11-27)
web/src/App.tsx (1)
  • App (3-5)
web/src/__tests__/stores/setup.test.ts (2)
web/src/stores/setup.ts (1)
  • useSetupStore (11-27)
web/src/api/endpoints/setup.ts (1)
  • getSetupStatus (19-22)
web/src/__tests__/router/guards.test.tsx (4)
web/src/stores/setup.ts (1)
  • useSetupStore (11-27)
web/src/__tests__/test-utils.tsx (1)
  • renderRoutes (31-37)
web/src/router/guards.tsx (4)
  • AuthGuard (11-19)
  • SetupGuard (26-50)
  • GuestGuard (55-63)
  • SetupCompleteGuard (69-100)
web/src/api/endpoints/setup.ts (1)
  • getSetupStatus (19-22)
web/src/router/guards.tsx (3)
web/src/stores/auth.ts (1)
  • useIsAuthenticated (208-208)
web/src/router/routes.ts (1)
  • ROUTES (7-28)
web/src/stores/setup.ts (2)
  • useSetupStore (11-27)
  • fetchSetupStatus (15-26)
🔇 Additional comments (54)
web/src/pages/TaskBoardPage.tsx (1)

1-7: LGTM!

Clean placeholder page implementation. The centered layout and custom text-surface-500 token are consistent with the other route stubs in this PR.

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

1-7: LGTM — clean placeholder page stub.

Structure and styling are consistent with the app-shell scaffold pattern.

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

1-7: LGTM — page stub is correct and consistent.

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

1-7: LGTM — straightforward placeholder implementation.

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

1-7: LGTM — looks good for a route placeholder page.

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

1-7: LGTM — implementation is consistent and correct.

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

1-7: LGTM — valid and consistent page stub.

web/.npmrc (1)

1-1: 🧹 Nitpick | 🔵 Trivial

Remove legacy-peer-deps=true since strict peer resolution is now viable.

Strict peer dependency resolution works without conflicts. This setting should be removed to enforce better lockfile hygiene and catch incompatibilities early.

⛔ Skipped due to learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T19:31:05.964Z
Learning: Applies to web/** : Use Node.js 22+ for the web dashboard. Dependencies in `web/package.json`
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).
web/src/stores/auth.ts (2)

139-143: LGTM!

The comment update clearly documents the intentional use of window.location.href for redirects from the Zustand store. This is the correct approach since the store operates outside the React component tree where React Router's navigation hooks aren't available.


206-215: LGTM!

The selector hooks follow Zustand best practices with inline selectors, preventing unnecessary re-renders. The useMustChangePassword fallback logic properly handles the page refresh scenario.

CLAUDE.md (3)

56-57: LGTM!

The Storybook commands are correctly documented with the appropriate port and build script names.


124-134: LGTM!

The package structure documentation accurately reflects the new router-driven architecture with pages/ for lazy-loaded components, router/ for configuration and guards, and the updated components/ description including the layout directory.


237-237: LGTM!

The CI jobs description correctly includes the new storybook-build step in the dashboard job pipeline.

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

11-27: LGTM!

The store implementation is clean and follows Zustand patterns correctly:

  • Guards against concurrent fetches with the loading check
  • Correctly maps the API response (!status.needs_setup)
  • Intentionally leaves setupComplete as null on error to allow retry on next navigation
web/src/components/layout/SidebarNavItem.tsx (1)

15-56: LGTM!

Well-structured component with good practices:

  • Proper use of NavLink's className function for active state styling
  • Accessibility handled correctly (aria-hidden on decorative icon, title for collapsed tooltip)
  • Badge clamping logic (99+) is appropriate for UI display
  • The end prop is correctly forwarded to NavLink for exact route matching
.gitignore (1)

54-54: LGTM!

Correctly adds the Storybook build output directory to .gitignore, preventing build artifacts from being committed.

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

1-7: LGTM!

Appropriate stub implementation for the setup wizard page. The use of h-screen (instead of h-full used in app-shell pages) is correct since this page renders outside the main layout.

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

1-7: LGTM!

Consistent stub implementation following the same pattern as other authenticated route pages with h-full for proper layout integration within the app shell.

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

1-7: LGTM!

Clean stub implementation with default export enabling lazy loading via React.lazy(). Consistent styling pattern with other authenticated route pages.

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

1-7: Looks good for a placeholder route page.

Line 1-7 is clean and correct for an initial stub component.

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

1-7: Stub implementation is correct.

Line 1-7 is valid and consistent with the app-shell page scaffold.

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

1-7: No concerns on this page stub.

Line 1-7 is straightforward and implementation is correct.

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

1-7: Implementation is good.

Line 1-7 is valid and appropriate for the current scaffold phase.

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

1-7: LGTM for this route placeholder.

Line 1-7 is correct and consistent with other stub pages.

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

1-7: No changes requested here.

Line 1-7 is clean and correctly implemented.

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

1-7: Looks correct as-is.

Line 1-7 properly implements a simple placeholder page.

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

1-7: Good implementation for the public login stub.

Line 1-7 is valid and aligns with the route scaffold.

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

1-14: LGTM!

Clean implementation of the 404 page. The asChild pattern with Button wrapping Link is the correct approach for shadcn/ui + React Router composition, and the Tailwind classes properly use the project's custom surface color tokens.

web/.storybook/preview.tsx (1)

4-18: LGTM!

The Storybook preview configuration correctly sets up dark theme with Tailwind integration. The decorator applying the dark class enables Tailwind's dark mode variants, and importing the global CSS ensures all design tokens are available in stories.

web/src/App.tsx (1)

1-5: LGTM!

Clean and minimal entry point that properly delegates routing to the dedicated router module. This separation of concerns makes the app structure easier to maintain and test.

web/.storybook/main.ts (1)

1-18: LGTM!

The Storybook configuration correctly integrates with Vite and Tailwind CSS 4. The viteFinal hook properly adds the Tailwind plugin while preserving existing plugins, and the addon selection (essentials, a11y, interactions) provides good development and testing capabilities.

.github/workflows/ci.yml (1)

309-330: LGTM!

The Storybook build job follows the established patterns for dashboard CI jobs. Good choices include using --disable-telemetry to avoid unnecessary network calls in CI and properly integrating with the path filter and CI pass gate.

web/src/__tests__/components/layout/SidebarNavItem.test.tsx (1)

1-55: LGTM!

Comprehensive test coverage for SidebarNavItem including collapsed/expanded states, badge rendering logic, and the 99+ cap. The tests use appropriate Testing Library queries and the renderWithRouter helper correctly handles the NavLink routing context.

web/src/components/layout/AppLayout.tsx (1)

14-28: LGTM!

Well-structured layout component with proper Suspense boundary placement for lazy-loaded routes. The flexbox layout with controlled overflow ensures the sidebar remains fixed while the main content scrolls independently.

web/src/__tests__/router/guards.test.tsx (4)

1-39: LGTM! Well-structured test setup with proper isolation.

The test file has good patterns:

  • API mock at module level for getSetupStatus
  • window.location override to prevent side effects from auth store's clearAuth() redirect logic
  • resetStores() helper properly resets both Zustand stores and clears localStorage

41-82: LGTM! AuthGuard test coverage is complete.

Both key scenarios are tested:

  • Unauthenticated users redirect to /login
  • Authenticated users see protected content

84-181: LGTM! Comprehensive SetupGuard coverage.

Good async testing pattern using dynamic import with vi.mocked() and waitFor for the fetch trigger test.


183-336: LGTM! GuestGuard and SetupCompleteGuard tests are thorough.

All guard edge cases are covered including the SetupCompleteGuard's complex logic for unauthenticated passthrough vs authenticated+setup-complete redirect.

web/src/__tests__/components/layout/Sidebar.test.tsx (1)

1-46: LGTM! Clean test setup with proper auth state initialization.

Good patterns:

  • window.location override for side-effect isolation
  • Separate resetStore() and setup() helpers for clarity
  • User mock object matches the auth store's user type
web/src/components/layout/StatusBar.tsx (1)

1-62: LGTM! Clean StatusBar skeleton implementation.

Good patterns:

  • Placeholder values (--) for data that will be populated later
  • Proper use of aria-hidden="true" on decorative Dot elements
  • Helper components (Divider, Dot, StatusItem) keep the main component readable
  • Tailwind classes follow the project's design tokens (surface-*, brand-*, success-*, warning-*)
web/src/__tests__/stores/setup.test.ts (1)

1-105: LGTM! Comprehensive setup store test coverage.

Excellent patterns:

  • Tests initial state, both success paths (needs_setup: true/false), concurrent fetch prevention, and error handling
  • The concurrent fetch test (Lines 65-93) uses a controlled Promise to verify the loading guard prevents duplicate requests
  • Error handling test confirms setupComplete stays null on failure (allowing retry)
web/src/__tests__/test-utils.tsx (2)

17-37: LGTM! Useful test utilities for router-dependent components.

Two well-designed helpers:

  • renderWithRouter: for testing components that need router context but don't care about specific routes
  • renderRoutes: for testing route configurations and guard behavior with full route trees

6-9: 🧹 Nitpick | 🔵 Trivial

Unused routes property in interface.

The routes property in RenderWithRouterOptions is defined but never used by either renderWithRouter or renderRoutes. Consider removing it or documenting its intended use.

♻️ Remove unused property
 interface RenderWithRouterOptions {
   initialEntries?: string[]
-  routes?: RouteObject[]
 }
			> Likely an incorrect or invalid review comment.
web/src/router/index.tsx (2)

1-40: LGTM! Well-structured lazy loading with consistent fallback.

Good patterns:

  • All 20+ page components are lazy-loaded for optimal code splitting
  • SuspenseWrapper provides a uniform loading experience
  • Lazy-loading AppLayout itself ensures the shell doesn't block initial page load

42-117: LGTM! Router configuration is well-organized.

Guard composition is correct:

  • GuestGuard on /login redirects authenticated users away
  • SetupCompleteGuard on /setup routes prevents access after setup is done
  • AuthGuardSetupGuardAppLayout chain ensures authenticated users with completed setup see the app shell

Child routes under AppLayout don't need individual SuspenseWrapper since AppLayout already wraps its <Outlet /> in <Suspense> (per AppLayout.tsx Lines 20-22).

web/src/router/routes.ts (1)

1-31: LGTM! Clean centralized route definitions.

Good patterns:

  • as const provides type-safe literal types for route paths
  • Single source of truth for URL patterns used across guards, router, and navigation
  • Parameterized routes (:taskId, :agentName, etc.) are clearly named
web/src/components/ui/button.stories.tsx (1)

1-70: Well-structured Storybook stories with good coverage.

The stories file follows CSF 3 format correctly, includes argTypes for interactive controls, and provides comprehensive coverage of Button variants and sizes. The AllVariants story is a nice addition for visual comparison.

web/src/components/layout/Sidebar.tsx (2)

27-40: Good implementation of persisted collapse state.

The pattern of initializing state from localStorage via a lazy initializer and syncing on toggle is correct. This ensures the sidebar remembers user preference across sessions.


42-135: Clean implementation of sidebar navigation structure.

The component is well-organized with clear sections (header, primary nav, workspace nav), proper conditional rendering based on collapsed state, and good use of the cn() utility for class merging. The route constants from ROUTES ensure consistency.

web/src/__tests__/App.test.tsx (1)

6-48: Solid test setup with proper isolation.

The test configuration correctly:

  • Mocks the setup API to control guard behavior
  • Patches window.location to prevent side effects from the auth store's hard redirect
  • Resets Zustand stores and clears localStorage before each test

This ensures reliable, isolated test execution.

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

11-19: AuthGuard implementation is clean and correct.

The guard correctly checks authentication state and redirects unauthenticated users to login. Using replace prevents the login page from appearing in history when navigating back.


26-50: SetupGuard correctly handles async setup status check.

The guard properly:

  • Triggers fetch only when needed (setupComplete === null && !loading)
  • Shows loading state while fetching
  • Redirects to setup if incomplete

The retry-on-error behavior (leaving setupComplete as null) aligns with the store's design.


55-63: GuestGuard correctly prevents authenticated users from accessing login.

Simple and effective implementation that redirects logged-in users to the dashboard.


69-100: SetupCompleteGuard correctly handles the setup access control.

The guard appropriately:

  • Allows unauthenticated access (first-time setup flow)
  • Fetches setup status only for authenticated users
  • Redirects to dashboard if setup is already complete

This prevents authenticated users from re-running setup while allowing the initial admin creation flow.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

♻️ Duplicate comments (1)
web/src/components/layout/Sidebar.tsx (1)

142-155: ⚠️ Potential issue | 🟡 Minor

Add an accessible name to the collapse toggle.

When the sidebar is collapsed, this control is icon-only and title is not a reliable accessible name. Please add an explicit label here so assistive tech can still announce the action.

♿ Minimal fix
           <button
             onClick={toggleCollapse}
             title={collapsed ? 'Expand sidebar' : 'Collapse sidebar'}
+            aria-label={collapsed ? 'Expand sidebar' : 'Collapse sidebar'}
+            aria-expanded={!collapsed}
             className="flex items-center gap-3 rounded-md px-3 py-2 text-sm text-surface-500 transition-colors hover:bg-surface-100 hover:text-surface-700"
           >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/layout/Sidebar.tsx` around lines 142 - 155, The collapse
toggle button currently relies on title and becomes icon-only when collapsed, so
add an explicit accessible name by adding an aria-label (or aria-labelledby)
that reflects the current state using the existing collapsed variable; update
the button where toggleCollapse is used to include aria-label={collapsed ?
'Expand sidebar' : 'Collapse sidebar'} (keeping the visible text when not
collapsed and icons PanelLeftOpen/PanelLeftClose unchanged) so assistive tech
can always announce the action.
🤖 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__/App.test.tsx`:
- Around line 42-45: The test resets useSetupStore state but omits the error
flag—update the call to useSetupStore.setState used in the test (the block that
sets setupComplete and loading) to also reset the error property (e.g., set
error: null or false as appropriate) so the store is fully cleared between tests
and prevents leaking setup-guard failure state into subsequent App renders.

In `@web/src/components/layout/SidebarNavItem.tsx`:
- Around line 42-46: The JSX expression rendering the badge in SidebarNavItem
uses a single long className string for the <span> (referenced by the badge prop
check and the span that shows {badge > 99 ? '99+' : badge}), which causes the
132-character line; split the className into multiple concatenated literals or
use template/string joining across multiple lines (or place each Tailwind
utility on its own line within the JSX attribute) so the line length is <=88
characters while preserving the same classes and conditional rendering logic.

In `@web/src/components/layout/StatusBar.tsx`:
- Around line 6-8: Replace the hard-coded vendor string in the StatusBar
component: locate the span containing "SynthOrg" in StatusBar.tsx and change it
to a generic placeholder such as "example-provider" (or another approved generic
identifier like "example-vendor") so UI copy does not contain real vendor names;
update any tests or snapshots referencing the "SynthOrg" literal to the new
placeholder as well.
- Line 5: The long className string in the StatusBar component's div exceeds the
88-char limit; break the class list into multiple concatenated strings or use
the project's classnames helper (e.g., cn) when setting className on the div in
StatusBar.tsx so the line length is under 88 characters while preserving the
same classes and order.

In `@web/src/router/guards.tsx`:
- Around line 42-55: The guard currently mounts protected children as soon as
useIsAuthenticated() is truthy (restored token) while fetchUser() runs
asynchronously, causing flashes and unhandled rejections; change the guard to
track a validation state (e.g., isValidating) and only render <Outlet /> after
fetchUser() completes successfully, showing a loading UI while pending and
handling errors by calling clearAuth() or navigating to ROUTES.LOGIN; ensure the
useEffect that calls fetchUser() around useEffect, isAuthenticated, user,
fetchUser catches/awaits the returned promise to avoid unhandled rejections and
toggles isValidating appropriately so Outlet is not rendered until validation
resolves.

In `@web/src/router/routes.ts`:
- Around line 30-31: PUBLIC_ROUTES currently lists ROUTES.LOGIN and ROUTES.SETUP
but omits ROUTES.SETUP_STEP, causing multi-step setup routes to be treated as
protected; update PUBLIC_ROUTES to include ROUTES.SETUP_STEP (or replace the
static array with a pattern check that accepts the setup step path) so
authentication bypass logic treats both ROUTES.SETUP and ROUTES.SETUP_STEP
consistently; locate and modify the exported PUBLIC_ROUTES constant in routes.ts
and ensure any auth-checking code that consumes PUBLIC_ROUTES continues to work
with the added value or new pattern.

In `@web/src/stores/setup.ts`:
- Around line 24-27: The catch block currently calls set({ loading: false,
error: true }) but leaves the previous setupComplete value intact, which can
cause stale access decisions; update the error path in the fetch/refresh handler
to explicitly reset setupComplete to null (e.g., set({ loading: false, error:
true, setupComplete: null })) so the guard sees an unknown state and shows
retry/error flow; locate the catch that calls set(...) in
web/src/stores/setup.ts and add setupComplete: null to that set call.

---

Duplicate comments:
In `@web/src/components/layout/Sidebar.tsx`:
- Around line 142-155: The collapse toggle button currently relies on title and
becomes icon-only when collapsed, so add an explicit accessible name by adding
an aria-label (or aria-labelledby) that reflects the current state using the
existing collapsed variable; update the button where toggleCollapse is used to
include aria-label={collapsed ? 'Expand sidebar' : 'Collapse sidebar'} (keeping
the visible text when not collapsed and icons PanelLeftOpen/PanelLeftClose
unchanged) so assistive tech can always announce the action.
🪄 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: 6387875e-0f30-419c-b235-9f08e1fa0322

📥 Commits

Reviewing files that changed from the base of the PR and between 8f86f70 and 4dba2f5.

⛔ Files ignored due to path filters (1)
  • web/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (46)
  • .github/workflows/ci.yml
  • .gitignore
  • CLAUDE.md
  • web/.npmrc
  • web/.storybook/main.ts
  • web/.storybook/preview.tsx
  • web/package.json
  • web/src/App.tsx
  • web/src/__tests__/App.test.tsx
  • web/src/__tests__/components/layout/Sidebar.test.tsx
  • web/src/__tests__/components/layout/SidebarNavItem.test.tsx
  • web/src/__tests__/router/guards.test.tsx
  • web/src/__tests__/stores/setup.test.ts
  • web/src/__tests__/test-utils.tsx
  • web/src/components/layout/AppLayout.tsx
  • web/src/components/layout/PlaceholderPage.tsx
  • web/src/components/layout/Sidebar.stories.tsx
  • web/src/components/layout/Sidebar.tsx
  • web/src/components/layout/SidebarNavItem.tsx
  • web/src/components/layout/StatusBar.tsx
  • web/src/components/ui/button.stories.tsx
  • web/src/pages/AgentDetailPage.tsx
  • web/src/pages/AgentsPage.tsx
  • web/src/pages/ApprovalsPage.tsx
  • web/src/pages/BudgetForecastPage.tsx
  • web/src/pages/BudgetPage.tsx
  • web/src/pages/DashboardPage.tsx
  • web/src/pages/LoginPage.tsx
  • web/src/pages/MeetingDetailPage.tsx
  • web/src/pages/MeetingsPage.tsx
  • web/src/pages/MessagesPage.tsx
  • web/src/pages/NotFoundPage.tsx
  • web/src/pages/OrgChartPage.tsx
  • web/src/pages/OrgEditPage.tsx
  • web/src/pages/ProviderDetailPage.tsx
  • web/src/pages/ProvidersPage.tsx
  • web/src/pages/SettingsNamespacePage.tsx
  • web/src/pages/SettingsPage.tsx
  • web/src/pages/SetupPage.tsx
  • web/src/pages/TaskBoardPage.tsx
  • web/src/pages/TaskDetailPage.tsx
  • web/src/router/guards.tsx
  • web/src/router/index.tsx
  • web/src/router/routes.ts
  • web/src/stores/auth.ts
  • web/src/stores/setup.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build Backend
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (3)
web/src/**/*.{tsx,ts}

📄 CodeRabbit inference engine (CLAUDE.md)

web/src/**/*.{tsx,ts}: React components must use TypeScript and follow shadcn/ui + Tailwind CSS conventions. Line length: 88 characters.
React code must pass ESLint (configured with @eslint-react/eslint-plugin) and TypeScript type-check
Never use real vendor names in React code, tests, or config examples. Use generic names: example-provider, example-large-001, etc.

Files:

  • web/src/pages/TaskDetailPage.tsx
  • web/src/pages/SettingsPage.tsx
  • web/src/pages/MeetingDetailPage.tsx
  • web/src/pages/ProvidersPage.tsx
  • web/src/components/layout/PlaceholderPage.tsx
  • web/src/pages/OrgEditPage.tsx
  • web/src/pages/BudgetForecastPage.tsx
  • web/src/pages/AgentDetailPage.tsx
  • web/src/pages/ProviderDetailPage.tsx
  • web/src/pages/BudgetPage.tsx
  • web/src/pages/DashboardPage.tsx
  • web/src/pages/OrgChartPage.tsx
  • web/src/pages/AgentsPage.tsx
  • web/src/pages/SetupPage.tsx
  • web/src/pages/ApprovalsPage.tsx
  • web/src/pages/LoginPage.tsx
  • web/src/pages/NotFoundPage.tsx
  • web/src/pages/SettingsNamespacePage.tsx
  • web/src/pages/TaskBoardPage.tsx
  • web/src/components/layout/AppLayout.tsx
  • web/src/__tests__/components/layout/SidebarNavItem.test.tsx
  • web/src/components/layout/Sidebar.stories.tsx
  • web/src/router/index.tsx
  • web/src/components/layout/StatusBar.tsx
  • web/src/stores/setup.ts
  • web/src/components/layout/SidebarNavItem.tsx
  • web/src/__tests__/test-utils.tsx
  • web/src/__tests__/stores/setup.test.ts
  • web/src/__tests__/App.test.tsx
  • web/src/pages/MessagesPage.tsx
  • web/src/__tests__/components/layout/Sidebar.test.tsx
  • web/src/__tests__/router/guards.test.tsx
  • web/src/App.tsx
  • web/src/router/routes.ts
  • web/src/components/layout/Sidebar.tsx
  • web/src/router/guards.tsx
  • web/src/pages/MeetingsPage.tsx
  • web/src/components/ui/button.stories.tsx
  • web/src/stores/auth.ts
**/*.json

📄 CodeRabbit inference engine (CLAUDE.md)

JSON files must be valid and checked by pre-commit hooks

Files:

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

📄 CodeRabbit inference engine (CLAUDE.md)

web/src/__tests__/**/*.test.{tsx,ts}: React unit tests must use Vitest with @testing-library/react. Coverage is scoped to files changed vs origin/main.
React property-based testing must use fast-check (fc.assert + fc.property)

Files:

  • web/src/__tests__/components/layout/SidebarNavItem.test.tsx
  • web/src/__tests__/stores/setup.test.ts
  • web/src/__tests__/App.test.tsx
  • web/src/__tests__/components/layout/Sidebar.test.tsx
  • web/src/__tests__/router/guards.test.tsx
🧠 Learnings (31)
📓 Common learnings
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.
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).
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)
📚 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/.npmrc
  • .gitignore
  • .github/workflows/ci.yml
  • web/package.json
  • 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:

  • web/.npmrc
  • .gitignore
  • .github/workflows/ci.yml
  • web/package.json
  • CLAUDE.md
  • web/src/App.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:

  • .gitignore
  • web/package.json
  • CLAUDE.md
  • web/src/__tests__/components/layout/SidebarNavItem.test.tsx
  • web/src/__tests__/test-utils.tsx
  • web/src/__tests__/stores/setup.test.ts
  • web/src/__tests__/App.test.tsx
  • web/src/__tests__/components/layout/Sidebar.test.tsx
  • web/src/__tests__/router/guards.test.tsx
📚 Learning: 2026-03-24T20:58:10.766Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T20:58:10.766Z
Learning: Applies to web/src/__tests__/**/*.test.{tsx,ts} : React unit tests must use Vitest with `testing-library/react`. Coverage is scoped to files changed vs origin/main.

Applied to files:

  • .gitignore
  • web/.storybook/main.ts
  • web/package.json
  • web/src/__tests__/components/layout/SidebarNavItem.test.tsx
  • web/src/__tests__/test-utils.tsx
  • web/src/__tests__/stores/setup.test.ts
  • web/src/__tests__/App.test.tsx
  • web/src/__tests__/components/layout/Sidebar.test.tsx
  • web/src/__tests__/router/guards.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:

  • .gitignore
  • CLAUDE.md
📚 Learning: 2026-03-24T20:58:10.766Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T20:58:10.766Z
Learning: Applies to web/src/**/*.{tsx,ts} : Never use real vendor names in React code, tests, or config examples. Use generic names: `example-provider`, `example-large-001`, etc.

Applied to files:

  • web/src/pages/ProvidersPage.tsx
  • web/src/components/layout/PlaceholderPage.tsx
  • web/src/pages/ProviderDetailPage.tsx
  • web/src/__tests__/test-utils.tsx
  • web/src/__tests__/App.test.tsx
📚 Learning: 2026-03-24T20:58:10.766Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T20:58:10.766Z
Learning: Applies to web/src/**/*.{tsx,ts} : React components must use TypeScript and follow shadcn/ui + Tailwind CSS conventions. Line length: 88 characters.

Applied to files:

  • web/src/pages/SetupPage.tsx
  • web/src/pages/ApprovalsPage.tsx
  • web/src/components/layout/AppLayout.tsx
  • CLAUDE.md
  • web/src/__tests__/App.test.tsx
  • web/src/components/layout/Sidebar.tsx
📚 Learning: 2026-03-15T18:17:43.675Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Applies to .github/workflows/**/*.yml : Path filtering: dorny/paths-filter detects Python/dashboard/docker changes; jobs only run when their domain is affected. CLI has its own workflow (cli.yml).

Applied to files:

  • .github/workflows/ci.yml
  • 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 .github/workflows/*.yml : Dependabot: daily updates for uv + github-actions + npm + pre-commit + docker + gomod, grouped minor/patch, no auto-merge. Use `/review-dep-pr` to review Dependabot PRs before merging.

Applied to files:

  • .github/workflows/ci.yml
  • 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 .github/workflows/docker.yml : Docker workflow: builds backend + web + sandbox images, pushes to GHCR, signs with cosign. SLSA L3 provenance attestations via actions/attest-build-provenance. Scans: Trivy (CRITICAL = hard fail, HIGH = warn) + Grype (critical cutoff) + CIS Docker Benchmark v1.6.0 compliance (informational). CVE triage via .github/.trivyignore.yaml and .github/.grype.yaml. Images only pushed after scans pass. Triggers on push to main and version tags (v*).

Applied to files:

  • .github/workflows/ci.yml
  • CLAUDE.md
📚 Learning: 2026-03-15T12:00:18.113Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T12:00:18.113Z
Learning: Applies to .github/workflows/docker.yml : CI Docker: build → scan → push to GHCR + cosign sign + SLSA L3 provenance via attest-build-provenance (images only pushed after Trivy/Grype scans pass).

Applied to files:

  • .github/workflows/ci.yml
  • 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 .github/workflows/cli.yml : CLI workflow: Go lint (golangci-lint + go vet) + test (-race -coverprofile) + build (cross-compile: linux/darwin/windows × amd64/arm64) + govulncheck + fuzz testing (main-only, 30s/target, continue-on-error, matrix over 4 packages). cli-pass gate includes fuzz as informational. GoReleaser release on v* tags. Cosign keyless signing of checksums.txt. SLSA L3 provenance attestations. Sigstore bundle (.sigstore.json) attached. Post-release appends checksums/verification/provenance to draft release notes.

Applied to files:

  • .github/workflows/ci.yml
  • 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: CLI workflow (`.github/workflows/cli.yml`) runs Go lint (golangci-lint + go vet) + test (race, coverage) + build (cross-compile matrix) + vulnerability check (govulncheck) + fuzz testing. Cross-compiles for linux/darwin/windows × amd64/arm64. GoReleaser release on v* tags with cosign keyless signing and SLSA L3 attestations.

Applied to files:

  • .github/workflows/ci.yml
  • CLAUDE.md
📚 Learning: 2026-03-24T20:58:10.766Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T20:58:10.766Z
Learning: Applies to web/src/**/*.{tsx,ts} : React code must pass ESLint (configured with `eslint-react/eslint-plugin`) and TypeScript type-check

Applied to files:

  • web/package.json
  • CLAUDE.md
📚 Learning: 2026-03-16T19:52:03.656Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T19:52:03.656Z
Learning: Applies to cli/**/*.go : Lint CLI Go code with golangci-lint and go vet; test with go test -race; check vulnerabilities with govulncheck

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-19T11:30:29.217Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:30:29.217Z
Learning: Applies to cli/**/*.go : Run Go lint via `golangci-lint run`, vet via `go vet`, tests via `go test ./...`, and fuzz via `go test -fuzz=FuzzTarget -fuzztime=30s`

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 cli/**/*.go : Lint Go code with `golangci-lint` and `go vet`. Run tests with `-race` flag to detect race conditions.

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-24T20:58:10.766Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T20:58:10.766Z
Learning: Applies to web/src/hooks/*.{tsx,ts} : React hooks must be in `web/src/hooks/` directory and follow React hooks conventions. Custom hooks must be prefixed with `use`.

Applied to files:

  • CLAUDE.md
  • web/src/__tests__/test-utils.tsx
📚 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-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-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: Pre-push hooks: mypy type-check + pytest unit tests + golangci-lint + go vet + go test (CLI, conditional on cli/**/*.go) (fast gate before push, skipped in pre-commit.ci — dedicated CI jobs already run these).

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: Pre-commit hooks: trailing-whitespace, end-of-file-fixer, check-yaml, check-toml, check-json, check-merge-conflict, check-added-large-files, no-commit-to-branch (main), ruff check+format, gitleaks, hadolint (Dockerfile linting).

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 .github/workflows/finalize-release.yml : Finalize Release workflow: publishes draft releases created by Release Please. Triggers on workflow_run completion of Docker and CLI workflows. Verifies both workflows succeeded for the associated tag before publishing. Extracts CLI checksums, cosign verification, and container verification data from HTML comments, assembles into combined Verification section. Guards against PR-triggered runs. Handles TOCTOU races. Immutable releases enabled—once published, release assets and body cannot be modified.

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-24T20:58:10.766Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T20:58:10.766Z
Learning: Applies to pyproject.toml : Dependencies are organized in groups: `test` (pytest + plugins, hypothesis), `dev` (includes test + ruff, mypy, pre-commit, commitizen, pip-audit). Install with `uv sync`.

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-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
📚 Learning: 2026-03-24T20:58:10.766Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T20:58:10.766Z
Learning: Applies to web/src/__tests__/**/*.test.{tsx,ts} : React property-based testing must use fast-check (`fc.assert` + `fc.property`)

Applied to files:

  • web/src/__tests__/components/layout/SidebarNavItem.test.tsx
  • web/src/__tests__/test-utils.tsx
  • web/src/__tests__/App.test.tsx
  • web/src/__tests__/components/layout/Sidebar.test.tsx
  • web/src/__tests__/router/guards.test.tsx
🧬 Code graph analysis (29)
web/src/pages/TaskDetailPage.tsx (1)
web/src/components/layout/PlaceholderPage.tsx (1)
  • PlaceholderPage (2-8)
web/src/pages/SettingsPage.tsx (1)
web/src/components/layout/PlaceholderPage.tsx (1)
  • PlaceholderPage (2-8)
web/src/pages/MeetingDetailPage.tsx (1)
web/src/components/layout/PlaceholderPage.tsx (1)
  • PlaceholderPage (2-8)
web/src/pages/ProvidersPage.tsx (1)
web/src/components/layout/PlaceholderPage.tsx (1)
  • PlaceholderPage (2-8)
web/src/pages/OrgEditPage.tsx (1)
web/src/components/layout/PlaceholderPage.tsx (1)
  • PlaceholderPage (2-8)
web/src/pages/BudgetForecastPage.tsx (1)
web/src/components/layout/PlaceholderPage.tsx (1)
  • PlaceholderPage (2-8)
web/src/pages/AgentDetailPage.tsx (1)
web/src/components/layout/PlaceholderPage.tsx (1)
  • PlaceholderPage (2-8)
web/src/pages/ProviderDetailPage.tsx (1)
web/src/components/layout/PlaceholderPage.tsx (1)
  • PlaceholderPage (2-8)
web/src/pages/BudgetPage.tsx (1)
web/src/components/layout/PlaceholderPage.tsx (1)
  • PlaceholderPage (2-8)
web/src/pages/DashboardPage.tsx (1)
web/src/components/layout/PlaceholderPage.tsx (1)
  • PlaceholderPage (2-8)
web/src/pages/OrgChartPage.tsx (1)
web/src/components/layout/PlaceholderPage.tsx (1)
  • PlaceholderPage (2-8)
web/src/pages/AgentsPage.tsx (1)
web/src/components/layout/PlaceholderPage.tsx (1)
  • PlaceholderPage (2-8)
web/src/pages/ApprovalsPage.tsx (1)
web/src/components/layout/PlaceholderPage.tsx (1)
  • PlaceholderPage (2-8)
web/src/pages/NotFoundPage.tsx (1)
web/src/components/ui/button.stories.tsx (1)
  • Link (43-45)
web/src/pages/SettingsNamespacePage.tsx (1)
web/src/components/layout/PlaceholderPage.tsx (1)
  • PlaceholderPage (2-8)
web/src/pages/TaskBoardPage.tsx (1)
web/src/components/layout/PlaceholderPage.tsx (1)
  • PlaceholderPage (2-8)
web/src/components/layout/AppLayout.tsx (2)
web/src/components/layout/StatusBar.tsx (1)
  • StatusBar (3-43)
web/src/components/layout/Sidebar.tsx (1)
  • Sidebar (27-225)
web/src/__tests__/components/layout/SidebarNavItem.test.tsx (2)
web/src/__tests__/test-utils.tsx (1)
  • renderWithRouter (16-25)
web/src/components/layout/SidebarNavItem.tsx (1)
  • SidebarNavItem (15-57)
web/src/components/layout/Sidebar.stories.tsx (2)
web/src/components/layout/Sidebar.tsx (1)
  • Sidebar (27-225)
web/src/stores/auth.ts (1)
  • useAuthStore (61-210)
web/src/router/index.tsx (2)
web/src/components/layout/AppLayout.tsx (1)
  • AppLayout (14-28)
web/src/router/guards.tsx (4)
  • GuestGuard (94-102)
  • SetupCompleteGuard (110-144)
  • AuthGuard (37-56)
  • SetupGuard (64-89)
web/src/stores/setup.ts (1)
web/src/api/endpoints/setup.ts (1)
  • getSetupStatus (19-22)
web/src/__tests__/stores/setup.test.ts (2)
web/src/stores/setup.ts (1)
  • useSetupStore (13-30)
web/src/api/endpoints/setup.ts (1)
  • getSetupStatus (19-22)
web/src/__tests__/App.test.tsx (3)
web/src/stores/auth.ts (1)
  • useAuthStore (61-210)
web/src/stores/setup.ts (1)
  • useSetupStore (13-30)
web/src/App.tsx (1)
  • App (3-5)
web/src/pages/MessagesPage.tsx (1)
web/src/components/layout/PlaceholderPage.tsx (1)
  • PlaceholderPage (2-8)
web/src/__tests__/components/layout/Sidebar.test.tsx (3)
web/src/stores/auth.ts (2)
  • useAuthStore (61-210)
  • setup (156-158)
web/src/__tests__/test-utils.tsx (1)
  • renderWithRouter (16-25)
web/src/components/layout/Sidebar.tsx (1)
  • Sidebar (27-225)
web/src/__tests__/router/guards.test.tsx (5)
web/src/stores/auth.ts (1)
  • useAuthStore (61-210)
web/src/stores/setup.ts (1)
  • useSetupStore (13-30)
web/src/__tests__/test-utils.tsx (1)
  • renderRoutes (30-36)
web/src/router/guards.tsx (4)
  • AuthGuard (37-56)
  • SetupGuard (64-89)
  • GuestGuard (94-102)
  • SetupCompleteGuard (110-144)
web/src/api/endpoints/setup.ts (1)
  • getSetupStatus (19-22)
web/src/App.tsx (1)
web/src/router/index.tsx (1)
  • AppRouter (115-117)
web/src/router/guards.tsx (3)
web/src/stores/auth.ts (3)
  • useIsAuthenticated (214-214)
  • useAuthStore (61-210)
  • fetchUser (160-184)
web/src/router/routes.ts (1)
  • ROUTES (7-28)
web/src/stores/setup.ts (2)
  • useSetupStore (13-30)
  • fetchSetupStatus (18-29)
web/src/pages/MeetingsPage.tsx (1)
web/src/components/layout/PlaceholderPage.tsx (1)
  • PlaceholderPage (2-8)
🔇 Additional comments (41)
.gitignore (1)

54-54: Ignoring Storybook build output is correct.

This keeps generated static artifacts out of source control and reduces noisy diffs.

web/src/components/layout/PlaceholderPage.tsx (1)

1-8: Clean reusable placeholder component.

Typed props and minimal markup make this a good scaffold for unimplemented routes.

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

1-5: Good use of shared page scaffold.

This keeps page stubs consistent while routing is being built out.

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

1-5: Implementation looks correct.

The page wrapper is simple and consistent with the placeholder routing strategy.

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

1-5: LGTM.

This follows the same typed placeholder-page composition used across the new routes.

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

1-5: Looks good.

The page stub is consistent with the rest of the route scaffolding.

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

1-5: Approved.

Consistent, typed, and straightforward placeholder page composition.

web/.npmrc (1)

1-3: The legacy-peer-deps=true setting in web/.npmrc is already properly scoped to the web/ directory only and is necessary. Storybook 8.6.18 declares peer support for React ^19.0.0-beta only, while the project uses the React 19 release (^19), which creates a genuine peer dependency conflict that npm would flag. The workaround is justified and correctly placed—no changes needed.

			> Likely an incorrect or invalid review comment.
web/src/pages/OrgEditPage.tsx (1)

1-5: Looks good: shared placeholder component is used correctly.

This resolves the earlier duplication pattern cleanly and keeps route stubs consistent.

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

1-5: Clean placeholder page implementation.

Consistent with the shared route-stub pattern and no issues in this segment.

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

1-5: LGTM for this route stub.

The page is minimal, consistent, and aligned with the shared placeholder approach.

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

1-5: Good implementation for placeholder route page.

No issues found in this changed segment.

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

1-5: Consistent and correct placeholder page wiring.

This change is straightforward and aligns with the app-shell stubbing pattern.

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

1-5: Looks good and aligned with conventions.

No issues detected in this page stub.

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

1-5: Good placeholder implementation.

Consistent with the rest of the new page stubs and no issues found.

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

1-5: LGTM.

The component is correctly implemented and consistent with the shared page pattern.

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

1-5: Looks good and consistent with the placeholder-page routing pattern.

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

1-7: Clean setup page scaffold; no issues found in this segment.

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

1-5: Implementation is straightforward and aligned with the other placeholder pages.

web/package.json (1)

13-15: Storybook scripts and dependency additions are coherent and well-aligned with the PR objectives.

Also applies to: 42-48, 57-57, 63-63

web/src/__tests__/test-utils.tsx (1)

16-25: Good test-helper abstraction; this should reduce duplication across router tests.

Also applies to: 30-36

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

1-5: Looks good—consistent with the other placeholder route pages.

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

1-5: LGTM!

Clean placeholder page implementation following the established pattern for lazy-loaded route scaffolding.

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

1-14: LGTM!

The NotFoundPage correctly uses the asChild pattern with Button wrapping the react-router Link, following shadcn/ui conventions. The centered layout with clear 404 messaging and navigation back to dashboard is well-implemented.

web/src/App.tsx (1)

1-5: LGTM!

Clean refactor delegating all routing logic to the AppRouter component. This is the appropriate pattern for a router-based application entry point.

web/src/stores/auth.ts (1)

139-149: LGTM!

Good security practice using sessionStorage (same-origin only) for the return path to prevent open redirect attacks. The comment clearly explains why window.location.href is used instead of react-router navigation.

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

1-7: LGTM!

Clean placeholder for the login page skeleton. The full-viewport centered layout is appropriate for an auth page.

web/.storybook/preview.tsx (1)

1-20: LGTM!

Well-structured Storybook preview configuration. The global decorator ensures consistent dark theme styling across all stories, and importing the global CSS file ensures Tailwind utilities are available.

.github/workflows/ci.yml (2)

309-330: LGTM!

The Storybook build job follows the established pattern for dashboard CI jobs. Good practice using --disable-telemetry for CI environments.


336-357: LGTM!

Correctly integrated the new Storybook build job into the ci-pass gate, ensuring Storybook build failures will block the overall CI.

web/.storybook/main.ts (1)

1-18: LGTM!

Clean Storybook configuration with proper Tailwind CSS v4 Vite plugin integration. The dynamic import in viteFinal correctly appends the Tailwind plugin while preserving existing Vite configuration. The addon selection (essentials, a11y, interactions) aligns with the PR objectives.

CLAUDE.md (1)

56-57: LGTM!

Documentation updates accurately reflect the new Storybook commands, app shell structure (layout/, pages/, router/), CI job changes, and dependency additions.

Also applies to: 126-126, 129-130, 237-237, 257-257

web/src/components/layout/AppLayout.tsx (1)

1-28: LGTM!

Clean app shell implementation with proper accessibility attributes on the loading fallback (role="status", aria-live="polite"). The layout structure correctly handles overflow with a fixed StatusBar, collapsible Sidebar, and independently scrollable main content area.

web/src/components/layout/Sidebar.stories.tsx (1)

1-54: LGTM!

Stories correctly use decorators to set localStorage before render, addressing the previous review feedback. The global decorator properly sets up auth state and router context needed for the Sidebar component.

web/src/__tests__/components/layout/SidebarNavItem.test.tsx (1)

1-75: LGTM!

Comprehensive test coverage for SidebarNavItem covering collapsed/expanded states, badge rendering with cap behavior, and dot indicator presence. Uses @testing-library/react with the project's renderWithRouter helper as per coding guidelines.

web/src/__tests__/stores/setup.test.ts (1)

1-132: LGTM!

Well-structured test suite for the setup store covering initial state, success paths for both needs_setup values, concurrent fetch prevention, error handling, and retry behavior. The concurrent fetch test (lines 67-95) is particularly well-designed with proper async control flow.

web/src/router/index.tsx (1)

1-117: LGTM!

Well-structured router configuration with proper lazy-loading, guard nesting, and Suspense boundaries. The route hierarchy correctly chains guards (AuthGuard → SetupGuard) for protected routes, and AppLayout's internal Suspense handles lazy page loading for child routes.

web/src/__tests__/components/layout/Sidebar.test.tsx (2)

121-137: LGTM!

The logout test now correctly sets the spy before rendering the component (lines 124-131 set state with logoutSpy, then line 132 renders), addressing the previous review feedback about potential test flakiness.


1-138: Comprehensive Sidebar test suite.

Well-structured tests covering navigation rendering, user info display, collapse/expand behavior with localStorage persistence, and logout functionality. The window.location mock properly prevents side effects from the auth store's redirect behavior.

web/src/components/ui/button.stories.tsx (1)

4-70: Good Storybook baseline.

Typed meta plus the aggregate variants story gives a useful smoke surface for the button API.

web/src/__tests__/router/guards.test.tsx (1)

161-218: Good async guard coverage.

These cases lock down fetch success, loading/error, retry, and fail-closed behavior instead of only the static redirect branches.

Also applies to: 338-402

Comment on lines +6 to +8
<span className="text-[10px] uppercase tracking-widest text-surface-400">
SynthOrg
</span>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Replace hard-coded vendor name with a generic placeholder string.

Line 7 uses a real vendor name in UI copy. This should use a generic placeholder identifier in this code path.

✏️ Suggested change
-      <span className="text-[10px] uppercase tracking-widest text-surface-400">
-        SynthOrg
-      </span>
+      <span className="text-[10px] uppercase tracking-widest text-surface-400">
+        example-provider
+      </span>
As per coding guidelines, "Never use real vendor names in React code, tests, or config examples. Use generic names: `example-provider`, `example-large-001`, etc."
📝 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
<span className="text-[10px] uppercase tracking-widest text-surface-400">
SynthOrg
</span>
<span className="text-[10px] uppercase tracking-widest text-surface-400">
example-provider
</span>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/layout/StatusBar.tsx` around lines 6 - 8, Replace the
hard-coded vendor string in the StatusBar component: locate the span containing
"SynthOrg" in StatusBar.tsx and change it to a generic placeholder such as
"example-provider" (or another approved generic identifier like
"example-vendor") so UI copy does not contain real vendor names; update any
tests or snapshots referencing the "SynthOrg" literal to the new placeholder as
well.

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

🤖 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/layout/Sidebar.tsx`:
- Around line 158-170: The className string on the Notifications button in
Sidebar.tsx (the button rendering Bell with className={cn('size-5 shrink-0',
collapsed && 'mx-auto')}) exceeds the 88-char limit; refactor by extracting the
computed className into a short named constant (e.g., notificationIconClass or
iconClass) above the JSX or by wrapping the cn(...) call onto multiple
concatenated expressions so the JSX line is under 88 chars, keeping the same
logic with collapsed and reusing the Bell and button elements unchanged.
- Around line 142-156: The button JSX exceeds the 88-char line limit due to a
long className string; refactor the button to use the cn() utility to build the
className across multiple lines for readability and consistency. Locate the
button element that uses toggleCollapse and the collapsed state (which renders
PanelLeftOpen / PanelLeftClose) and replace the long className literal with a
cn(...) call split onto separate lines, keeping the same classes and leaving
title, aria-label, onClick, and the conditional children unchanged.

In `@web/src/router/guards.tsx`:
- Around line 49-71: The guard currently renders Outlet as soon as
useIsAuthenticated() is true while fetchUser() validates in background, causing
a brief render before token validation; to enforce fail-closed behavior
implement a validating state and block rendering until fetchUser() resolves: add
a validating boolean (e.g., useState) inside AuthGuard, set validating=true
before calling fetchUser() and set to false in finally, use validatedRef only to
avoid duplicate calls, and change the render to return <FullScreenLoading />
while validating is true (keep existing Navigate to ROUTES.LOGIN behavior when
!isAuthenticated), referencing AuthGuard, fetchUser, validatedRef, useAuthStore,
useIsAuthenticated and FullScreenLoading to locate where to change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 84a8a8c1-a30f-4bee-94ff-526eaddb363c

📥 Commits

Reviewing files that changed from the base of the PR and between 4dba2f5 and 188d01c.

📒 Files selected for processing (8)
  • docker/web/Dockerfile
  • web/src/__tests__/App.test.tsx
  • web/src/components/layout/Sidebar.tsx
  • web/src/components/layout/SidebarNavItem.tsx
  • web/src/components/layout/StatusBar.tsx
  • web/src/router/guards.tsx
  • web/src/router/routes.ts
  • web/src/stores/setup.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Build Sandbox
  • GitHub Check: Build Web
  • GitHub Check: Build Backend
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (3)
docker/**

📄 CodeRabbit inference engine (CLAUDE.md)

All Docker files are in docker/ (Dockerfiles, compose, .env.example). Single root .dockerignore. All images build with context: .

Files:

  • docker/web/Dockerfile
web/src/**/*.{tsx,ts}

📄 CodeRabbit inference engine (CLAUDE.md)

web/src/**/*.{tsx,ts}: React components must use TypeScript and follow shadcn/ui + Tailwind CSS conventions. Line length: 88 characters.
React code must pass ESLint (configured with @eslint-react/eslint-plugin) and TypeScript type-check
Never use real vendor names in React code, tests, or config examples. Use generic names: example-provider, example-large-001, etc.

Files:

  • web/src/components/layout/StatusBar.tsx
  • web/src/stores/setup.ts
  • web/src/components/layout/Sidebar.tsx
  • web/src/components/layout/SidebarNavItem.tsx
  • web/src/__tests__/App.test.tsx
  • web/src/router/guards.tsx
  • web/src/router/routes.ts
web/src/__tests__/**/*.test.{tsx,ts}

📄 CodeRabbit inference engine (CLAUDE.md)

web/src/__tests__/**/*.test.{tsx,ts}: React unit tests must use Vitest with @testing-library/react. Coverage is scoped to files changed vs origin/main.
React property-based testing must use fast-check (fc.assert + fc.property)

Files:

  • web/src/__tests__/App.test.tsx
🧠 Learnings (20)
📓 Common learnings
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.
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).
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)
📚 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:

  • docker/web/Dockerfile
📚 Learning: 2026-03-19T11:33:01.580Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:33:01.580Z
Learning: Applies to docker/Dockerfile.web : `nginxinc/nginx-unprivileged` base with non-root user (UID 101), Vue 3 SPA (PrimeVue + Tailwind CSS), SPA routing, API/WebSocket proxy to backend

Applied to files:

  • docker/web/Dockerfile
📚 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 docker/Dockerfile : Docker: 3-stage build (builder → setup → distroless runtime) for backend, Chainguard Python, non-root (UID 65532), CIS-hardened

Applied to files:

  • docker/web/Dockerfile
📚 Learning: 2026-03-24T20:58:10.766Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T20:58:10.766Z
Learning: Applies to docker/Dockerfile* : All Docker images use Chainguard distroless (backend, non-root), nginx-unprivileged (web, SPA + API proxy), or Python + Node.js (sandbox, non-root)

Applied to files:

  • docker/web/Dockerfile
📚 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: Dependabot: auto-updates Docker image digests and versions daily.

Applied to files:

  • docker/web/Dockerfile
📚 Learning: 2026-03-24T20:58:10.766Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T20:58:10.766Z
Learning: Applies to docker/** : All Docker files are in `docker/` (Dockerfiles, compose, `.env.example`). Single root `.dockerignore`. All images build with `context: .`

Applied to files:

  • docker/web/Dockerfile
📚 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 .github/workflows/docker.yml : Docker workflow: builds backend + web + sandbox images, pushes to GHCR, signs with cosign. SLSA L3 provenance attestations via actions/attest-build-provenance. Scans: Trivy (CRITICAL = hard fail, HIGH = warn) + Grype (critical cutoff) + CIS Docker Benchmark v1.6.0 compliance (informational). CVE triage via .github/.trivyignore.yaml and .github/.grype.yaml. Images only pushed after scans pass. Triggers on push to main and version tags (v*).

Applied to files:

  • docker/web/Dockerfile
📚 Learning: 2026-03-15T16:38:08.735Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T16:38:08.735Z
Learning: Applies to docker/** : Docker build context: single root .dockerignore (both images build with context: .). Tags: CI tags images with version from pyproject.toml ([tool.commitizen].version), semver, and SHA.

Applied to files:

  • docker/web/Dockerfile
📚 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 docker/Dockerfile.sandbox : Docker sandbox: `synthorg-sandbox` — Python 3.14 + Node.js + git, non-root (UID 10001), agent code execution sandbox

Applied to files:

  • docker/web/Dockerfile
📚 Learning: 2026-03-15T12:00:18.113Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T12:00:18.113Z
Learning: Applies to .github/workflows/docker.yml : CI Docker: build → scan → push to GHCR + cosign sign + SLSA L3 provenance via attest-build-provenance (images only pushed after Trivy/Grype scans pass).

Applied to files:

  • docker/web/Dockerfile
📚 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:

  • docker/web/Dockerfile
📚 Learning: 2026-03-24T20:58:10.766Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T20:58:10.766Z
Learning: Applies to web/src/**/*.{tsx,ts} : React components must use TypeScript and follow shadcn/ui + Tailwind CSS conventions. Line length: 88 characters.

Applied to files:

  • web/src/components/layout/StatusBar.tsx
  • web/src/components/layout/Sidebar.tsx
  • web/src/components/layout/SidebarNavItem.tsx
  • web/src/__tests__/App.test.tsx
📚 Learning: 2026-03-24T20:58:10.766Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T20:58:10.766Z
Learning: Applies to web/src/**/*.{tsx,ts} : Never use real vendor names in React code, tests, or config examples. Use generic names: `example-provider`, `example-large-001`, etc.

Applied to files:

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

Applied to files:

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

Applied to files:

  • web/src/components/layout/StatusBar.tsx
📚 Learning: 2026-03-24T20:58:10.766Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T20:58:10.766Z
Learning: Applies to **/{src,tests}/**/*.py : Never use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, tests, or config examples. Use generic names: `example-provider`, `example-large-001`, `test-provider`, etc.

Applied to files:

  • web/src/components/layout/StatusBar.tsx
📚 Learning: 2026-03-24T20:58:10.766Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T20:58:10.766Z
Learning: Applies to web/src/__tests__/**/*.test.{tsx,ts} : React unit tests must use Vitest with `testing-library/react`. Coverage is scoped to files changed vs origin/main.

Applied to files:

  • web/src/__tests__/App.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__/App.test.tsx
📚 Learning: 2026-03-24T20:58:10.766Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T20:58:10.766Z
Learning: Applies to web/src/__tests__/**/*.test.{tsx,ts} : React property-based testing must use fast-check (`fc.assert` + `fc.property`)

Applied to files:

  • web/src/__tests__/App.test.tsx
🔇 Additional comments (10)
docker/web/Dockerfile (1)

12-12: Good addition for build consistency.

Copying .npmrc ensures Docker builds use the same npm configuration (legacy-peer-deps=true) as CI and local dev. The file is only present in the discarded builder stage, not the final nginx runtime image.

Confirmed .npmrc contains only safe configuration with no auth tokens or secrets.

web/src/components/layout/StatusBar.tsx (1)

1-69: LGTM!

The component is well-structured with proper use of cn() for className composition, good accessibility practices (aria-hidden on decorative elements), and clean separation of helper components (Divider, Dot, StatusItem).

Note: The past review flagged "SynthOrg" as a vendor name, but this is the application's own brand name, not an external vendor like Anthropic/OpenAI/Claude/GPT that the coding guidelines prohibit. The app's own branding is appropriate here.

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

13-30: LGTM!

The Zustand store implementation is well-designed:

  • Proper concurrent-call guard with if (get().loading) return
  • Error handling correctly resets setupComplete to null (addressing the previous review concern about stale guard decisions)
  • Clean typed interface with clear documentation comments
web/src/router/routes.ts (1)

1-35: LGTM!

The route constants module is well-organized with proper as const typing. The PUBLIC_ROUTES array now correctly includes ROUTES.SETUP_STEP (addressing the previous review concern about incomplete public routes for multi-step setup).

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

79-104: LGTM!

SetupGuard correctly:

  • Triggers fetch when setupComplete === null and not loading/error
  • Shows error UI with retry capability
  • Shows loading state while fetching
  • Redirects to setup when incomplete

125-159: LGTM!

SetupCompleteGuard implements proper fail-closed behavior for authenticated users—redirecting to dashboard on error rather than allowing setup access. The unauthenticated pass-through is correctly documented.

web/src/components/layout/SidebarNavItem.tsx (1)

1-63: LGTM!

Well-implemented navigation item with:

  • Proper active-state styling via NavLink's className callback
  • Good accessibility (aria-hidden on decorative elements, title for tooltip when collapsed)
  • Clean badge formatting with 99+ cap
  • Line length compliance using cn() composition
web/src/__tests__/App.test.tsx (1)

1-77: LGTM!

The test file is well-structured:

  • Proper store resets in beforeEach including the error flag (addressing previous review)
  • Semantic queries with getByRole (addressing previous fragility concerns)
  • Clean mock setup for the setup API
  • Appropriate window.location handling for auth redirects
web/src/components/layout/Sidebar.tsx (2)

27-40: LGTM on collapse persistence logic.

Clean implementation using localStorage with proper state sync on toggle. The functional updater pattern in setCollapsed correctly captures the previous state.


196-221: LGTM on user/logout section.

Proper conditional rendering when user is present, with accessibility attributes (aria-label, title) on the logout button addressing the previous review concern.

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

♻️ Duplicate comments (1)
web/src/components/layout/Sidebar.tsx (1)

218-223: ⚠️ Potential issue | 🟡 Minor

Line length rule still violated on logout button className.

Line 222 exceeds the 88-character limit. Please split/extract it (same pattern already used with SIDEBAR_BUTTON_CLASS).

♻️ Proposed refactor
+const LOGOUT_BUTTON_CLASS = cn(
+  'rounded-md p-1 text-surface-400 transition-colors',
+  'hover:bg-surface-100 hover:text-surface-700',
+)
@@
               <button
                 onClick={logout}
                 title="Logout"
                 aria-label="Logout"
-                className="rounded-md p-1 text-surface-400 transition-colors hover:bg-surface-100 hover:text-surface-700"
+                className={LOGOUT_BUTTON_CLASS}
               >
As per coding guidelines: "Line length: 88 characters."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/layout/Sidebar.tsx` around lines 218 - 223, The logout
button's className string in the JSX for the logout button exceeds the 88-char
line limit; extract the repeated class string into a shared constant (like the
existing SIDEBAR_BUTTON_CLASS) and replace the inline className on the logout
button with that constant (or compose it if extra classes are needed) so the JSX
line is shortened and follows the line-length rule.
🤖 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__/router/guards.test.tsx`:
- Around line 229-251: Update the test to exercise the retry flow: use
vi.mocked(getSetupStatus) (mockGetSetupStatus) and make the initial failure
mockRejectedValueOnce(new Error('Network error')), call useAuthStore.setState as
before, render the routes but capture the user helper returned by renderRoutes,
then after asserting the error UI, set
mockGetSetupStatus.mockResolvedValueOnce(...) with a successful setup response,
simulate clicking the retry button via await
user.click(screen.getByRole('button', { name: /retry/i })), wait for the
expected app content (e.g. "App Content") to appear, and assert
mockGetSetupStatus was called twice to verify the retry attempted a second
fetch; keep references to getSetupStatus, SetupGuard, renderRoutes, useAuthStore
and mockGetSetupStatus to locate the changes.
- Around line 105-128: Add a test in guards.test.tsx that verifies AuthGuard
"fails open" on non-401 errors: mock the auth endpoint (getMe) to reject with a
non-401 error using vi.mocked(getMe).mockRejectedValueOnce(new Error('Network
error')), set a token via useAuthStore.setState({ token: 'test-token' }), render
the route tree with AuthGuard via renderRoutes (children render
<div>Protected</div>), assert the "Loading..." text appears, then await
waitFor(() => expect(screen.getByText('Protected')).toBeInTheDocument()) to
confirm the outlet renders despite the transient failure.

In `@web/src/components/layout/Sidebar.tsx`:
- Around line 34-36: The Sidebar component currently reads and writes
localStorage directly for STORAGE_KEY when initializing the collapsed state and
toggling it; wrap all localStorage accesses in a safe guard (check typeof window
!== 'undefined' or try/catch) so failures in restricted contexts don't throw: in
the useState initializer for collapsed (and anywhere setCollapsed triggers a
write) attempt to read localStorage.getItem(STORAGE_KEY) inside a try block and
fall back to a safe default (e.g., false) on error, and similarly wrap
localStorage.setItem calls in try/catch so errors are ignored or logged instead
of crashing the component.
- Around line 57-61: The Sidebar component currently renders vendor branding
text ("SynthOrg") and its initial ("S") using the collapsed conditional; replace
both occurrences with generic names per policy — e.g., change the expanded label
rendered by the span in Sidebar.tsx from "SynthOrg" to a generic identifier like
"example-provider" and change the collapsed initial from "S" to a matching
neutral initial like "E" (or another letter tied to your chosen generic label)
so the component (references: collapsed variable and the two span elements) no
longer contains real vendor names.

---

Duplicate comments:
In `@web/src/components/layout/Sidebar.tsx`:
- Around line 218-223: The logout button's className string in the JSX for the
logout button exceeds the 88-char line limit; extract the repeated class string
into a shared constant (like the existing SIDEBAR_BUTTON_CLASS) and replace the
inline className on the logout button with that constant (or compose it if extra
classes are needed) so the JSX line is shortened and follows the line-length
rule.
🪄 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: 351a332d-c38c-402f-b13a-ae77261515a4

📥 Commits

Reviewing files that changed from the base of the PR and between 188d01c and acaba36.

📒 Files selected for processing (3)
  • web/src/__tests__/router/guards.test.tsx
  • web/src/components/layout/Sidebar.tsx
  • web/src/router/guards.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 Sandbox
  • GitHub Check: Build Backend
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (2)
web/src/**/*.{tsx,ts}

📄 CodeRabbit inference engine (CLAUDE.md)

web/src/**/*.{tsx,ts}: React components must use TypeScript and follow shadcn/ui + Tailwind CSS conventions. Line length: 88 characters.
React code must pass ESLint (configured with @eslint-react/eslint-plugin) and TypeScript type-check
Never use real vendor names in React code, tests, or config examples. Use generic names: example-provider, example-large-001, etc.

Files:

  • web/src/components/layout/Sidebar.tsx
  • web/src/__tests__/router/guards.test.tsx
  • web/src/router/guards.tsx
web/src/__tests__/**/*.test.{tsx,ts}

📄 CodeRabbit inference engine (CLAUDE.md)

web/src/__tests__/**/*.test.{tsx,ts}: React unit tests must use Vitest with @testing-library/react. Coverage is scoped to files changed vs origin/main.
React property-based testing must use fast-check (fc.assert + fc.property)

Files:

  • web/src/__tests__/router/guards.test.tsx
🧠 Learnings (6)
📓 Common learnings
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.
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).
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)
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`).
📚 Learning: 2026-03-24T20:58:10.766Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T20:58:10.766Z
Learning: Applies to web/src/**/*.{tsx,ts} : React components must use TypeScript and follow shadcn/ui + Tailwind CSS conventions. Line length: 88 characters.

Applied to files:

  • web/src/components/layout/Sidebar.tsx
📚 Learning: 2026-03-15T11:48:14.867Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T11:48:14.867Z
Learning: Applies to **/*.{py,md} : Line length: 88 characters (ruff).

Applied to files:

  • web/src/components/layout/Sidebar.tsx
📚 Learning: 2026-03-24T20:58:10.766Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T20:58:10.766Z
Learning: Applies to web/src/__tests__/**/*.test.{tsx,ts} : React unit tests must use Vitest with `testing-library/react`. Coverage is scoped to files changed vs origin/main.

Applied to files:

  • web/src/__tests__/router/guards.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__/router/guards.test.tsx
📚 Learning: 2026-03-24T20:58:10.766Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T20:58:10.766Z
Learning: Applies to web/src/__tests__/**/*.test.{tsx,ts} : React property-based testing must use fast-check (`fc.assert` + `fc.property`)

Applied to files:

  • web/src/__tests__/router/guards.test.tsx
🧬 Code graph analysis (3)
web/src/components/layout/Sidebar.tsx (4)
web/src/hooks/useAuth.ts (1)
  • useAuth (10-31)
web/src/stores/auth.ts (2)
  • logout (206-208)
  • useAuthStore (61-210)
web/src/components/layout/SidebarNavItem.tsx (1)
  • SidebarNavItem (15-63)
web/src/router/routes.ts (1)
  • ROUTES (7-28)
web/src/__tests__/router/guards.test.tsx (5)
web/src/stores/auth.ts (1)
  • useAuthStore (61-210)
web/src/stores/setup.ts (1)
  • useSetupStore (13-30)
web/src/__tests__/test-utils.tsx (1)
  • renderRoutes (30-36)
web/src/router/guards.tsx (4)
  • AuthGuard (50-83)
  • SetupGuard (91-116)
  • GuestGuard (121-129)
  • SetupCompleteGuard (137-171)
web/src/api/endpoints/setup.ts (1)
  • getSetupStatus (19-22)
web/src/router/guards.tsx (3)
web/src/stores/auth.ts (2)
  • useIsAuthenticated (214-214)
  • useAuthStore (61-210)
web/src/router/routes.ts (1)
  • ROUTES (7-28)
web/src/stores/setup.ts (2)
  • useSetupStore (13-30)
  • fetchSetupStatus (18-29)
🔇 Additional comments (10)
web/src/components/layout/Sidebar.tsx (1)

27-31: Good cleanup on shared button classes and accessibility labels.

Extracting SIDEBAR_BUTTON_CLASS and adding aria-label on control buttons improves consistency and a11y.

Also applies to: 148-175

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

7-42: Good accessibility and component extraction.

The shared loading and error components appropriately use ARIA attributes (role="status", aria-live="polite", role="alert"). Extracting FullScreenLoading addresses the previous review feedback about duplication.


50-83: Fail-closed token validation implemented correctly.

The validating state now blocks <Outlet /> until fetchUser() completes, addressing the previous review feedback. The pattern correctly handles:

  • 401 responses: clearAuth() clears token → isAuthenticated becomes false → redirect to login
  • Non-401 errors: Tolerated (retry on subsequent API calls), outlet renders after validation attempt

The useRef prevents duplicate fetches on strict-mode double-renders, and the .catch(() => {}) / .finally() pattern ensures no unhandled rejections while always clearing the validating state.


91-116: LGTM!

The guard correctly prioritizes error handling over loading states, and the retry mechanism integrates cleanly with the store's fetchSetupStatus which resets error: false, loading: true before retrying.


121-129: LGTM!

Simple and correct implementation for guest-only routes.


137-171: LGTM!

The fail-closed design correctly differs from SetupGuard: on fetch failure, authenticated users are redirected to the dashboard rather than blocked with an error screen, preventing unauthorized setup access during transient failures.

web/src/__tests__/router/guards.test.tsx (4)

7-53: LGTM!

The test setup correctly:

  • Mocks APIs at module level for consistent behavior
  • Prevents window.location side effects from clearAuth()
  • Provides resetStores() to ensure test isolation via Zustand's setState API

55-129: LGTM!

The AuthGuard tests comprehensively cover the three main states:

  1. Unauthenticated → redirect
  2. Authenticated with user → render immediately
  3. Token-only (page refresh) → loading then render after validation

The loading-state test (lines 105-128) correctly validates the fail-closed behavior by checking the intermediate loading state before the mocked getMe resolves.


254-300: LGTM!

The GuestGuard tests correctly isolate the guard's behavior by using a plain <div> for the redirect target rather than nesting guards, avoiding unintended interactions.


302-436: LGTM!

Comprehensive coverage of SetupCompleteGuard including the critical fail-closed test (lines 408-435) that verifies authenticated users are redirected to the dashboard rather than granted setup access when the status check fails.

Comment on lines +57 to +61
{collapsed ? (
<span className="mx-auto text-lg font-bold text-brand-400">S</span>
) : (
<span className="text-lg font-bold text-brand-400">SynthOrg</span>
)}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Replace hardcoded vendor branding with a generic label.

Line 60 uses SynthOrg, which violates the generic-name requirement for React code examples/UI copy in this repo policy.

♻️ Proposed fix
         {collapsed ? (
-          <span className="mx-auto text-lg font-bold text-brand-400">S</span>
+          <span className="mx-auto text-lg font-bold text-brand-400">E</span>
         ) : (
-          <span className="text-lg font-bold text-brand-400">SynthOrg</span>
+          <span className="text-lg font-bold text-brand-400">Example Org</span>
         )}
As per coding guidelines: "Never use real vendor names in React code, tests, or config examples. Use generic names: `example-provider`, `example-large-001`, etc."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/layout/Sidebar.tsx` around lines 57 - 61, The Sidebar
component currently renders vendor branding text ("SynthOrg") and its initial
("S") using the collapsed conditional; replace both occurrences with generic
names per policy — e.g., change the expanded label rendered by the span in
Sidebar.tsx from "SynthOrg" to a generic identifier like "example-provider" and
change the collapsed initial from "S" to a matching neutral initial like "E" (or
another letter tied to your chosen generic label) so the component (references:
collapsed variable and the two span elements) no longer contains real vendor
names.

Aureliolo and others added 6 commits March 24, 2026 23:02
Add the dashboard navigation shell with React Router, collapsible sidebar,
status bar, route guards, and Storybook infrastructure for component
development.

App Shell:
- React Router v7 with createBrowserRouter and lazy-loaded routes
- 4 route guards: AuthGuard, SetupGuard, GuestGuard, SetupCompleteGuard
- Collapsible sidebar (220px/56px) with primary + workspace nav sections
- Full-width status bar skeleton (matching mockup v2 layout)
- 20 page stubs for all routes from page-structure.md
- 404 not-found page with back-to-dashboard link
- Setup store enhanced with fetchSetupStatus() for guard logic

Storybook:
- @storybook/react v8 + Vite builder
- Tailwind CSS 4 integration via viteFinal + @tailwindcss/vite
- Dark theme config (dark-mode-only dashboard)
- @storybook/addon-a11y for accessibility checks
- @storybook/addon-interactions for interaction testing
- Initial stories: Button variants, Sidebar expanded/collapsed

CI/CD:
- New dashboard-storybook-build job in ci.yml
- All dashboard CI jobs updated with --legacy-peer-deps for Storybook compat

Testing:
- renderWithRouter and renderRoutes test utilities
- Route guard tests (10 test cases)
- Sidebar tests (7 test cases covering nav, collapse, persistence)
- Updated App.test.tsx for router-based rendering
- All 203 tests pass

Closes #770

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix undefined text-text-secondary/text-text-primary CSS tokens across
  all page stubs and guards (use valid surface-500/surface-700 tokens)
- Add `end` prop to Dashboard NavLink to prevent permanent active state
- Use Zustand selectors in setup store guards (consistent with auth store)
- Use cn() for class merging in StatusBar Dot component
- Create web/.npmrc with legacy-peer-deps=true (replaces CLI flags in CI)
- Remove --legacy-peer-deps from all CI npm ci commands
- Update CLAUDE.md: add router/, pages/, layout/ to Package Structure,
  add Storybook commands, update CI job description
- Add setup store tests (concurrent fetch guard, error handling, inversion)
- Add SetupGuard fetch-triggering useEffect test
- Add SidebarNavItem tests (badge capping, dot, collapsed tooltip)
- Add sidebar logout button test
- Fix App.test.tsx: deduplicate tests, add authenticated shell test
- Fix Sidebar test: reset auth store in beforeEach

Pre-reviewed by 5 agents, 15 findings addressed

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Security hardening:
- AuthGuard proactively validates token via fetchUser on page refresh
- SetupCompleteGuard fails closed on fetch error (redirects to dashboard)
- Setup store tracks error state with retry support
- Save return path to sessionStorage before auth redirect

Accessibility:
- Sidebar nav landmark scoped to navigation links only
- aria-label on Notifications and Logout buttons
- role="status" + aria-live="polite" on loading fallbacks

Code quality:
- Extract PlaceholderPage component (DRYs 17 page stubs)
- Extract FullScreenLoading/FullScreenError in guards
- Fix undefined bg-bg-base CSS token in LoginPage/SetupPage
- Fix Storybook stories play/decorator consistency
- Remove redundant truncate class in Sidebar
- Remove unused routes property from test-utils interface
- Add .npmrc comment explaining Storybook 8.x workaround
- Align Storybook addon versions to ^8.6.18
- Add Storybook to CLAUDE.md dependencies list

Tests:
- Fix logout spy timing (set before render, remove as-never cast)
- Use semantic queries in App.test.tsx (role-based, not count-based)
- Add SetupGuard error path test with retry button
- Add SetupCompleteGuard fail-closed test
- Add setup store error flag + retry tests
- Add SidebarNavItem dotColor prop tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- AuthGuard: proactive token validation via fetchUser with useRef guard
  to prevent re-attempts, .catch() to avoid unhandled rejections
- Setup store: explicitly reset setupComplete to null on error
- SetupCompleteGuard: handle error state from setup store
- App.test.tsx: reset setup store error flag in beforeEach
- SidebarNavItem: split badge className to respect 88-char line limit
- StatusBar: break long className with cn() helper
- Sidebar: add aria-label to collapse toggle button
- PUBLIC_ROUTES: include SETUP_STEP for completeness
- Dockerfile.web: copy .npmrc for legacy-peer-deps (fixes CI build)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- AuthGuard: fail-closed with useState(isAuthenticated && !user) to
  show loading during token validation on page refresh; validatedRef
  prevents duplicate fetch; .catch()/.finally() avoids unhandled
  rejections; lint-safe (no synchronous setState in effect body)
- Sidebar: extract SIDEBAR_BUTTON_CLASS constant to fix 88-char line
  limit on collapse toggle and notifications button classNames
- New test: AuthGuard loading state when token present but user null

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

- SetupGuard error test: exercise retry click, verify second fetch
  succeeds and renders app content
- AuthGuard: add test for fail-open on non-401 errors (transient
  network failure renders children in degraded state)
- Sidebar: wrap localStorage reads/writes in try/catch for restricted
  contexts (quota exceeded, privacy mode)
- Sidebar: extract logout button className with cn() for 88-char limit

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Aureliolo Aureliolo force-pushed the feat/770-app-shell-storybook branch from acaba36 to 947fd7f Compare March 24, 2026 22:06
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: 6

🤖 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/layout/Sidebar.stories.tsx`:
- Around line 38-53: Extract the repeated 'sidebar_collapsed' string into a
single shared constant (e.g., SIDEBAR_COLLAPSED_KEY) and use that constant
inside the decorators for the Expanded and Collapsed Story objects instead of
hardcoding the literal; declare the constant near the top of the file (and
export it if other modules need it), then replace
localStorage.setItem('sidebar_collapsed', ...) in the Expanded and Collapsed
decorators with localStorage.setItem(SIDEBAR_COLLAPSED_KEY, 'false') and
localStorage.setItem(SIDEBAR_COLLAPSED_KEY, 'true') respectively.

In `@web/src/components/layout/SidebarNavItem.tsx`:
- Line 11: The dotColor prop on SidebarNavItem is overly permissive (string);
constrain it to a union of known CSS class names to prevent invalid classes
being passed: update the dotColor type declaration in SidebarNavItem
(prop/interface/type for SidebarNavItem or SidebarNavItemProps) to something
like 'bg-success-500' | 'bg-warning-500' | 'bg-danger-500' | string so callers
can use the known set while retaining a fallback string; adjust any usages or
prop defaulting in the SidebarNavItem component to match the new union type.

In `@web/src/router/index.tsx`:
- Around line 88-106: The child routes list mounts lazy-loaded components (e.g.,
DashboardPage, OrgChartPage, TaskBoardPage, AgentDetailPage, etc.) without
per-route Suspense boundaries, causing errors when navigating to a
not-yet-loaded chunk; wrap each route's element with the project's
SuspenseWrapper (or equivalent) so each lazy page has its own suspense boundary
(e.g., replace element: <DashboardPage /> with element:
<SuspenseWrapper><DashboardPage /></SuspenseWrapper> for all entries) while
keeping AppLayout intact.
- Line 106: The catch-all route "{ path: '*', element: <NotFoundPage /> }" is
currently nested under the protected tree (AuthGuard/SetupGuard), so
unauthenticated users hit the login redirect instead of the 404; move the
wildcard route out of the AuthGuard/SetupGuard children to the top-level routes
array (or add an additional top-level fallback route) so NotFoundPage is matched
for unknown paths before or outside the protected wrapper, ensuring the wildcard
is not a child of the protected route tree.

In `@web/src/stores/auth.ts`:
- Around line 139-149: The duplicate checks using the same condition on
currentPath should be consolidated: compute the condition once (e.g., a boolean
like isNotAuthOrSetup based on currentPath !== '/login' && currentPath !==
'/setup') and wrap both sessionStorage.setItem('auth_return_path', currentPath)
and the hard redirect window.location.href = '/login' inside a single if block
(or use the boolean to gate both actions) in the auth store where currentPath,
sessionStorage.setItem and window.location.href are used.
- Around line 139-144: LoginPage currently doesn't consume the stored
sessionStorage key auth_return_path; update the LoginPage component (e.g., in
its successful authentication handler such as handleLogin/onAuthenticated or the
place that sets the authenticated user and does navigation) to read
sessionStorage.getItem('auth_return_path'), validate it's a same-origin path
(non-empty and starts with '/'), perform a redirect to that path (fallback to
'/' or dashboard if absent/invalid), and then remove the key
(sessionStorage.removeItem('auth_return_path')) so it won't be reused; ensure
this logic runs only after successful authentication and preserves existing
post-login flows.
🪄 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: 7b9a5c6a-c19c-473a-a865-9b904ad90230

📥 Commits

Reviewing files that changed from the base of the PR and between acaba36 and 947fd7f.

⛔ Files ignored due to path filters (1)
  • web/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (47)
  • .github/workflows/ci.yml
  • .gitignore
  • CLAUDE.md
  • docker/web/Dockerfile
  • web/.npmrc
  • web/.storybook/main.ts
  • web/.storybook/preview.tsx
  • web/package.json
  • web/src/App.tsx
  • web/src/__tests__/App.test.tsx
  • web/src/__tests__/components/layout/Sidebar.test.tsx
  • web/src/__tests__/components/layout/SidebarNavItem.test.tsx
  • web/src/__tests__/router/guards.test.tsx
  • web/src/__tests__/stores/setup.test.ts
  • web/src/__tests__/test-utils.tsx
  • web/src/components/layout/AppLayout.tsx
  • web/src/components/layout/PlaceholderPage.tsx
  • web/src/components/layout/Sidebar.stories.tsx
  • web/src/components/layout/Sidebar.tsx
  • web/src/components/layout/SidebarNavItem.tsx
  • web/src/components/layout/StatusBar.tsx
  • web/src/components/ui/button.stories.tsx
  • web/src/pages/AgentDetailPage.tsx
  • web/src/pages/AgentsPage.tsx
  • web/src/pages/ApprovalsPage.tsx
  • web/src/pages/BudgetForecastPage.tsx
  • web/src/pages/BudgetPage.tsx
  • web/src/pages/DashboardPage.tsx
  • web/src/pages/LoginPage.tsx
  • web/src/pages/MeetingDetailPage.tsx
  • web/src/pages/MeetingsPage.tsx
  • web/src/pages/MessagesPage.tsx
  • web/src/pages/NotFoundPage.tsx
  • web/src/pages/OrgChartPage.tsx
  • web/src/pages/OrgEditPage.tsx
  • web/src/pages/ProviderDetailPage.tsx
  • web/src/pages/ProvidersPage.tsx
  • web/src/pages/SettingsNamespacePage.tsx
  • web/src/pages/SettingsPage.tsx
  • web/src/pages/SetupPage.tsx
  • web/src/pages/TaskBoardPage.tsx
  • web/src/pages/TaskDetailPage.tsx
  • web/src/router/guards.tsx
  • web/src/router/index.tsx
  • web/src/router/routes.ts
  • web/src/stores/auth.ts
  • web/src/stores/setup.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Build Backend
  • GitHub Check: Build Sandbox
  • GitHub Check: Build Web
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Analyze (python)
  • GitHub Check: Analyze (go)
🧰 Additional context used
📓 Path-based instructions (7)
web/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

ESLint linting is required for React/TypeScript web dashboard code via npm --prefix web run lint.

Files:

  • web/src/pages/ApprovalsPage.tsx
  • web/src/pages/TaskDetailPage.tsx
  • web/src/pages/SettingsPage.tsx
  • web/src/pages/AgentsPage.tsx
  • web/src/pages/OrgEditPage.tsx
  • web/src/pages/MeetingDetailPage.tsx
  • web/src/components/layout/PlaceholderPage.tsx
  • web/src/pages/SettingsNamespacePage.tsx
  • web/src/pages/MessagesPage.tsx
  • web/src/pages/DashboardPage.tsx
  • web/src/pages/TaskBoardPage.tsx
  • web/src/pages/ProviderDetailPage.tsx
  • web/src/pages/AgentDetailPage.tsx
  • web/src/pages/ProvidersPage.tsx
  • web/src/pages/OrgChartPage.tsx
  • web/src/pages/SetupPage.tsx
  • web/src/pages/NotFoundPage.tsx
  • web/src/pages/BudgetPage.tsx
  • web/src/pages/LoginPage.tsx
  • web/src/pages/MeetingsPage.tsx
  • web/src/components/layout/AppLayout.tsx
  • web/src/components/layout/StatusBar.tsx
  • web/src/__tests__/components/layout/SidebarNavItem.test.tsx
  • web/src/components/layout/Sidebar.stories.tsx
  • web/src/__tests__/stores/setup.test.ts
  • web/src/__tests__/App.test.tsx
  • web/src/__tests__/components/layout/Sidebar.test.tsx
  • web/src/stores/setup.ts
  • web/src/components/layout/Sidebar.tsx
  • web/src/components/layout/SidebarNavItem.tsx
  • web/src/__tests__/test-utils.tsx
  • web/src/components/ui/button.stories.tsx
  • web/src/router/index.tsx
  • web/src/App.tsx
  • web/src/router/routes.ts
  • web/src/stores/auth.ts
  • web/src/__tests__/router/guards.test.tsx
  • web/src/pages/BudgetForecastPage.tsx
  • web/src/router/guards.tsx
web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

TypeScript type checking is required for web dashboard via npm --prefix web run type-check.

Files:

  • web/src/pages/ApprovalsPage.tsx
  • web/src/pages/TaskDetailPage.tsx
  • web/src/pages/SettingsPage.tsx
  • web/src/pages/AgentsPage.tsx
  • web/src/pages/OrgEditPage.tsx
  • web/src/pages/MeetingDetailPage.tsx
  • web/src/components/layout/PlaceholderPage.tsx
  • web/src/pages/SettingsNamespacePage.tsx
  • web/src/pages/MessagesPage.tsx
  • web/src/pages/DashboardPage.tsx
  • web/src/pages/TaskBoardPage.tsx
  • web/src/pages/ProviderDetailPage.tsx
  • web/src/pages/AgentDetailPage.tsx
  • web/src/pages/ProvidersPage.tsx
  • web/src/pages/OrgChartPage.tsx
  • web/src/pages/SetupPage.tsx
  • web/src/pages/NotFoundPage.tsx
  • web/src/pages/BudgetPage.tsx
  • web/src/pages/LoginPage.tsx
  • web/src/pages/MeetingsPage.tsx
  • web/src/components/layout/AppLayout.tsx
  • web/src/components/layout/StatusBar.tsx
  • web/src/__tests__/components/layout/SidebarNavItem.test.tsx
  • web/src/components/layout/Sidebar.stories.tsx
  • web/src/__tests__/stores/setup.test.ts
  • web/src/__tests__/App.test.tsx
  • web/src/__tests__/components/layout/Sidebar.test.tsx
  • web/src/stores/setup.ts
  • web/src/components/layout/Sidebar.tsx
  • web/src/components/layout/SidebarNavItem.tsx
  • web/src/__tests__/test-utils.tsx
  • web/src/components/ui/button.stories.tsx
  • web/src/router/index.tsx
  • web/src/App.tsx
  • web/src/router/routes.ts
  • web/src/stores/auth.ts
  • web/src/__tests__/router/guards.test.tsx
  • web/src/pages/BudgetForecastPage.tsx
  • web/src/router/guards.tsx
**/*.{yml,yaml,toml,json}

📄 CodeRabbit inference engine (CLAUDE.md)

YAML, TOML, JSON files must pass check-yaml, check-toml, check-json pre-commit hooks respectively.

Files:

  • web/package.json
web/package.json

📄 CodeRabbit inference engine (CLAUDE.md)

Web dashboard uses Node.js 22+. Key dependencies: React 19, react-router, shadcn/ui, Radix UI, Tailwind CSS 4, Zustand, @tanstack/react-query, @xyflow/react, Recharts, Framer Motion, cmdk, Axios, Lucide React, Vitest.

Files:

  • web/package.json
**/Dockerfile*

📄 CodeRabbit inference engine (CLAUDE.md)

Dockerfiles must pass hadolint linting via pre-commit hooks.

Files:

  • docker/web/Dockerfile
docker/**

📄 CodeRabbit inference engine (CLAUDE.md)

Docker configuration: use .env.example as template, all Docker files in docker/ directory (Dockerfiles, compose, env). Build and verify with docker compose commands.

Files:

  • docker/web/Dockerfile
web/**/__tests__/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

web/**/__tests__/**/*.{ts,tsx}: Web dashboard tests use Vitest with coverage scoped to files changed vs origin/main. Run with npm --prefix web run test.
Use fast-check for property-based testing in JavaScript/TypeScript (fc.assert + fc.property).

Files:

  • web/src/__tests__/components/layout/SidebarNavItem.test.tsx
  • web/src/__tests__/stores/setup.test.ts
  • web/src/__tests__/App.test.tsx
  • web/src/__tests__/components/layout/Sidebar.test.tsx
  • web/src/__tests__/test-utils.tsx
  • web/src/__tests__/router/guards.test.tsx
🧠 Learnings (41)
📓 Common learnings
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.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T22:02:25.937Z
Learning: Applies to web/package.json : Web dashboard uses Node.js 22+. Key dependencies: React 19, react-router, shadcn/ui, Radix UI, Tailwind CSS 4, Zustand, tanstack/react-query, xyflow/react, Recharts, Framer Motion, cmdk, Axios, Lucide React, Vitest.
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).
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)
📚 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/.npmrc
  • .gitignore
  • web/package.json
  • docker/web/Dockerfile
  • CLAUDE.md
  • web/.storybook/main.ts
📚 Learning: 2026-03-24T22:02:25.937Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T22:02:25.937Z
Learning: Applies to web/package.json : Web dashboard uses Node.js 22+. Key dependencies: React 19, react-router, shadcn/ui, Radix UI, Tailwind CSS 4, Zustand, tanstack/react-query, xyflow/react, Recharts, Framer Motion, cmdk, Axios, Lucide React, Vitest.

Applied to files:

  • web/.npmrc
  • .gitignore
  • web/package.json
  • CLAUDE.md
  • web/src/App.tsx
📚 Learning: 2026-03-15T18:17:43.675Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Applies to web/** : Web dashboard: Node.js 20+, dependencies in web/package.json (Vue 3, PrimeVue, Tailwind CSS, Pinia, VueFlow, ECharts, Axios, vue-draggable-plus, Vitest, fast-check, ESLint, vue-tsc).

Applied to files:

  • .gitignore
  • web/package.json
  • CLAUDE.md
  • web/src/App.tsx
📚 Learning: 2026-03-24T22:02:25.937Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T22:02:25.937Z
Learning: Applies to web/**/__tests__/**/*.{ts,tsx} : Web dashboard tests use Vitest with coverage scoped to files changed vs origin/main. Run with `npm --prefix web run test`.

Applied to files:

  • .gitignore
  • web/package.json
  • web/src/__tests__/components/layout/SidebarNavItem.test.tsx
  • CLAUDE.md
  • web/src/__tests__/stores/setup.test.ts
  • web/.storybook/main.ts
  • web/src/__tests__/App.test.tsx
  • web/src/__tests__/components/layout/Sidebar.test.tsx
  • web/src/__tests__/test-utils.tsx
  • web/src/__tests__/router/guards.test.tsx
📚 Learning: 2026-03-24T22:02:25.937Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T22:02:25.937Z
Learning: Applies to web/**/*.{ts,tsx,js,jsx} : ESLint linting is required for React/TypeScript web dashboard code via `npm --prefix web run lint`.

Applied to files:

  • .gitignore
  • web/package.json
  • CLAUDE.md
📚 Learning: 2026-03-24T22:02:25.937Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T22:02:25.937Z
Learning: Applies to web/**/*.{ts,tsx} : TypeScript type checking is required for web dashboard via `npm --prefix web run type-check`.

Applied to files:

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

Applied to files:

  • .gitignore
  • web/src/__tests__/components/layout/SidebarNavItem.test.tsx
  • CLAUDE.md
  • web/src/__tests__/stores/setup.test.ts
  • web/.storybook/main.ts
  • web/src/__tests__/App.test.tsx
  • web/src/__tests__/components/layout/Sidebar.test.tsx
  • web/src/__tests__/test-utils.tsx
  • web/src/__tests__/router/guards.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:

  • .gitignore
  • 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 .github/workflows/*.yml : Dependabot: daily updates for uv + github-actions + npm + pre-commit + docker + gomod, grouped minor/patch, no auto-merge. Use `/review-dep-pr` to review Dependabot PRs before merging.

Applied to files:

  • .github/workflows/ci.yml
  • docker/web/Dockerfile
  • 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 .github/workflows/**/*.yml : Path filtering: dorny/paths-filter detects Python/dashboard/docker changes; jobs only run when their domain is affected. CLI has its own workflow (cli.yml).

Applied to files:

  • .github/workflows/ci.yml
  • 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 .github/workflows/docker.yml : Docker workflow: builds backend + web + sandbox images, pushes to GHCR, signs with cosign. SLSA L3 provenance attestations via actions/attest-build-provenance. Scans: Trivy (CRITICAL = hard fail, HIGH = warn) + Grype (critical cutoff) + CIS Docker Benchmark v1.6.0 compliance (informational). CVE triage via .github/.trivyignore.yaml and .github/.grype.yaml. Images only pushed after scans pass. Triggers on push to main and version tags (v*).

Applied to files:

  • .github/workflows/ci.yml
  • docker/web/Dockerfile
  • 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 .github/workflows/cli.yml : CLI workflow: Go lint (golangci-lint + go vet) + test (-race -coverprofile) + build (cross-compile: linux/darwin/windows × amd64/arm64) + govulncheck + fuzz testing (main-only, 30s/target, continue-on-error, matrix over 4 packages). cli-pass gate includes fuzz as informational. GoReleaser release on v* tags. Cosign keyless signing of checksums.txt. SLSA L3 provenance attestations. Sigstore bundle (.sigstore.json) attached. Post-release appends checksums/verification/provenance to draft release notes.

Applied to files:

  • .github/workflows/ci.yml
  • CLAUDE.md
📚 Learning: 2026-03-15T12:00:18.113Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T12:00:18.113Z
Learning: Applies to .github/workflows/docker.yml : CI Docker: build → scan → push to GHCR + cosign sign + SLSA L3 provenance via attest-build-provenance (images only pushed after Trivy/Grype scans pass).

Applied to files:

  • .github/workflows/ci.yml
  • docker/web/Dockerfile
  • 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: CLI workflow (`.github/workflows/cli.yml`) runs Go lint (golangci-lint + go vet) + test (race, coverage) + build (cross-compile matrix) + vulnerability check (govulncheck) + fuzz testing. Cross-compiles for linux/darwin/windows × amd64/arm64. GoReleaser release on v* tags with cosign keyless signing and SLSA L3 attestations.

Applied to files:

  • .github/workflows/ci.yml
  • 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:

  • docker/web/Dockerfile
  • CLAUDE.md
📚 Learning: 2026-03-19T11:33:01.580Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:33:01.580Z
Learning: Applies to docker/Dockerfile.web : `nginxinc/nginx-unprivileged` base with non-root user (UID 101), Vue 3 SPA (PrimeVue + Tailwind CSS), SPA routing, API/WebSocket proxy to backend

Applied to files:

  • docker/web/Dockerfile
📚 Learning: 2026-03-24T22:02:25.937Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T22:02:25.937Z
Learning: Applies to docker/Dockerfile* : Docker images: backend (Chainguard distroless, non-root), web (nginx-unprivileged, SPA + API proxy), sandbox (Python + Node.js, non-root). All Dockerfiles in `docker/` use single root `.dockerignore` (all images build with `context: .`).

Applied to files:

  • docker/web/Dockerfile
📚 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 docker/Dockerfile : Docker: 3-stage build (builder → setup → distroless runtime) for backend, Chainguard Python, non-root (UID 65532), CIS-hardened

Applied to files:

  • docker/web/Dockerfile
📚 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: Dependabot: auto-updates Docker image digests and versions daily.

Applied to files:

  • docker/web/Dockerfile
📚 Learning: 2026-03-15T16:38:08.735Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T16:38:08.735Z
Learning: Applies to docker/** : Docker build context: single root .dockerignore (both images build with context: .). Tags: CI tags images with version from pyproject.toml ([tool.commitizen].version), semver, and SHA.

Applied to files:

  • docker/web/Dockerfile
📚 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 docker/Dockerfile.sandbox : Docker sandbox: `synthorg-sandbox` — Python 3.14 + Node.js + git, non-root (UID 10001), agent code execution sandbox

Applied to files:

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

Applied to files:

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

Applied to files:

  • web/src/components/layout/StatusBar.tsx
  • web/src/components/layout/Sidebar.tsx
📚 Learning: 2026-03-24T22:02:25.937Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T22:02:25.937Z
Learning: Applies to +(src|tests)/**/*.py : NEVER use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, tests, or config examples. Use generic names: `example-provider`, `example-large-001`, `example-medium-001`, `example-small-001`, `large`/`medium`/`small` as aliases. Vendor names may only appear in: (1) `docs/design/operations.md` provider list, (2) `.claude/` skill/agent files, (3) third-party import paths/module names (e.g. `litellm.types.llms.openai`).

Applied to files:

  • web/src/components/layout/StatusBar.tsx
  • web/src/components/layout/Sidebar.tsx
📚 Learning: 2026-03-24T22:02:25.937Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T22:02:25.937Z
Learning: Applies to web/**/__tests__/**/*.{ts,tsx} : Use fast-check for property-based testing in JavaScript/TypeScript (`fc.assert` + `fc.property`).

Applied to files:

  • web/src/__tests__/components/layout/SidebarNavItem.test.tsx
  • web/src/__tests__/App.test.tsx
  • web/src/__tests__/components/layout/Sidebar.test.tsx
  • web/src/__tests__/test-utils.tsx
  • web/src/__tests__/router/guards.test.tsx
📚 Learning: 2026-03-16T19:52:03.656Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T19:52:03.656Z
Learning: Applies to cli/**/*.go : Lint CLI Go code with golangci-lint and go vet; test with go test -race; check vulnerabilities with govulncheck

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-19T11:30:29.217Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:30:29.217Z
Learning: Applies to cli/**/*.go : Run Go lint via `golangci-lint run`, vet via `go vet`, tests via `go test ./...`, and fuzz via `go test -fuzz=FuzzTarget -fuzztime=30s`

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 cli/**/*.go : Lint Go code with `golangci-lint` and `go vet`. Run tests with `-race` flag to detect race conditions.

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:02:25.937Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T22:02:25.937Z
Learning: CI uses `dorny/paths-filter` for path filtering—jobs only run when their domain is affected. CLI has its own workflow (`cli.yml`).

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: Pre-push hooks: mypy type-check + pytest unit tests + golangci-lint + go vet + go test (CLI, conditional on cli/**/*.go) (fast gate before push, skipped in pre-commit.ci — dedicated CI jobs already run these).

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-24T22:02:25.937Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T22:02:25.937Z
Learning: Pre-push hooks enforce: mypy type-check + pytest unit tests + golangci-lint + go vet + go test (CLI). These are fast gates before push and skipped in pre-commit.ci.

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-24T22:02:25.937Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T22:02:25.937Z
Learning: Applies to .github/workflows/dev-release.yml : Dev channel releases are created for every push to `main` (except Release Please bumps) as semver dev tags (e.g. `v0.4.7-dev.3`) with draft pre-releases via `dev-release.yml`. Users opt in with `synthorg config set channel dev`. All dev releases flow through the same Docker + CLI pipelines as stable releases. All dev releases and tags are deleted when a stable release is published.

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-24T22:02:25.937Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T22:02:25.937Z
Learning: Applies to pyproject.toml : Python dependency groups: `test` (pytest + plugins, hypothesis), `dev` (includes test + ruff, mypy, pre-commit, commitizen, pip-audit). Install with `uv sync` (dev group is default).

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-24T22:02:25.937Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T22:02:25.937Z
Learning: Applies to pyproject.toml : Required Python dependencies: `mem0ai` (Mem0 memory backend), `cryptography` (Fernet encryption for sensitive settings), `faker` (multi-locale agent name generation).

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-24T22:02:25.937Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T22:02:25.937Z
Learning: Applies to cli/go.mod : CLI uses Go 1.26+. Key dependencies: 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 cli/** : CLI: Go 1.26+, dependencies in cli/go.mod (Cobra, charmbracelet/huh).

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-15T11:48:14.867Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T11:48:14.867Z
Learning: Applies to **/*.{py,md} : Line length: 88 characters (ruff).

Applied to files:

  • web/src/components/layout/Sidebar.tsx
🧬 Code graph analysis (29)
web/src/pages/ApprovalsPage.tsx (1)
web/src/components/layout/PlaceholderPage.tsx (1)
  • PlaceholderPage (2-8)
web/src/pages/TaskDetailPage.tsx (1)
web/src/components/layout/PlaceholderPage.tsx (1)
  • PlaceholderPage (2-8)
web/src/pages/SettingsPage.tsx (1)
web/src/components/layout/PlaceholderPage.tsx (1)
  • PlaceholderPage (2-8)
web/src/pages/AgentsPage.tsx (1)
web/src/components/layout/PlaceholderPage.tsx (1)
  • PlaceholderPage (2-8)
web/src/pages/OrgEditPage.tsx (1)
web/src/components/layout/PlaceholderPage.tsx (1)
  • PlaceholderPage (2-8)
web/src/pages/MeetingDetailPage.tsx (1)
web/src/components/layout/PlaceholderPage.tsx (1)
  • PlaceholderPage (2-8)
web/src/pages/SettingsNamespacePage.tsx (1)
web/src/components/layout/PlaceholderPage.tsx (1)
  • PlaceholderPage (2-8)
web/src/pages/MessagesPage.tsx (1)
web/src/components/layout/PlaceholderPage.tsx (1)
  • PlaceholderPage (2-8)
web/src/pages/DashboardPage.tsx (1)
web/src/components/layout/PlaceholderPage.tsx (1)
  • PlaceholderPage (2-8)
web/src/pages/TaskBoardPage.tsx (1)
web/src/components/layout/PlaceholderPage.tsx (1)
  • PlaceholderPage (2-8)
web/src/pages/ProviderDetailPage.tsx (1)
web/src/components/layout/PlaceholderPage.tsx (1)
  • PlaceholderPage (2-8)
web/src/pages/AgentDetailPage.tsx (1)
web/src/components/layout/PlaceholderPage.tsx (1)
  • PlaceholderPage (2-8)
web/src/pages/ProvidersPage.tsx (1)
web/src/components/layout/PlaceholderPage.tsx (1)
  • PlaceholderPage (2-8)
web/src/pages/OrgChartPage.tsx (1)
web/src/components/layout/PlaceholderPage.tsx (1)
  • PlaceholderPage (2-8)
web/src/pages/NotFoundPage.tsx (1)
web/src/components/ui/button.stories.tsx (1)
  • Link (43-45)
web/src/pages/BudgetPage.tsx (1)
web/src/components/layout/PlaceholderPage.tsx (1)
  • PlaceholderPage (2-8)
web/src/pages/MeetingsPage.tsx (1)
web/src/components/layout/PlaceholderPage.tsx (1)
  • PlaceholderPage (2-8)
web/src/components/layout/AppLayout.tsx (2)
web/src/components/layout/StatusBar.tsx (1)
  • StatusBar (3-50)
web/src/components/layout/Sidebar.tsx (1)
  • Sidebar (49-250)
web/src/__tests__/components/layout/SidebarNavItem.test.tsx (2)
web/src/__tests__/test-utils.tsx (1)
  • renderWithRouter (16-25)
web/src/components/layout/SidebarNavItem.tsx (1)
  • SidebarNavItem (15-63)
web/src/components/layout/Sidebar.stories.tsx (2)
web/src/components/layout/Sidebar.tsx (1)
  • Sidebar (49-250)
web/src/stores/auth.ts (1)
  • useAuthStore (61-210)
web/src/__tests__/stores/setup.test.ts (2)
web/src/stores/setup.ts (1)
  • useSetupStore (13-30)
web/src/api/endpoints/setup.ts (1)
  • getSetupStatus (19-22)
web/src/__tests__/App.test.tsx (3)
web/src/stores/auth.ts (1)
  • useAuthStore (61-210)
web/src/stores/setup.ts (1)
  • useSetupStore (13-30)
web/src/App.tsx (1)
  • App (3-5)
web/src/__tests__/components/layout/Sidebar.test.tsx (1)
web/src/components/layout/Sidebar.tsx (1)
  • Sidebar (49-250)
web/src/stores/setup.ts (1)
web/src/api/endpoints/setup.ts (1)
  • getSetupStatus (19-22)
web/src/components/layout/Sidebar.tsx (4)
web/src/hooks/useAuth.ts (1)
  • useAuth (10-31)
web/src/stores/auth.ts (2)
  • logout (206-208)
  • useAuthStore (61-210)
web/src/components/layout/SidebarNavItem.tsx (1)
  • SidebarNavItem (15-63)
web/src/router/routes.ts (1)
  • ROUTES (7-28)
web/src/App.tsx (1)
web/src/router/index.tsx (1)
  • AppRouter (115-117)
web/src/__tests__/router/guards.test.tsx (6)
web/src/stores/auth.ts (1)
  • useAuthStore (61-210)
web/src/stores/setup.ts (1)
  • useSetupStore (13-30)
web/src/__tests__/test-utils.tsx (1)
  • renderRoutes (30-36)
web/src/router/guards.tsx (4)
  • AuthGuard (50-83)
  • SetupGuard (91-116)
  • GuestGuard (121-129)
  • SetupCompleteGuard (137-171)
web/src/api/endpoints/auth.ts (1)
  • getMe (27-30)
web/src/api/endpoints/setup.ts (1)
  • getSetupStatus (19-22)
web/src/pages/BudgetForecastPage.tsx (1)
web/src/components/layout/PlaceholderPage.tsx (1)
  • PlaceholderPage (2-8)
web/src/router/guards.tsx (3)
web/src/stores/auth.ts (2)
  • useIsAuthenticated (214-214)
  • useAuthStore (61-210)
web/src/router/routes.ts (1)
  • ROUTES (7-28)
web/src/stores/setup.ts (2)
  • useSetupStore (13-30)
  • fetchSetupStatus (18-29)
🔇 Additional comments (62)
web/src/components/layout/PlaceholderPage.tsx (1)

1-8: Reusable placeholder component looks good.

Nice extraction for page stubs; typed props and markup are clean.

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

1-5: Task detail stub is implemented cleanly.

This matches the shared placeholder pattern and keeps routing modules consistent.

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

1-5: Settings stub follows the shared pattern well.

No concerns here.

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

1-5: Meeting detail stub is good to merge.

Implementation is consistent and clear.

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

1-5: Nice follow-through on shared placeholder usage.

This is aligned with the extracted PlaceholderPage pattern and removes previous duplication concerns.

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

1-5: Provider detail stub looks correct.

No issues in this segment.

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

1-5: Dashboard stub is clean and consistent.

Looks good.

web/.npmrc (1)

1-3: [Your rewritten review comment text here]
[Exactly ONE classification tag]

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

1-5: LGTM!

Clean placeholder page following the established pattern for route components.

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

1-5: LGTM!

Consistent placeholder page implementation.

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

1-5: LGTM!

Consistent placeholder page implementation.

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

1-5: LGTM!

Consistent placeholder page implementation.

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

1-5: LGTM!

Consistent placeholder page implementation.

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

1-14: LGTM!

Well-structured 404 page with proper semantic markup, accessible heading hierarchy, and correct use of the asChild pattern for the Button-as-Link composition.

docker/web/Dockerfile (1)

12-12: LGTM!

Including .npmrc in the build stage ensures consistent npm configuration (legacy-peer-deps=true) across Docker builds, CI, and local development.

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

1-5: LGTM!

Simple placeholder page following the established pattern used by other route modules. The import path and component usage are correct.

web/.storybook/main.ts (1)

1-18: LGTM!

Storybook configuration is well-structured. The viteFinal hook correctly integrates Tailwind CSS 4 via dynamic import, and the nullish coalescing safely handles potentially undefined plugins.

web/.storybook/preview.tsx (1)

1-20: LGTM!

Preview configuration correctly imports global styles and applies dark theme. The decorator ensures consistent Tailwind styling for all stories.

web/package.json (2)

13-15: LGTM!

Storybook scripts are correctly configured. The dev server runs on the standard port 6006, and the build output directory matches the .gitignore entry.


42-63: LGTM!

All Storybook packages are now consistently versioned at ^8.6.18, addressing the previous review feedback about version alignment.

.gitignore (1)

54-54: LGTM!

Correctly ignores Storybook's static build output directory, consistent with the storybook:build script's output path.

.github/workflows/ci.yml (2)

310-331: LGTM!

The new Storybook build job follows the established pattern for dashboard CI jobs. The --disable-telemetry flag is a good practice for CI environments, and the 15-minute timeout is appropriate.


337-358: LGTM!

The ci-pass gate correctly includes dashboard-storybook-build in both the needs array and the RESULTS aggregation, ensuring Storybook build failures will block the CI gate.

CLAUDE.md (4)

56-57: LGTM!

Storybook commands are correctly documented with accurate ports and script names matching package.json.


126-134: LGTM!

Package structure documentation accurately reflects the new pages/ and router/ directories, and the updated components/layout/ description.


237-237: LGTM!

CI jobs documentation correctly includes the new storybook-build step.


257-257: LGTM!

Dependencies list correctly includes Storybook among the web dashboard dependencies.

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

1-30: LGTM!

Well-structured Zustand store with proper loading guards and error handling. The error path correctly resets setupComplete to null (addressing previous review feedback), ensuring guards see an unknown state and can show retry UI.

web/src/__tests__/test-utils.tsx (1)

1-36: LGTM!

Well-structured test utilities for React Router v7. The renderWithRouter helper with a catch-all route is ideal for component-level tests, while renderRoutes provides flexibility for testing full route configurations. Exposing the router instance enables route state assertions in tests.

web/src/components/ui/button.stories.tsx (1)

1-70: LGTM!

Comprehensive Storybook stories covering all button variants and sizes. The satisfies Meta<typeof Button> pattern ensures type safety. The AllVariants story provides a useful side-by-side comparison for visual review.

web/src/components/layout/SidebarNavItem.tsx (1)

15-62: LGTM!

The component properly handles:

  • Active state styling via NavLink's isActive callback
  • Collapsed mode with icon-only display and title tooltip for accessibility
  • Badge capping at 99+ for readability
  • Proper aria-hidden attributes on decorative elements
web/src/pages/ApprovalsPage.tsx (1)

1-5: LGTM!

Simple placeholder page following the established pattern. Default export is appropriate for lazy-loaded routes.

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

1-5: LGTM!

Consistent placeholder page implementation.

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

1-5: LGTM!

Appropriate placeholder for the dynamic settings route. When implementing the actual feature, you'll want to use useParams() to display the actual namespace.

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

1-5: LGTM!

Consistent placeholder implementation for the budget forecast route.

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

1-5: LGTM!

Consistent placeholder implementation for the budget route.

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

1-7: Clean setup-page scaffold and styling baseline.

This placeholder implementation is clear and suitable for the guarded setup flow.

web/src/App.tsx (1)

1-5: Router handoff in App is correct and minimal.

Good replacement of static content with the router entrypoint.

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

1-7: Login page scaffold looks good for initial guarded routing.

No correctness or accessibility blockers in this segment.

web/src/components/layout/AppLayout.tsx (2)

6-12: Accessible loading fallback is implemented correctly.

role="status" plus aria-live="polite" is a solid fix for suspense loading announcements.


14-28: App shell composition is solid.

The layout cleanly separates status, navigation, and routed content with proper overflow handling.

web/src/components/layout/StatusBar.tsx (1)

3-69: Status bar implementation is clean and well-factored.

Good extraction of primitives (Divider, Dot, StatusItem) and consistent styling/accessibility treatment.

web/src/__tests__/components/layout/SidebarNavItem.test.tsx (1)

6-75: Strong behavioral coverage for SidebarNavItem.

These test cases capture the critical collapsed/expanded, badge, and dot branches well.

web/src/__tests__/stores/setup.test.ts (1)

15-132: Setup store tests are comprehensive and high-value.

Great coverage of success, failure, retry, and concurrency behavior.

web/src/router/index.tsx (1)

1-40: LGTM!

The lazy-loading setup, SuspenseWrapper component, and imports are well-structured. The guard composition (GuestGuard for login, SetupCompleteGuard for setup, AuthGuard > SetupGuard for protected routes) correctly implements the specified access control logic.

web/src/__tests__/components/layout/Sidebar.test.tsx (2)

121-137: LGTM!

The logout test correctly sets up the spy before rendering, addressing the previous review feedback. The useAuthStore.setState() call includes all required state fields plus the logout spy, ensuring the component captures the mocked function during render.


41-46: LGTM!

Test isolation is properly implemented with resetStore(), localStorage.clear(), and vi.clearAllMocks() in beforeEach. This prevents state leakage between tests.

web/src/__tests__/App.test.tsx (2)

35-49: LGTM!

The beforeEach properly resets both stores including error: false for the setup store, addressing the previous review feedback. Test isolation is complete with localStorage.clear() and vi.clearAllMocks().


51-76: LGTM!

The tests now use semantic, role-based queries (getByRole('heading', { name: /login/i }), getByRole('navigation', { name: /main navigation/i }), getByRole('main')) instead of fragile text matching and exact counts. This addresses the previous review feedback and makes tests more resilient to UI changes.

web/src/router/routes.ts (1)

1-35: LGTM!

The centralized route constants provide a clean single source of truth. PUBLIC_ROUTES correctly includes all unauthenticated paths (LOGIN, SETUP, SETUP_STEP), addressing the previous review feedback. The as const assertion ensures proper literal type inference.

web/src/components/layout/Sidebar.tsx (4)

33-47: LGTM!

The readCollapsed and writeCollapsed helpers properly guard localStorage access with try-catch blocks, preventing runtime crashes in restricted browser contexts (private browsing, quota exceeded, etc.). This addresses the previous review feedback.


162-176: LGTM!

The button styling uses the extracted SIDEBAR_BUTTON_CLASS constant, keeping JSX clean and addressing the previous line-length feedback. The collapse toggle has proper title and aria-label attributes for accessibility.


232-243: LGTM!

The logout button includes both title and aria-label="Logout" for accessibility, and the icon has aria-hidden="true". This addresses the previous review feedback about screen reader support.


71-75: No changes needed — "SynthOrg" is the project's own product name.

"SynthOrg" is the defined product name for this framework (visible in web/src/utils/constants.ts as APP_NAME, in the README as "What is SynthOrg?", and in the package name). The vendor-agnostic naming rule applies to third-party LLM vendors (Anthropic, OpenAI, Claude, GPT) that should be abstracted, not to the project's own product branding. The current usage is appropriate.

			> Likely an incorrect or invalid review comment.
web/src/router/guards.tsx (4)

50-83: LGTM!

The AuthGuard now implements fail-closed behavior correctly: it tracks a validating state initialized based on whether we have a token but no user (page refresh scenario), shows FullScreenLoading while validation is in progress, and only renders protected content after fetchUser() completes. This addresses the previous review feedback about flashing protected UI before token validation.


7-42: LGTM!

The shared FullScreenLoading and FullScreenError components reduce duplication across guards and include proper accessibility attributes (role="status", aria-live="polite", role="alert"). This addresses the previous review feedback about extracting shared components.


91-116: LGTM!

SetupGuard correctly handles the three states: loading (shows spinner), error (shows retry UI), and resolved (redirects to setup or renders children). The effect properly guards against redundant fetches with the !loading && !error conditions.


137-171: LGTM!

SetupCompleteGuard implements fail-closed security: authenticated users are redirected to dashboard on fetch errors rather than being allowed setup access. Unauthenticated users pass through (setup page handles its own auth flow). This is the correct security posture for protecting the setup wizard.

web/src/__tests__/router/guards.test.tsx (4)

131-155: LGTM!

The "fails open on non-401 errors" test documents the intentional design decision that transient network failures don't block authenticated users. This addresses the previous review suggestion and clarifies the guard's degraded-state behavior.


256-297: LGTM!

The retry interaction test validates the full error-recovery flow: initial failure shows error UI, clicking retry triggers a second fetch, and success renders the protected content. The assertion expect(mockGetSetupStatus).toHaveBeenCalledTimes(2) confirms the retry actually invoked the API. This addresses the previous review suggestion.


1-54: LGTM!

Test setup is well-organized with proper mocks for APIs, window.location override to prevent navigation side effects, and a resetStores helper that fully resets both auth and setup stores including the error flag.


454-481: LGTM!

The fail-closed test for SetupCompleteGuard correctly verifies that authenticated users are redirected to dashboard when the setup status fetch fails, rather than being allowed access to the setup wizard. This is critical security behavior.

label: string
collapsed: boolean
badge?: number
dotColor?: string
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 constraining dotColor to known color classes.

The dotColor prop accepts any string, which is flexible but could lead to invalid class names being passed. If there's a fixed set of status colors used in the app, consider using a union type.

dotColor?: 'bg-success-500' | 'bg-warning-500' | 'bg-danger-500' | string

This is a minor observation—the current implementation works correctly.

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

In `@web/src/components/layout/SidebarNavItem.tsx` at line 11, The dotColor prop
on SidebarNavItem is overly permissive (string); constrain it to a union of
known CSS class names to prevent invalid classes being passed: update the
dotColor type declaration in SidebarNavItem (prop/interface/type for
SidebarNavItem or SidebarNavItemProps) to something like 'bg-success-500' |
'bg-warning-500' | 'bg-danger-500' | string so callers can use the known set
while retaining a fallback string; adjust any usages or prop defaulting in the
SidebarNavItem component to match the new union type.

Comment on lines +88 to +106
children: [
{ index: true, element: <DashboardPage /> },
{ path: 'org', element: <OrgChartPage /> },
{ path: 'org/edit', element: <OrgEditPage /> },
{ path: 'tasks', element: <TaskBoardPage /> },
{ path: 'tasks/:taskId', element: <TaskDetailPage /> },
{ path: 'budget', element: <BudgetPage /> },
{ path: 'budget/forecast', element: <BudgetForecastPage /> },
{ path: 'approvals', element: <ApprovalsPage /> },
{ path: 'agents', element: <AgentsPage /> },
{ path: 'agents/:agentName', element: <AgentDetailPage /> },
{ path: 'messages', element: <MessagesPage /> },
{ path: 'meetings', element: <MeetingsPage /> },
{ path: 'meetings/:meetingId', element: <MeetingDetailPage /> },
{ path: 'providers', element: <ProvidersPage /> },
{ path: 'providers/:providerName', element: <ProviderDetailPage /> },
{ path: 'settings', element: <SettingsPage /> },
{ path: 'settings/:namespace', element: <SettingsNamespacePage /> },
{ path: '*', element: <NotFoundPage /> },
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Lazy-loaded page components inside protected routes lack individual Suspense boundaries.

The page components (DashboardPage, OrgChartPage, etc.) are lazy-loaded but rendered directly without SuspenseWrapper. When navigating between routes, if the new page chunk hasn't loaded yet, React will throw because there's no Suspense boundary catching the lazy component's promise.

The AppLayout wrapper has Suspense (lines 84-86), but once it's mounted, navigating to a child route won't trigger that boundary again—the child lazy component needs its own.

🛠️ Proposed fix: wrap each lazy page in SuspenseWrapper
             children: [
-              { index: true, element: <DashboardPage /> },
-              { path: 'org', element: <OrgChartPage /> },
-              { path: 'org/edit', element: <OrgEditPage /> },
-              { path: 'tasks', element: <TaskBoardPage /> },
+              { index: true, element: <SuspenseWrapper><DashboardPage /></SuspenseWrapper> },
+              { path: 'org', element: <SuspenseWrapper><OrgChartPage /></SuspenseWrapper> },
+              { path: 'org/edit', element: <SuspenseWrapper><OrgEditPage /></SuspenseWrapper> },
+              { path: 'tasks', element: <SuspenseWrapper><TaskBoardPage /></SuspenseWrapper> },
               // ... apply to all lazy-loaded pages
📝 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
children: [
{ index: true, element: <DashboardPage /> },
{ path: 'org', element: <OrgChartPage /> },
{ path: 'org/edit', element: <OrgEditPage /> },
{ path: 'tasks', element: <TaskBoardPage /> },
{ path: 'tasks/:taskId', element: <TaskDetailPage /> },
{ path: 'budget', element: <BudgetPage /> },
{ path: 'budget/forecast', element: <BudgetForecastPage /> },
{ path: 'approvals', element: <ApprovalsPage /> },
{ path: 'agents', element: <AgentsPage /> },
{ path: 'agents/:agentName', element: <AgentDetailPage /> },
{ path: 'messages', element: <MessagesPage /> },
{ path: 'meetings', element: <MeetingsPage /> },
{ path: 'meetings/:meetingId', element: <MeetingDetailPage /> },
{ path: 'providers', element: <ProvidersPage /> },
{ path: 'providers/:providerName', element: <ProviderDetailPage /> },
{ path: 'settings', element: <SettingsPage /> },
{ path: 'settings/:namespace', element: <SettingsNamespacePage /> },
{ path: '*', element: <NotFoundPage /> },
children: [
{ index: true, element: <SuspenseWrapper><DashboardPage /></SuspenseWrapper> },
{ path: 'org', element: <SuspenseWrapper><OrgChartPage /></SuspenseWrapper> },
{ path: 'org/edit', element: <SuspenseWrapper><OrgEditPage /></SuspenseWrapper> },
{ path: 'tasks', element: <SuspenseWrapper><TaskBoardPage /></SuspenseWrapper> },
{ path: 'tasks/:taskId', element: <SuspenseWrapper><TaskDetailPage /></SuspenseWrapper> },
{ path: 'budget', element: <SuspenseWrapper><BudgetPage /></SuspenseWrapper> },
{ path: 'budget/forecast', element: <SuspenseWrapper><BudgetForecastPage /></SuspenseWrapper> },
{ path: 'approvals', element: <SuspenseWrapper><ApprovalsPage /></SuspenseWrapper> },
{ path: 'agents', element: <SuspenseWrapper><AgentsPage /></SuspenseWrapper> },
{ path: 'agents/:agentName', element: <SuspenseWrapper><AgentDetailPage /></SuspenseWrapper> },
{ path: 'messages', element: <SuspenseWrapper><MessagesPage /></SuspenseWrapper> },
{ path: 'meetings', element: <SuspenseWrapper><MeetingsPage /></SuspenseWrapper> },
{ path: 'meetings/:meetingId', element: <SuspenseWrapper><MeetingDetailPage /></SuspenseWrapper> },
{ path: 'providers', element: <SuspenseWrapper><ProvidersPage /></SuspenseWrapper> },
{ path: 'providers/:providerName', element: <SuspenseWrapper><ProviderDetailPage /></SuspenseWrapper> },
{ path: 'settings', element: <SuspenseWrapper><SettingsPage /></SuspenseWrapper> },
{ path: 'settings/:namespace', element: <SuspenseWrapper><SettingsNamespacePage /></SuspenseWrapper> },
{ path: '*', element: <NotFoundPage /> },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/router/index.tsx` around lines 88 - 106, The child routes list mounts
lazy-loaded components (e.g., DashboardPage, OrgChartPage, TaskBoardPage,
AgentDetailPage, etc.) without per-route Suspense boundaries, causing errors
when navigating to a not-yet-loaded chunk; wrap each route's element with the
project's SuspenseWrapper (or equivalent) so each lazy page has its own suspense
boundary (e.g., replace element: <DashboardPage /> with element:
<SuspenseWrapper><DashboardPage /></SuspenseWrapper> for all entries) while
keeping AppLayout intact.

{ path: 'providers/:providerName', element: <ProviderDetailPage /> },
{ path: 'settings', element: <SettingsPage /> },
{ path: 'settings/:namespace', element: <SettingsNamespacePage /> },
{ path: '*', element: <NotFoundPage /> },
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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if there's a top-level 404 route or if this is the only one
rg -n "NotFoundPage|path:\s*['\"]?\*" web/src/router/

Repository: Aureliolo/synthorg

Length of output: 238


🏁 Script executed:

head -120 web/src/router/index.tsx | tail -50

Repository: Aureliolo/synthorg

Length of output: 1872


🏁 Script executed:

head -80 web/src/router/index.tsx

Repository: Aureliolo/synthorg

Length of output: 2750


🏁 Script executed:

find web/src/router -name "*guard*" -o -name "*Guard*" | head -20

Repository: Aureliolo/synthorg

Length of output: 87


🏁 Script executed:

cat -n web/src/router/guards.tsx

Repository: Aureliolo/synthorg

Length of output: 6424


Catch-all 404 route is nested inside protected routes—unauthenticated users won't see it.

The * route is a child of the AuthGuard/SetupGuard protected tree. If an unauthenticated user visits /nonexistent, they'll be redirected to /login rather than seeing the 404 page.

If this is intentional (only show 404 after authentication), the current structure is correct. Otherwise, consider adding a top-level catch-all outside the protected tree.

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

In `@web/src/router/index.tsx` at line 106, The catch-all route "{ path: '*',
element: <NotFoundPage /> }" is currently nested under the protected tree
(AuthGuard/SetupGuard), so unauthenticated users hit the login redirect instead
of the 404; move the wildcard route out of the AuthGuard/SetupGuard children to
the top-level routes array (or add an additional top-level fallback route) so
NotFoundPage is matched for unknown paths before or outside the protected
wrapper, ensuring the wildcard is not a child of the protected route tree.

Comment on lines 139 to 149
// Save return path so LoginPage can redirect back after re-authentication.
// sessionStorage is same-origin only, preventing open redirect attacks.
const currentPath = window.location.pathname
if (currentPath !== '/login' && currentPath !== '/setup') {
sessionStorage.setItem('auth_return_path', currentPath)
}
// Hard redirect to login -- intentionally uses window.location (not react-router)
// because this runs in a Zustand store outside the React tree.
if (currentPath !== '/login' && currentPath !== '/setup') {
window.location.href = '/login'
}
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 consolidating the duplicate condition check.

The same condition currentPath !== '/login' && currentPath !== '/setup' is checked twice (lines 142 and 147). These can be combined into a single block.

♻️ Suggested consolidation
       // Save return path so LoginPage can redirect back after re-authentication.
       // sessionStorage is same-origin only, preventing open redirect attacks.
       const currentPath = window.location.pathname
       if (currentPath !== '/login' && currentPath !== '/setup') {
         sessionStorage.setItem('auth_return_path', currentPath)
-      }
-      // Hard redirect to login -- intentionally uses window.location (not react-router)
-      // because this runs in a Zustand store outside the React tree.
-      if (currentPath !== '/login' && currentPath !== '/setup') {
+        // Hard redirect to login -- intentionally uses window.location (not react-router)
+        // because this runs in a Zustand store outside the React tree.
         window.location.href = '/login'
       }
📝 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
// Save return path so LoginPage can redirect back after re-authentication.
// sessionStorage is same-origin only, preventing open redirect attacks.
const currentPath = window.location.pathname
if (currentPath !== '/login' && currentPath !== '/setup') {
sessionStorage.setItem('auth_return_path', currentPath)
}
// Hard redirect to login -- intentionally uses window.location (not react-router)
// because this runs in a Zustand store outside the React tree.
if (currentPath !== '/login' && currentPath !== '/setup') {
window.location.href = '/login'
}
// Save return path so LoginPage can redirect back after re-authentication.
// sessionStorage is same-origin only, preventing open redirect attacks.
const currentPath = window.location.pathname
if (currentPath !== '/login' && currentPath !== '/setup') {
sessionStorage.setItem('auth_return_path', currentPath)
// Hard redirect to login -- intentionally uses window.location (not react-router)
// because this runs in a Zustand store outside the React tree.
window.location.href = '/login'
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/stores/auth.ts` around lines 139 - 149, The duplicate checks using
the same condition on currentPath should be consolidated: compute the condition
once (e.g., a boolean like isNotAuthOrSetup based on currentPath !== '/login' &&
currentPath !== '/setup') and wrap both
sessionStorage.setItem('auth_return_path', currentPath) and the hard redirect
window.location.href = '/login' inside a single if block (or use the boolean to
gate both actions) in the auth store where currentPath, sessionStorage.setItem
and window.location.href are used.

Comment on lines +139 to +144
// Save return path so LoginPage can redirect back after re-authentication.
// sessionStorage is same-origin only, preventing open redirect attacks.
const currentPath = window.location.pathname
if (currentPath !== '/login' && currentPath !== '/setup') {
sessionStorage.setItem('auth_return_path', currentPath)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Verify that LoginPage reads auth_return_path from sessionStorage
# Expected: LoginPage should contain logic to read and redirect to auth_return_path

# Search for auth_return_path usage in the codebase
rg -n 'auth_return_path' --type ts --type tsx -g '!*.test.*' -g '!*.spec.*'

# Search specifically in LoginPage if it exists
fd -e tsx -e ts 'LoginPage' --exec cat {}

Repository: Aureliolo/synthorg

Length of output: 321


LoginPage must implement redirect logic using stored auth_return_path.

The code stores the return path in sessionStorage (lines 139-144) with a comment indicating "LoginPage can redirect back after re-authentication," but the LoginPage component does not read or use this value. Implement logic in LoginPage to retrieve auth_return_path from sessionStorage and redirect to it after successful authentication.

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

In `@web/src/stores/auth.ts` around lines 139 - 144, LoginPage currently doesn't
consume the stored sessionStorage key auth_return_path; update the LoginPage
component (e.g., in its successful authentication handler such as
handleLogin/onAuthenticated or the place that sets the authenticated user and
does navigation) to read sessionStorage.getItem('auth_return_path'), validate
it's a same-origin path (non-empty and starts with '/'), perform a redirect to
that path (fallback to '/' or dashboard if absent/invalid), and then remove the
key (sessionStorage.removeItem('auth_return_path')) so it won't be reused;
ensure this logic runs only after successful authentication and preserves
existing post-login flows.

- Export STORAGE_KEY from Sidebar, import in stories (DRY)
- Consolidate duplicate pathname check in clearAuth into single block

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

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/src/stores/auth.ts`:
- Around line 139-147: LoginPage currently doesn't consume the saved
auth_return_path from sessionStorage; update the LoginPage component (the login
success handler, e.g., onLoginSuccess or handleSubmit in LoginPage) to read
sessionStorage.getItem('auth_return_path') after successful authentication,
clear it via sessionStorage.removeItem('auth_return_path'), then navigate to
that path (or fallback to a default route like '/') using the existing
navigation mechanism (e.g., window.location.href or react-router history/push
depending on how LoginPage handles redirects). Ensure the code only reads/clears
the key after a confirmed successful login to preserve existing behavior.
🪄 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: fe28d066-9b43-462e-9b0a-2f789add1b04

📥 Commits

Reviewing files that changed from the base of the PR and between 947fd7f and 9e00e7c.

📒 Files selected for processing (3)
  • web/src/components/layout/Sidebar.stories.tsx
  • web/src/components/layout/Sidebar.tsx
  • web/src/stores/auth.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build Backend
  • GitHub Check: Build Sandbox
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (2)
web/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

ESLint linting is required for React/TypeScript web dashboard code via npm --prefix web run lint.

Files:

  • web/src/stores/auth.ts
  • web/src/components/layout/Sidebar.stories.tsx
  • web/src/components/layout/Sidebar.tsx
web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

TypeScript type checking is required for web dashboard via npm --prefix web run type-check.

Files:

  • web/src/stores/auth.ts
  • web/src/components/layout/Sidebar.stories.tsx
  • web/src/components/layout/Sidebar.tsx
🧠 Learnings (5)
📓 Common learnings
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.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T22:02:25.937Z
Learning: Applies to web/package.json : Web dashboard uses Node.js 22+. Key dependencies: React 19, react-router, shadcn/ui, Radix UI, Tailwind CSS 4, Zustand, tanstack/react-query, xyflow/react, Recharts, Framer Motion, cmdk, Axios, Lucide React, Vitest.
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).
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)
📚 Learning: 2026-03-15T11:48:14.867Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T11:48:14.867Z
Learning: Applies to **/*.{py,md} : Line length: 88 characters (ruff).

Applied to files:

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

Applied to files:

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

Applied to files:

  • web/src/components/layout/Sidebar.tsx
📚 Learning: 2026-03-24T22:02:25.937Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T22:02:25.937Z
Learning: Applies to +(src|tests)/**/*.py : NEVER use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, tests, or config examples. Use generic names: `example-provider`, `example-large-001`, `example-medium-001`, `example-small-001`, `large`/`medium`/`small` as aliases. Vendor names may only appear in: (1) `docs/design/operations.md` provider list, (2) `.claude/` skill/agent files, (3) third-party import paths/module names (e.g. `litellm.types.llms.openai`).

Applied to files:

  • web/src/components/layout/Sidebar.tsx
🧬 Code graph analysis (2)
web/src/components/layout/Sidebar.stories.tsx (1)
web/src/components/layout/Sidebar.tsx (2)
  • Sidebar (49-250)
  • STORAGE_KEY (25-25)
web/src/components/layout/Sidebar.tsx (4)
web/src/hooks/useAuth.ts (1)
  • useAuth (10-31)
web/src/stores/auth.ts (2)
  • logout (204-206)
  • useAuthStore (61-208)
web/src/components/layout/SidebarNavItem.tsx (1)
  • SidebarNavItem (15-63)
web/src/router/routes.ts (1)
  • ROUTES (7-28)
🔇 Additional comments (8)
web/src/components/layout/Sidebar.tsx (5)

71-75: Clarification needed on "SynthOrg" branding.

A past review flagged this as a vendor name violation. However, reviewing the learnings more carefully, they prohibit third-party AI vendor names like "Anthropic, OpenAI, Claude, GPT" to keep code vendor-agnostic for LLM providers.

"SynthOrg" appears to be the product/project name itself (per the repository name Aureliolo/synthorg), not a third-party vendor reference. If this is indeed the product's own branding, the guideline likely doesn't apply here.

Please confirm whether "SynthOrg" is the intended product name or if it should be replaced with a generic placeholder per project policy.


33-47: LGTM on localStorage hardening.

The readCollapsed and writeCollapsed helper functions properly wrap localStorage access in try/catch blocks, preventing runtime crashes in restricted browser contexts. This addresses the concern from the previous review.


49-60: LGTM on state management.

The collapsed state initialization and toggleCollapse handler are well-implemented:

  • Lazy initialization via readCollapsed function
  • Functional state update pattern ensures correct toggling
  • Storage persistence happens synchronously with state update

78-156: LGTM on navigation structure.

The navigation section is well-organized with:

  • Proper semantic markup (nav with aria-label)
  • Consistent use of ROUTES constants from centralized config
  • Conditional rendering of section headings when expanded
  • Correct prop passing to SidebarNavItem components

158-248: LGTM on bottom section accessibility and styling.

All accessibility concerns from previous reviews have been addressed:

  • Collapse toggle, notifications, and logout buttons all have proper aria-label attributes
  • Connection status indicator has an accessible label
  • SIDEBAR_BUTTON_CLASS constant consolidates shared button styles, keeping lines under the length limit
web/src/components/layout/Sidebar.stories.tsx (3)

1-5: LGTM on imports and STORAGE_KEY reuse.

The import of STORAGE_KEY from Sidebar.tsx addresses the previous review comment about extracting the duplicated 'sidebar_collapsed' string into a shared constant.


6-36: LGTM on story meta configuration.

The meta configuration properly:

  • Sets up mock auth state for the user display section
  • Wraps stories in MemoryRouter for routing context (required by NavLink in SidebarNavItem)
  • Uses layout: 'fullscreen' for proper sidebar rendering

38-54: LGTM on story variants.

Both Expanded and Collapsed stories correctly use decorators to set localStorage before the component renders. This addresses the previous review comments about:

  • Using decorators instead of play hooks for initial state
  • Removing redundant play hooks
  • Using the shared STORAGE_KEY constant

Comment on lines +139 to 147
// Hard redirect to login -- intentionally uses window.location (not
// react-router) because this runs in a Zustand store outside the
// React tree. Saves return path so LoginPage can redirect back
// after re-authentication (sessionStorage is same-origin only).
const currentPath = window.location.pathname
if (currentPath !== '/login' && currentPath !== '/setup') {
sessionStorage.setItem('auth_return_path', currentPath)
window.location.href = '/login'
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify LoginPage implements redirect logic using auth_return_path
rg -n 'auth_return_path' --type ts --type tsx -g '!*.test.*' -g '!*.spec.*' || echo "No usages found"

# Check LoginPage for sessionStorage access
fd -e tsx 'LoginPage' --exec rg -n 'sessionStorage' {} || echo "LoginPage does not access sessionStorage"

Repository: Aureliolo/synthorg

Length of output: 163


🏁 Script executed:

#!/bin/bash
# Broader search for auth_return_path without type restrictions
rg -n 'auth_return_path' . --type-list | head -20
echo "---"
rg -n 'auth_return_path' .

# Find and inspect LoginPage file
echo "---"
fd -type f 'LoginPage' .
fd -type f -i 'login' . | grep -i page

Repository: Aureliolo/synthorg

Length of output: 911


🏁 Script executed:

#!/bin/bash
# Find LoginPage file more broadly
find . -type f -name "*ogin*" | grep -v node_modules | grep -v ".git"
echo "---"
# Also check for any file with 'Login' in name
find . -type f \( -name "*.tsx" -o -name "*.ts" -o -name "*.jsx" -o -name "*.js" \) | xargs grep -l "LoginPage\|login.*page\|export.*Login" 2>/dev/null | head -20

Repository: Aureliolo/synthorg

Length of output: 352


🏁 Script executed:

#!/bin/bash
# Inspect LoginPage implementation
wc -l ./web/src/pages/LoginPage.tsx
echo "---"
cat -n ./web/src/pages/LoginPage.tsx

Repository: Aureliolo/synthorg

Length of output: 361


LoginPage implementation is incomplete—it must retrieve and use auth_return_path to redirect after authentication.

The consolidation of the currentPath check (lines 143-147) correctly fixes the duplicate condition. However, LoginPage is currently a stub that does not consume the stored auth_return_path. The feature does not work end-to-end: the return path is stored in sessionStorage but never retrieved or used for redirection.

LoginPage must be updated to:

  • Retrieve auth_return_path from sessionStorage after successful authentication
  • Clear it from sessionStorage
  • Redirect the user to that path (or a default page if not set)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/stores/auth.ts` around lines 139 - 147, LoginPage currently doesn't
consume the saved auth_return_path from sessionStorage; update the LoginPage
component (the login success handler, e.g., onLoginSuccess or handleSubmit in
LoginPage) to read sessionStorage.getItem('auth_return_path') after successful
authentication, clear it via sessionStorage.removeItem('auth_return_path'), then
navigate to that path (or fallback to a default route like '/') using the
existing navigation mechanism (e.g., window.location.href or react-router
history/push depending on how LoginPage handles redirects). Ensure the code only
reads/clears the key after a confirmed successful login to preserve existing
behavior.

@Aureliolo Aureliolo merged commit d4dde90 into main Mar 24, 2026
30 checks passed
@Aureliolo Aureliolo deleted the feat/770-app-shell-storybook branch March 24, 2026 22:46
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: app shell, Storybook, and CI/CD pipeline

1 participant