fix: align frontend types with backend and debounce WS refetches#916
fix: align frontend types with backend and debounce WS refetches#916
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (3)
📜 Recent review details⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
🧰 Additional context used📓 Path-based instructions (8)web/src/**/*.{ts,tsx}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
web/src/**/*.{ts,tsx,css}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
**/*.{ts,tsx,json}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
web/**/*.stories.{ts,tsx}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
**/*.{py,ts,tsx,go}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
web/src/pages/**/*.{ts,tsx}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
docs/design/*.md📄 CodeRabbit inference engine (CLAUDE.md)
Files:
docs/**/*.md📄 CodeRabbit inference engine (CLAUDE.md)
Files:
🧠 Learnings (8)📓 Common learnings📚 Learning: 2026-03-29T11:41:44.483ZApplied to files:
📚 Learning: 2026-03-29T11:41:44.482ZApplied to files:
📚 Learning: 2026-03-29T11:41:44.483ZApplied to files:
📚 Learning: 2026-03-29T11:41:44.483ZApplied to files:
📚 Learning: 2026-03-29T11:41:44.483ZApplied to files:
📚 Learning: 2026-03-15T21:20:09.993ZApplied to files:
📚 Learning: 2026-03-20T08:28:32.845ZApplied to files:
🔇 Additional comments (10)
WalkthroughThis PR aligns frontend types and consumers with backend models and debounces WebSocket-triggered refetches. DepartmentHealth was reshaped to backend fields (department_name, utilization_percent, department_cost_7d, currency, active_agent_count, etc.). AgentConfig now uses untyped Record fields for sub-objects, makes id/status/hiring_date optional, removes skills, and adds authority. An ActivityEventType literal union and ACTIVITY_EVENT_TYPE_VALUES were added. DeptHealthBar dropped the taskCount prop and relabels its meter as utilization. Numerous tests, stories, fixtures, and consumers were updated to the new shapes and debounce behavior. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
There was a problem hiding this comment.
Code Review
This pull request refactors the AgentConfig and DepartmentHealth API types to align with the backend schema, transitioning several structured fields to generic Record<string, unknown> types and renaming health metrics to utilization. It introduces WebSocket debouncing in the useAgentsData and useAgentDetailData hooks to coalesce burst events and prevent excessive refetching. Additionally, the UI components and test suites have been updated to handle optional agent fields and the new data structures. Feedback focuses on improving type safety when accessing properties on the now-generic tools and model objects by using bracket notation and explicit type checks.
web/src/pages/AgentDetailPage.tsx
Outdated
|
|
||
| <ErrorBoundary level="section"> | ||
| <ToolBadges tools={[...agent.tools.allowed]} /> | ||
| <ToolBadges tools={Array.isArray(agent.tools.allowed) ? (agent.tools.allowed as unknown[]).filter((t): t is string => typeof t === 'string') : []} /> |
There was a problem hiding this comment.
While this defensive check is great, accessing properties on a Record<string, unknown> using dot notation isn't type-safe and can be flagged by stricter TypeScript rules. Using bracket notation (['allowed']) is safer and more explicit about accessing a dynamic property.
| <ToolBadges tools={Array.isArray(agent.tools.allowed) ? (agent.tools.allowed as unknown[]).filter((t): t is string => typeof t === 'string') : []} /> | |
| <ToolBadges tools={Array.isArray(agent.tools['allowed']) ? (agent.tools['allowed'] as unknown[]).filter((t): t is string => typeof t === 'string') : []} /> |
| <div className="border-t border-border pt-4 space-y-2"> | ||
| <p className="text-xs font-semibold uppercase tracking-wider text-text-muted">Model</p> | ||
| <p className="text-xs text-text-secondary font-mono">{agent.model.provider} / {agent.model.model_id}</p> | ||
| <p className="text-xs text-text-secondary font-mono">{String(agent.model.provider ?? '')} / {String(agent.model.model_id ?? '')}</p> |
There was a problem hiding this comment.
This is a good defensive measure. However, using dot notation on a Record<string, unknown> is not type-safe. It's better to use bracket notation. Also, String(value) can produce "[object Object]" if value is an object. A typeof check would be more robust.
| <p className="text-xs text-text-secondary font-mono">{String(agent.model.provider ?? '')} / {String(agent.model.model_id ?? '')}</p> | |
| <p className="text-xs text-text-secondary font-mono">{(typeof agent.model['provider'] === 'string' ? agent.model['provider'] : '')} / {(typeof agent.model['model_id'] === 'string' ? agent.model['model_id'] : '')}</p> |
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Snapshot WarningsEnsure that dependencies are being submitted on PR branches. Re-running this action after a short time may resolve the issue. See the documentation for more information and troubleshooting advice. Scanned FilesNone |
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
web/src/hooks/useAgentDetailData.ts (1)
96-116:⚠️ Potential issue | 🟠 MajorHandle
agentNametransitions in the WS lifecycle.
useWebSocketonly re-runs its setup whenenabledchanges; it intentionally ignores laterbindingschanges. With the currentagentName ? ... : EMPTY_BINDINGSbranch, mounting this hook with an empty name never installs handlers when the name appears later, and switching back to an empty name leaves the old handlers live until unmount. Because the timer is also only cleared on unmount, a queued callback can still fire after navigation and callfetchAgentDetail('').Please tie the socket lifecycle to
agentNameand clear any pending debounce whenagentNamechanges, not just on unmount. A rerender test for'' -> 'alice' -> ''would lock this down.🔧 Minimal fix
- useEffect(() => () => { - if (wsDebounceRef.current) clearTimeout(wsDebounceRef.current) - }, []) + useEffect(() => { + if (!agentName && wsDebounceRef.current) { + clearTimeout(wsDebounceRef.current) + wsDebounceRef.current = null + } + + return () => { + if (wsDebounceRef.current) { + clearTimeout(wsDebounceRef.current) + wsDebounceRef.current = null + } + } + }, [agentName]) ... - const { connected: wsConnected, setupError: wsSetupError } = useWebSocket({ bindings }) + const { connected: wsConnected, setupError: wsSetupError } = useWebSocket({ + bindings, + enabled: Boolean(agentName), + })🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/hooks/useAgentDetailData.ts` around lines 96 - 116, The socket handlers currently never get installed/removed when agentName transitions because useWebSocket ignores later bindings changes; fix by tying the socket lifecycle to agentName and clearing pending debounce on name changes: pass enabled: Boolean(agentName) into useWebSocket({ bindings, enabled: !!agentName }) so the hook sets up/tears down when agentName changes, and add a useEffect that watches agentName and on change clears any pending timeout on wsDebounceRef.current (and optionally resets agentNameRef.current) to prevent fetchAgentDetail('') from firing; keep DETAIL_CHANNELS, bindings, wsDebounceRef, useEffect, useWebSocket, fetchAgentDetail and agentNameRef references to locate where to modify.web/src/pages/org-edit/AgentsTab.tsx (1)
168-177:⚠️ Potential issue | 🔴 CriticalBackend endpoint for reordering agents is not implemented.
The frontend code (lines 172-173) sends agent IDs to
POST /departments/{name}/reorder-agents, but this endpoint does not exist in the backend. The DepartmentController has only read-only endpoints (@get). This will cause the reorder operation to fail with a 404 error at runtime.Additionally, when
agent.idis undefined, the code falls back toagent.name, which may not be a valid agent identifier from the backend's perspective (the backend likely expects UUIDs or stable IDs, not human-readable names).Implement the missing
/departments/{name}/reorder-agentsendpoint in the backend, and ensure it validates that all agent identifiers inagent_idsare actual IDs, not names. Filter out or reject agents without valid IDs before processing the reorder request.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/pages/org-edit/AgentsTab.tsx` around lines 168 - 177, Add a POST handler on the backend to implement the /departments/{name}/reorder-agents endpoint: add a method (e.g., reorderAgents) to DepartmentController that accepts a body { agent_ids: string[] }, validates each entry is a valid agent ID (e.g., UUID format) and belongs to the given department, rejects with 400 if any ID is missing/invalid (do not accept names as IDs), and calls a service method (e.g., DepartmentService.reorderAgents(departmentName, agentIds)) to persist the new order; ensure the controller returns appropriate success/error responses and that DepartmentService updates the stored order for that department atomically.web/src/__tests__/stores/company.test.ts (1)
390-407:⚠️ Potential issue | 🟡 MinorAssertions still miss the optional-ID fallback path
agentIdsnow usea.id ?? a.name, but Lines [400] and [406] still asserta.idonly. This won’t validate reorder behavior for agents withoutid.🧪 Proposed test fix
- expect(reordered.map((a) => a.id)).toEqual(agentIds) + expect(reordered.map((a) => a.id ?? a.name)).toEqual(agentIds) ... - expect(restored.map((a) => a.id)).toEqual(agentIds.toReversed()) + expect(restored.map((a) => a.id ?? a.name)).toEqual(agentIds.toReversed())🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/__tests__/stores/company.test.ts` around lines 390 - 407, The assertions use a.id only and therefore miss agents that fallback to name; update both checks that currently call .map((a) => a.id) (the "reordered" and "restored" expectations) to use the same fallback logic as agentIds: .map(a => a.id ?? a.name). This keeps the assertions aligned with the agentIds variable and with the optimisticReorderAgents behavior; ensure the rollback expectation still compares to agentIds.toReversed() but using the same fallback mapping.web/src/pages/org/build-org-tree.ts (1)
23-30:⚠️ Potential issue | 🟠 MajorThread department currency through the org-chart node data.
DepartmentHealthnow carries per-departmentcurrency, but this payload only preservescost7d.web/src/pages/org/DepartmentGroupNode.tsxthen forwards no currency toDepartmentStatsBar, andweb/src/pages/org/DepartmentStatsBar.tsxfalls back that prop to"EUR". Any non-EUR department will therefore render the wrong cost label in the org chart.💱 Suggested change
export interface DepartmentGroupData { departmentName: DepartmentName displayName: string healthPercent: number | null agentCount: number activeCount: number cost7d: number | null + currency: string | null isDropTarget?: boolean [key: string]: unknown }data: { departmentName: dept.name, displayName: dept.display_name ?? dept.name, healthPercent: health?.utilization_percent ?? null, agentCount: deptMembers.length, activeCount, cost7d: health?.department_cost_7d ?? null, + currency: health?.currency ?? null, } satisfies DepartmentGroupData,Then pass
data.currency ?? undefinedfromweb/src/pages/org/DepartmentGroupNode.tsxintoDepartmentStatsBar.Also applies to: 105-112
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/pages/org/build-org-tree.ts` around lines 23 - 30, DepartmentGroupData currently only includes cost7d so the per-department currency from DepartmentHealth is dropped, causing DepartmentStatsBar to default to "EUR"; update the data flow to preserve and pass currency: add currency?: string to the DepartmentGroupData shape (referenced in web/src/pages/org/build-org-tree.ts), ensure DepartmentGroupNode (web/src/pages/org/DepartmentGroupNode.tsx) reads data.currency (from DepartmentHealth) and passes data.currency ?? undefined into the DepartmentStatsBar component, and update DepartmentStatsBar prop usage (web/src/pages/org/DepartmentStatsBar.tsx) to accept and use that currency prop instead of falling back to "EUR".
🤖 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__/pages/org-edit/DepartmentsTab.test.tsx`:
- Around line 11-14: The test fixture now uses the renamed field
utilization_percent but the test only counts meter elements, so add an assertion
that the rendered percentage (e.g., "72%") appears (or that the specific meter
element for makeDepartmentHealth('product') has value 72) to ensure the new
field is actually used; update the DepartmentsTab.test.tsx assertions to check
for the visible "72%" (or query the meter value for the product department) and
also ensure the department_name lookup still targets the correct department
entry produced by makeDepartmentHealth.
In `@web/src/__tests__/utils/dashboard.test.ts`:
- Around line 187-190: The helper dh currently casts the name parameter to
DepartmentHealth['department_name'] which weakens fixture typing; change the
function signature to accept name: DepartmentHealth['department_name'] (i.e.
function dh(name: DepartmentHealth['department_name'], utilization: number,
agents = 1)) and remove the inline cast in the returned object so the
department_name field is strongly typed from the parameter; keep the rest of the
object shape the same.
In `@web/src/components/ui/dept-health-bar.tsx`:
- Line 42: The component prop name 'health' in DeptHealthBar should be renamed
to a domain-accurate name (e.g., utilizationPercent) to match the aria-label
wording; update the DeptHealthBar component signature, its props interface/type,
any defaultProps or destructuring that uses 'health', and all call sites that
pass the prop to use utilizationPercent instead (or add a short-lived alias
mapping from health => utilizationPercent to preserve backwards compatibility
while you update callers); also update the aria-label usage at the line with
aria-label={`${name} utilization: ${clamped != null ? `${clamped}%` :
'unavailable'}`} to read the new prop name.
In `@web/src/hooks/useAgentsData.ts`:
- Line 9: The WS refetch debounce value and logic duplicated by WS_DEBOUNCE_MS
in useAgentsData.ts (and the identical timeout/reset logic in
useAgentDetailData.ts) should be centralized: extract the numeric debounce
constant and any small helper debounce/reset function into a shared module
(e.g., export WS_DEBOUNCE_MS and a debounceReset helper) and import those
symbols into useAgentsData.ts and useAgentDetailData.ts, replacing the local
WS_DEBOUNCE_MS and duplicated timeout/reset logic so both hooks use the single
shared source of truth for debounce timing and behavior.
In `@web/src/pages/org-edit/AgentEditDrawer.tsx`:
- Around line 153-156: The JSX currently stringifies agent.model fields directly
causing “[object Object]” or an unwanted " / " when values are missing; update
AgentEditDrawer to first extract agent.model.provider and agent.model.model_id
into a modelParts array that only keeps values where typeof === 'string' and
value.length > 0, then conditionally render the <p> element only when
modelParts.length > 0 and join the parts with ' / ' (use modelParts.join(' / '))
so the separator appears only between real string parts.
In `@web/src/pages/org/DepartmentStatsBar.tsx`:
- Line 24: The conditional rendering currently only checks cost7d !== null which
still allows NaN/Infinity to reach formatCurrency; update the guard in
DepartmentStatsBar (the JSX that renders StatPill with label "Cost (7d)") to
only render when Number.isFinite(cost7d) (or equivalent finite-number check)
before calling formatCurrency so StatPill receives a valid numeric value.
In `@web/src/stores/budget.ts`:
- Around line 125-127: The map keys for agentNameMap/agentDeptMap currently use
agent.id ?? agent.name which can mismatch lookups by r.agent_id; normalize and
populate keys so lookups succeed regardless of which identifier is used. Change
the logic in the block that sets agentNameMap and agentDeptMap (where agentId is
computed) to: compute a canonicalKey = String(agent.id ?? agent.name) and set
maps under that key, and if agent.id exists also set the maps under
String(agent.id) and String(agent.name) (or vice‑versa) so lookups by r.agent_id
or by agent.name both resolve; ensure you use the same normalization
(String(...)) as used when reading r.agent_id in the budget aggregation code.
---
Outside diff comments:
In `@web/src/__tests__/stores/company.test.ts`:
- Around line 390-407: The assertions use a.id only and therefore miss agents
that fallback to name; update both checks that currently call .map((a) => a.id)
(the "reordered" and "restored" expectations) to use the same fallback logic as
agentIds: .map(a => a.id ?? a.name). This keeps the assertions aligned with the
agentIds variable and with the optimisticReorderAgents behavior; ensure the
rollback expectation still compares to agentIds.toReversed() but using the same
fallback mapping.
In `@web/src/hooks/useAgentDetailData.ts`:
- Around line 96-116: The socket handlers currently never get installed/removed
when agentName transitions because useWebSocket ignores later bindings changes;
fix by tying the socket lifecycle to agentName and clearing pending debounce on
name changes: pass enabled: Boolean(agentName) into useWebSocket({ bindings,
enabled: !!agentName }) so the hook sets up/tears down when agentName changes,
and add a useEffect that watches agentName and on change clears any pending
timeout on wsDebounceRef.current (and optionally resets agentNameRef.current) to
prevent fetchAgentDetail('') from firing; keep DETAIL_CHANNELS, bindings,
wsDebounceRef, useEffect, useWebSocket, fetchAgentDetail and agentNameRef
references to locate where to modify.
In `@web/src/pages/org-edit/AgentsTab.tsx`:
- Around line 168-177: Add a POST handler on the backend to implement the
/departments/{name}/reorder-agents endpoint: add a method (e.g., reorderAgents)
to DepartmentController that accepts a body { agent_ids: string[] }, validates
each entry is a valid agent ID (e.g., UUID format) and belongs to the given
department, rejects with 400 if any ID is missing/invalid (do not accept names
as IDs), and calls a service method (e.g.,
DepartmentService.reorderAgents(departmentName, agentIds)) to persist the new
order; ensure the controller returns appropriate success/error responses and
that DepartmentService updates the stored order for that department atomically.
In `@web/src/pages/org/build-org-tree.ts`:
- Around line 23-30: DepartmentGroupData currently only includes cost7d so the
per-department currency from DepartmentHealth is dropped, causing
DepartmentStatsBar to default to "EUR"; update the data flow to preserve and
pass currency: add currency?: string to the DepartmentGroupData shape
(referenced in web/src/pages/org/build-org-tree.ts), ensure DepartmentGroupNode
(web/src/pages/org/DepartmentGroupNode.tsx) reads data.currency (from
DepartmentHealth) and passes data.currency ?? undefined into the
DepartmentStatsBar component, and update DepartmentStatsBar prop usage
(web/src/pages/org/DepartmentStatsBar.tsx) to accept and use that currency prop
instead of falling back to "EUR".
🪄 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: 8f443f80-1987-4202-887a-693753dcb774
📒 Files selected for processing (52)
CLAUDE.mddocs/design/brand-and-ux.mdweb/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/__tests__/helpers/factories.tsweb/src/__tests__/hooks/useAgentDetailData.test.tsweb/src/__tests__/hooks/useAgentsData.test.tsweb/src/__tests__/pages/dashboard/OrgHealthSection.test.tsxweb/src/__tests__/pages/org-edit/DepartmentsTab.test.tsxweb/src/__tests__/pages/org/DepartmentStatsBar.test.tsxweb/src/__tests__/pages/org/build-org-tree.test.tsweb/src/__tests__/stores/agents.test.tsweb/src/__tests__/stores/analytics.test.tsweb/src/__tests__/stores/budget.test.tsweb/src/__tests__/stores/company.test.tsweb/src/__tests__/utils/agents.property.test.tsweb/src/__tests__/utils/agents.test.tsweb/src/__tests__/utils/dashboard.property.test.tsweb/src/__tests__/utils/dashboard.test.tsweb/src/api/types.tsweb/src/components/ui/dept-health-bar.stories.tsxweb/src/components/ui/dept-health-bar.tsxweb/src/hooks/useAgentDetailData.tsweb/src/hooks/useAgentsData.tsweb/src/pages/AgentDetailPage.tsxweb/src/pages/DashboardPage.tsxweb/src/pages/agents/AgentGridView.stories.tsxweb/src/pages/agents/AgentGridView.tsxweb/src/pages/agents/AgentIdentityHeader.stories.tsxweb/src/pages/agents/AgentIdentityHeader.tsxweb/src/pages/dashboard/DashboardPage.stories.tsxweb/src/pages/dashboard/OrgHealthSection.stories.tsxweb/src/pages/dashboard/OrgHealthSection.tsxweb/src/pages/org-edit/AgentCreateDialog.stories.tsxweb/src/pages/org-edit/AgentEditDrawer.stories.tsxweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/pages/org-edit/AgentsTab.stories.tsxweb/src/pages/org-edit/AgentsTab.tsxweb/src/pages/org-edit/DepartmentEditDrawer.stories.tsxweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/pages/org-edit/DepartmentsTab.stories.tsxweb/src/pages/org-edit/DepartmentsTab.tsxweb/src/pages/org-edit/YamlEditorPanel.stories.tsxweb/src/pages/org/DepartmentGroupNode.stories.tsxweb/src/pages/org/DepartmentGroupNode.tsxweb/src/pages/org/DepartmentStatsBar.stories.tsxweb/src/pages/org/DepartmentStatsBar.tsxweb/src/pages/org/build-org-tree.tsweb/src/pages/setup/ThemePreview.tsxweb/src/stores/budget.tsweb/src/stores/company.tsweb/src/utils/agents.tsweb/src/utils/dashboard.ts
💤 Files with no reviewable changes (1)
- web/src/pages/DashboardPage.tsx
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: Dashboard Test
- GitHub Check: Build Web
- GitHub Check: Build Sandbox
- GitHub Check: Build Backend
- GitHub Check: Dependency Review
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (9)
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
web/src/**/*.{ts,tsx}: Web Dashboard: ALWAYS reuse existing components fromweb/src/components/ui/before creating new ones. Reuse StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton variants, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup, Drawer, InputField, SelectField, SliderField, ToggleField, TaskStatusIndicator, PriorityBadge, ProviderHealthBadge, TokenUsageBar, CodeMirrorEditor, SegmentedControl, ThemeToggle.
Do NOT recreate status dots inline—use<StatusBadge>. Do NOT build card-with-header layouts from scratch—use<SectionCard>. Do NOT create metric displays withtext-metric font-bold—use<MetricCard>. Do NOT render initials circles manually—use<Avatar>. Do NOT create complex (>8 line) JSX inside.map()—extract to a shared component.
Web design system enforcement: A PostToolUse hook (scripts/check_web_design_system.py) runs automatically on every Edit/Write toweb/src/files. It catches hardcoded hex colors, rgba values, font-family declarations, missing Storybook stories, duplicate patterns, and complex map blocks. Fix all violations before proceeding.
React 19 + shadcn/ui + Tailwind CSS 4 dashboard: Use React Router, Zustand stores,@tanstack/react-query,@xyflow/react,@dagrejs/dagre, d3-force,@dnd-kit, Recharts, Framer Motion, cmdk, js-yaml, Axios.
Files:
web/src/pages/org-edit/AgentEditDrawer.stories.tsxweb/src/__tests__/stores/agents.test.tsweb/src/__tests__/pages/org-edit/DepartmentsTab.test.tsxweb/src/__tests__/utils/agents.property.test.tsweb/src/__tests__/utils/agents.test.tsweb/src/__tests__/stores/budget.test.tsweb/src/pages/agents/AgentIdentityHeader.stories.tsxweb/src/pages/org/DepartmentGroupNode.tsxweb/src/pages/org-edit/AgentsTab.stories.tsxweb/src/stores/budget.tsweb/src/components/ui/dept-health-bar.tsxweb/src/pages/AgentDetailPage.tsxweb/src/__tests__/hooks/useAgentsData.test.tsweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/hooks/useAgentsData.tsweb/src/pages/org-edit/AgentCreateDialog.stories.tsxweb/src/pages/org-edit/YamlEditorPanel.stories.tsxweb/src/utils/dashboard.tsweb/src/pages/setup/ThemePreview.tsxweb/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/pages/agents/AgentGridView.stories.tsxweb/src/components/ui/dept-health-bar.stories.tsxweb/src/pages/agents/AgentIdentityHeader.tsxweb/src/pages/org/DepartmentGroupNode.stories.tsxweb/src/pages/org-edit/DepartmentsTab.tsxweb/src/pages/org-edit/DepartmentEditDrawer.stories.tsxweb/src/hooks/useAgentDetailData.tsweb/src/pages/org-edit/DepartmentsTab.stories.tsxweb/src/pages/agents/AgentGridView.tsxweb/src/pages/dashboard/DashboardPage.stories.tsxweb/src/__tests__/pages/dashboard/OrgHealthSection.test.tsxweb/src/stores/company.tsweb/src/__tests__/pages/org/DepartmentStatsBar.test.tsxweb/src/__tests__/utils/dashboard.test.tsweb/src/__tests__/hooks/useAgentDetailData.test.tsweb/src/pages/org-edit/AgentsTab.tsxweb/src/__tests__/utils/dashboard.property.test.tsweb/src/pages/org/DepartmentStatsBar.stories.tsxweb/src/utils/agents.tsweb/src/__tests__/stores/analytics.test.tsweb/src/__tests__/helpers/factories.tsweb/src/__tests__/stores/company.test.tsweb/src/pages/dashboard/OrgHealthSection.stories.tsxweb/src/__tests__/pages/org/build-org-tree.test.tsweb/src/pages/org/DepartmentStatsBar.tsxweb/src/pages/org/build-org-tree.tsweb/src/pages/dashboard/OrgHealthSection.tsxweb/src/api/types.ts
web/src/**/*.{ts,tsx,css}
📄 CodeRabbit inference engine (CLAUDE.md)
Design tokens: Colors use Tailwind semantic classes (
text-foreground,bg-card,text-accent, etc.) or CSS variables (var(--so-accent)). NEVER hardcode hex values. Typography usesfont-sansorfont-mono. Spacing uses density-aware tokens or standard Tailwind. Shadows/borders use token variables.
Files:
web/src/pages/org-edit/AgentEditDrawer.stories.tsxweb/src/__tests__/stores/agents.test.tsweb/src/__tests__/pages/org-edit/DepartmentsTab.test.tsxweb/src/__tests__/utils/agents.property.test.tsweb/src/__tests__/utils/agents.test.tsweb/src/__tests__/stores/budget.test.tsweb/src/pages/agents/AgentIdentityHeader.stories.tsxweb/src/pages/org/DepartmentGroupNode.tsxweb/src/pages/org-edit/AgentsTab.stories.tsxweb/src/stores/budget.tsweb/src/components/ui/dept-health-bar.tsxweb/src/pages/AgentDetailPage.tsxweb/src/__tests__/hooks/useAgentsData.test.tsweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/hooks/useAgentsData.tsweb/src/pages/org-edit/AgentCreateDialog.stories.tsxweb/src/pages/org-edit/YamlEditorPanel.stories.tsxweb/src/utils/dashboard.tsweb/src/pages/setup/ThemePreview.tsxweb/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/pages/agents/AgentGridView.stories.tsxweb/src/components/ui/dept-health-bar.stories.tsxweb/src/pages/agents/AgentIdentityHeader.tsxweb/src/pages/org/DepartmentGroupNode.stories.tsxweb/src/pages/org-edit/DepartmentsTab.tsxweb/src/pages/org-edit/DepartmentEditDrawer.stories.tsxweb/src/hooks/useAgentDetailData.tsweb/src/pages/org-edit/DepartmentsTab.stories.tsxweb/src/pages/agents/AgentGridView.tsxweb/src/pages/dashboard/DashboardPage.stories.tsxweb/src/__tests__/pages/dashboard/OrgHealthSection.test.tsxweb/src/stores/company.tsweb/src/__tests__/pages/org/DepartmentStatsBar.test.tsxweb/src/__tests__/utils/dashboard.test.tsweb/src/__tests__/hooks/useAgentDetailData.test.tsweb/src/pages/org-edit/AgentsTab.tsxweb/src/__tests__/utils/dashboard.property.test.tsweb/src/pages/org/DepartmentStatsBar.stories.tsxweb/src/utils/agents.tsweb/src/__tests__/stores/analytics.test.tsweb/src/__tests__/helpers/factories.tsweb/src/__tests__/stores/company.test.tsweb/src/pages/dashboard/OrgHealthSection.stories.tsxweb/src/__tests__/pages/org/build-org-tree.test.tsweb/src/pages/org/DepartmentStatsBar.tsxweb/src/pages/org/build-org-tree.tsweb/src/pages/dashboard/OrgHealthSection.tsxweb/src/api/types.ts
**/*.{ts,tsx,json}
📄 CodeRabbit inference engine (CLAUDE.md)
TypeScript 6.0: Remove
baseUrl(deprecated);pathsentries are relative to tsconfig directory.esModuleInteropalways true—cannot be set tofalse.typesdefaults to[]—must explicitly list (e.g.'types': ['vitest/globals']).DOM.Iterablemerged intoDOM.moduleResolution: 'classic'and'node10'removed—use'bundler'or'nodenext'.strictdefaults totrue.noUncheckedSideEffectImportsdefaults totrue.
Files:
web/src/pages/org-edit/AgentEditDrawer.stories.tsxweb/src/__tests__/stores/agents.test.tsweb/src/__tests__/pages/org-edit/DepartmentsTab.test.tsxweb/src/__tests__/utils/agents.property.test.tsweb/src/__tests__/utils/agents.test.tsweb/src/__tests__/stores/budget.test.tsweb/src/pages/agents/AgentIdentityHeader.stories.tsxweb/src/pages/org/DepartmentGroupNode.tsxweb/src/pages/org-edit/AgentsTab.stories.tsxweb/src/stores/budget.tsweb/src/components/ui/dept-health-bar.tsxweb/src/pages/AgentDetailPage.tsxweb/src/__tests__/hooks/useAgentsData.test.tsweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/hooks/useAgentsData.tsweb/src/pages/org-edit/AgentCreateDialog.stories.tsxweb/src/pages/org-edit/YamlEditorPanel.stories.tsxweb/src/utils/dashboard.tsweb/src/pages/setup/ThemePreview.tsxweb/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/pages/agents/AgentGridView.stories.tsxweb/src/components/ui/dept-health-bar.stories.tsxweb/src/pages/agents/AgentIdentityHeader.tsxweb/src/pages/org/DepartmentGroupNode.stories.tsxweb/src/pages/org-edit/DepartmentsTab.tsxweb/src/pages/org-edit/DepartmentEditDrawer.stories.tsxweb/src/hooks/useAgentDetailData.tsweb/src/pages/org-edit/DepartmentsTab.stories.tsxweb/src/pages/agents/AgentGridView.tsxweb/src/pages/dashboard/DashboardPage.stories.tsxweb/src/__tests__/pages/dashboard/OrgHealthSection.test.tsxweb/src/stores/company.tsweb/src/__tests__/pages/org/DepartmentStatsBar.test.tsxweb/src/__tests__/utils/dashboard.test.tsweb/src/__tests__/hooks/useAgentDetailData.test.tsweb/src/pages/org-edit/AgentsTab.tsxweb/src/__tests__/utils/dashboard.property.test.tsweb/src/pages/org/DepartmentStatsBar.stories.tsxweb/src/utils/agents.tsweb/src/__tests__/stores/analytics.test.tsweb/src/__tests__/helpers/factories.tsweb/src/__tests__/stores/company.test.tsweb/src/pages/dashboard/OrgHealthSection.stories.tsxweb/src/__tests__/pages/org/build-org-tree.test.tsweb/src/pages/org/DepartmentStatsBar.tsxweb/src/pages/org/build-org-tree.tsweb/src/pages/dashboard/OrgHealthSection.tsxweb/src/api/types.ts
web/**/*.stories.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Storybook 10: ESM-only—no CJS support. Packages like
@storybook/addon-essentials,@storybook/addon-interactions,@storybook/test,@storybook/blocksno longer published.@storybook/addon-docsis separate. Import fromstorybook/test(not@storybook/test),storybook/actions. UsedefineMainfrom@storybook/react-vite/nodeanddefinePreviewfrom@storybook/react-vite. Backgrounds API: useparameters.backgrounds.options(object) +initialGlobals.backgrounds.value. a11y testing: useparameters.a11y.test: 'error' | 'todo' | 'off'. Minimum versions: Node 20.19+, Vite 5+, Vitest 3+, TypeScript 4.9+.
Files:
web/src/pages/org-edit/AgentEditDrawer.stories.tsxweb/src/pages/agents/AgentIdentityHeader.stories.tsxweb/src/pages/org-edit/AgentsTab.stories.tsxweb/src/pages/org-edit/AgentCreateDialog.stories.tsxweb/src/pages/org-edit/YamlEditorPanel.stories.tsxweb/src/pages/agents/AgentGridView.stories.tsxweb/src/components/ui/dept-health-bar.stories.tsxweb/src/pages/org/DepartmentGroupNode.stories.tsxweb/src/pages/org-edit/DepartmentEditDrawer.stories.tsxweb/src/pages/org-edit/DepartmentsTab.stories.tsxweb/src/pages/dashboard/DashboardPage.stories.tsxweb/src/pages/org/DepartmentStatsBar.stories.tsxweb/src/pages/dashboard/OrgHealthSection.stories.tsx
**/*.{py,ts,tsx,go}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{py,ts,tsx,go}: Lint & format: Python via ruff (uv run ruff check+uv run ruff format). TypeScript web via ESLint (zero warnings enforced:npm --prefix web run lint). Go CLI via golangci-lint andgo vet. All enforced in pre-commit hooks.
Type checking: Python via mypy strict mode (uv run mypy src/ tests/). TypeScript vianpm --prefix web run type-check. Go viago -C cli vet ./....
Files:
web/src/pages/org-edit/AgentEditDrawer.stories.tsxweb/src/__tests__/stores/agents.test.tsweb/src/__tests__/pages/org-edit/DepartmentsTab.test.tsxweb/src/__tests__/utils/agents.property.test.tsweb/src/__tests__/utils/agents.test.tsweb/src/__tests__/stores/budget.test.tsweb/src/pages/agents/AgentIdentityHeader.stories.tsxweb/src/pages/org/DepartmentGroupNode.tsxweb/src/pages/org-edit/AgentsTab.stories.tsxweb/src/stores/budget.tsweb/src/components/ui/dept-health-bar.tsxweb/src/pages/AgentDetailPage.tsxweb/src/__tests__/hooks/useAgentsData.test.tsweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/hooks/useAgentsData.tsweb/src/pages/org-edit/AgentCreateDialog.stories.tsxweb/src/pages/org-edit/YamlEditorPanel.stories.tsxweb/src/utils/dashboard.tsweb/src/pages/setup/ThemePreview.tsxweb/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/pages/agents/AgentGridView.stories.tsxweb/src/components/ui/dept-health-bar.stories.tsxweb/src/pages/agents/AgentIdentityHeader.tsxweb/src/pages/org/DepartmentGroupNode.stories.tsxweb/src/pages/org-edit/DepartmentsTab.tsxweb/src/pages/org-edit/DepartmentEditDrawer.stories.tsxweb/src/hooks/useAgentDetailData.tsweb/src/pages/org-edit/DepartmentsTab.stories.tsxweb/src/pages/agents/AgentGridView.tsxweb/src/pages/dashboard/DashboardPage.stories.tsxweb/src/__tests__/pages/dashboard/OrgHealthSection.test.tsxweb/src/stores/company.tsweb/src/__tests__/pages/org/DepartmentStatsBar.test.tsxweb/src/__tests__/utils/dashboard.test.tsweb/src/__tests__/hooks/useAgentDetailData.test.tsweb/src/pages/org-edit/AgentsTab.tsxweb/src/__tests__/utils/dashboard.property.test.tsweb/src/pages/org/DepartmentStatsBar.stories.tsxweb/src/utils/agents.tsweb/src/__tests__/stores/analytics.test.tsweb/src/__tests__/helpers/factories.tsweb/src/__tests__/stores/company.test.tsweb/src/pages/dashboard/OrgHealthSection.stories.tsxweb/src/__tests__/pages/org/build-org-tree.test.tsweb/src/pages/org/DepartmentStatsBar.tsxweb/src/pages/org/build-org-tree.tsweb/src/pages/dashboard/OrgHealthSection.tsxweb/src/api/types.ts
web/src/pages/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Web dashboard pages: Lazy-loaded page components (one per route). Page-scoped sub-components in
pages/<page-name>/subdirs. Feature dirs added as pages are built. Stores for auth, WebSocket, toast, analytics, setup wizard, company, agents, budget, tasks, settings, providers, theme.
Files:
web/src/pages/org-edit/AgentEditDrawer.stories.tsxweb/src/pages/agents/AgentIdentityHeader.stories.tsxweb/src/pages/org/DepartmentGroupNode.tsxweb/src/pages/org-edit/AgentsTab.stories.tsxweb/src/pages/AgentDetailPage.tsxweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/pages/org-edit/AgentCreateDialog.stories.tsxweb/src/pages/org-edit/YamlEditorPanel.stories.tsxweb/src/pages/setup/ThemePreview.tsxweb/src/pages/agents/AgentGridView.stories.tsxweb/src/pages/agents/AgentIdentityHeader.tsxweb/src/pages/org/DepartmentGroupNode.stories.tsxweb/src/pages/org-edit/DepartmentsTab.tsxweb/src/pages/org-edit/DepartmentEditDrawer.stories.tsxweb/src/pages/org-edit/DepartmentsTab.stories.tsxweb/src/pages/agents/AgentGridView.tsxweb/src/pages/dashboard/DashboardPage.stories.tsxweb/src/pages/org-edit/AgentsTab.tsxweb/src/pages/org/DepartmentStatsBar.stories.tsxweb/src/pages/dashboard/OrgHealthSection.stories.tsxweb/src/pages/org/DepartmentStatsBar.tsxweb/src/pages/org/build-org-tree.tsweb/src/pages/dashboard/OrgHealthSection.tsx
web/src/components/ui/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Web components: Place new shared components in
web/src/components/ui/with kebab-case filenames. Create.stories.tsxwith all states. Export props as TypeScript interface. Use design tokens exclusively—no hardcoded colors, fonts, or spacing. Importcnfrom@/lib/utilsfor conditional class merging.
Files:
web/src/components/ui/dept-health-bar.tsxweb/src/components/ui/dept-health-bar.stories.tsx
docs/design/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
When approved deviations occur, update the relevant
docs/design/page to reflect the new reality.
Files:
docs/design/brand-and-ux.md
docs/**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
Documentation:
docs/(Markdown, built with Zensical, config:mkdocs.yml). Design spec indocs/design/(11 pages), architecture indocs/architecture/, roadmap indocs/roadmap/. REST API reference auto-generated byscripts/export_openapi.py. Library reference auto-generated via mkdocstrings + Griffe. Landing page insite/(Astro).
Files:
docs/design/brand-and-ux.md
🧠 Learnings (12)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/**/*.{ts,tsx} : Web Dashboard: ALWAYS reuse existing components from `web/src/components/ui/` before creating new ones. Reuse StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton variants, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup, Drawer, InputField, SelectField, SliderField, ToggleField, TaskStatusIndicator, PriorityBadge, ProviderHealthBadge, TokenUsageBar, CodeMirrorEditor, SegmentedControl, ThemeToggle.
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/**/*.stories.{ts,tsx} : Storybook 10: ESM-only—no CJS support. Packages like `storybook/addon-essentials`, `storybook/addon-interactions`, `storybook/test`, `storybook/blocks` no longer published. `storybook/addon-docs` is separate. Import from `storybook/test` (not `storybook/test`), `storybook/actions`. Use `defineMain` from `storybook/react-vite/node` and `definePreview` from `storybook/react-vite`. Backgrounds API: use `parameters.backgrounds.options` (object) + `initialGlobals.backgrounds.value`. a11y testing: use `parameters.a11y.test: 'error' | 'todo' | 'off'`. Minimum versions: Node 20.19+, Vite 5+, Vitest 3+, TypeScript 4.9+.
Applied to files:
web/src/pages/org-edit/AgentEditDrawer.stories.tsxweb/src/pages/org-edit/AgentsTab.stories.tsxweb/src/pages/org-edit/AgentCreateDialog.stories.tsxweb/src/pages/org-edit/YamlEditorPanel.stories.tsxweb/src/pages/agents/AgentGridView.stories.tsxweb/src/components/ui/dept-health-bar.stories.tsxweb/src/pages/org/DepartmentGroupNode.stories.tsxweb/src/pages/org-edit/DepartmentEditDrawer.stories.tsxweb/src/pages/org-edit/DepartmentsTab.stories.tsxweb/src/pages/org/DepartmentStatsBar.stories.tsx
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/**/*.{ts,tsx} : Web Dashboard: ALWAYS reuse existing components from `web/src/components/ui/` before creating new ones. Reuse StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton variants, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup, Drawer, InputField, SelectField, SliderField, ToggleField, TaskStatusIndicator, PriorityBadge, ProviderHealthBadge, TokenUsageBar, CodeMirrorEditor, SegmentedControl, ThemeToggle.
Applied to files:
CLAUDE.mdweb/src/components/ui/dept-health-bar.tsxweb/src/pages/AgentDetailPage.tsxweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/utils/dashboard.tsweb/src/pages/setup/ThemePreview.tsxweb/src/components/ui/dept-health-bar.stories.tsxweb/src/pages/org-edit/DepartmentsTab.tsxweb/src/pages/dashboard/DashboardPage.stories.tsxweb/src/__tests__/pages/dashboard/OrgHealthSection.test.tsxweb/src/__tests__/utils/dashboard.test.tsweb/src/pages/org-edit/AgentsTab.tsxdocs/design/brand-and-ux.mdweb/src/pages/dashboard/OrgHealthSection.stories.tsxweb/src/pages/dashboard/OrgHealthSection.tsx
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do NOT recreate status dots inline—use `<StatusBadge>`. Do NOT build card-with-header layouts from scratch—use `<SectionCard>`. Do NOT create metric displays with `text-metric font-bold`—use `<MetricCard>`. Do NOT render initials circles manually—use `<Avatar>`. Do NOT create complex (>8 line) JSX inside `.map()`—extract to a shared component.
Applied to files:
CLAUDE.mdweb/src/components/ui/dept-health-bar.tsxweb/src/pages/AgentDetailPage.tsxweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/pages/setup/ThemePreview.tsxweb/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/components/ui/dept-health-bar.stories.tsxweb/src/pages/agents/AgentIdentityHeader.tsxweb/src/pages/org-edit/DepartmentsTab.tsxweb/src/pages/agents/AgentGridView.tsxweb/src/__tests__/pages/org/DepartmentStatsBar.test.tsxweb/src/pages/org-edit/AgentsTab.tsxdocs/design/brand-and-ux.mdweb/src/pages/dashboard/OrgHealthSection.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:
CLAUDE.mddocs/design/brand-and-ux.md
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : Web components: Place new shared components in `web/src/components/ui/` with kebab-case filenames. Create `.stories.tsx` with all states. Export props as TypeScript interface. Use design tokens exclusively—no hardcoded colors, fonts, or spacing. Import `cn` from `@/lib/utils` for conditional class merging.
Applied to files:
CLAUDE.mdweb/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/components/ui/dept-health-bar.stories.tsxweb/src/pages/org-edit/DepartmentEditDrawer.stories.tsxweb/src/pages/org-edit/DepartmentsTab.stories.tsxweb/src/pages/org/DepartmentStatsBar.stories.tsx
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/**/*.{ts,tsx} : React 19 + shadcn/ui + Tailwind CSS 4 dashboard: Use React Router, Zustand stores, tanstack/react-query, xyflow/react, dagrejs/dagre, d3-force, dnd-kit, Recharts, Framer Motion, cmdk, js-yaml, Axios.
Applied to files:
CLAUDE.mdweb/src/pages/org/DepartmentGroupNode.tsxweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/hooks/useAgentsData.tsweb/src/pages/org-edit/DepartmentsTab.tsxweb/src/hooks/useAgentDetailData.tsweb/src/pages/dashboard/DashboardPage.stories.tsxweb/src/pages/org-edit/AgentsTab.tsx
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/pages/**/*.{ts,tsx} : Web dashboard pages: Lazy-loaded page components (one per route). Page-scoped sub-components in `pages/<page-name>/` subdirs. Feature dirs added as pages are built. Stores for auth, WebSocket, toast, analytics, setup wizard, company, agents, budget, tasks, settings, providers, theme.
Applied to files:
CLAUDE.mdweb/src/pages/dashboard/DashboardPage.stories.tsx
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to web/src/__tests__/**/*.{ts,js} : Dashboard testing: Vitest unit tests organized by feature under `web/src/__tests__/`. Use fast-check for property-based testing (`fc.assert` + `fc.property`).
Applied to files:
web/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/__tests__/pages/dashboard/OrgHealthSection.test.tsxweb/src/__tests__/pages/org/DepartmentStatsBar.test.tsxweb/src/__tests__/utils/dashboard.test.tsweb/src/__tests__/utils/dashboard.property.test.ts
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/**/*.{ts,tsx,css} : Design tokens: Colors use Tailwind semantic classes (`text-foreground`, `bg-card`, `text-accent`, etc.) or CSS variables (`var(--so-accent)`). NEVER hardcode hex values. Typography uses `font-sans` or `font-mono`. Spacing uses density-aware tokens or standard Tailwind. Shadows/borders use token variables.
Applied to files:
docs/design/brand-and-ux.md
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to **/*.{ts,tsx,json} : TypeScript 6.0: Remove `baseUrl` (deprecated); `paths` entries are relative to tsconfig directory. `esModuleInterop` always true—cannot be set to `false`. `types` defaults to `[]`—must explicitly list (e.g. `'types': ['vitest/globals']`). `DOM.Iterable` merged into `DOM`. `moduleResolution: 'classic'` and `'node10'` removed—use `'bundler'` or `'nodenext'`. `strict` defaults to `true`. `noUncheckedSideEffectImports` defaults to `true`.
Applied to files:
web/src/api/types.ts
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to src/synthorg/hr/**/*.py : HR: Hiring, firing, onboarding, agent registry, performance tracking, activity timeline, career history, promotion/demotion.
Applied to files:
web/src/api/types.ts
🔇 Additional comments (41)
CLAUDE.md (1)
246-246: Documentation update is consistent with currentDeptHealthBarAPI.This line now correctly reflects the component’s utilization semantics and current prop surface (
health?,agentCount, notaskCount).web/src/pages/agents/AgentIdentityHeader.stories.tsx (1)
23-23: Fixture update matches the newAgentConfigcontract.Adding
authority: {}here keeps story args valid with the required field and avoids type/runtime drift in stories.web/src/pages/setup/ThemePreview.tsx (1)
175-175: Callsite is correctly aligned with the updatedDeptHealthBarAPI.Good update removing the obsolete prop and keeping the preview in sync.
web/src/components/ui/dept-health-bar.stories.tsx (1)
17-17: Stories are consistently updated to the new prop contract.All variants now correctly omit the removed prop and stay aligned with
DeptHealthBar’s current API.Also applies to: 21-21, 25-25, 29-29, 33-33, 37-37, 41-41, 44-47
web/src/__tests__/components/ui/dept-health-bar.test.tsx (1)
31-31: Test inputs are correctly synced with the component API changes.These updates keep existing behavioral coverage intact while removing the obsolete prop usage.
Also applies to: 37-37, 49-49, 55-55, 63-63
web/src/__tests__/utils/agents.property.test.ts (1)
74-74:arbAgentnow matches the updatedAgentConfigshape.Adding
authorityhere keeps property tests type-safe and representative.web/src/__tests__/stores/budget.test.ts (1)
134-134: Mock payload update is correct for the new agent schema.Including
authoritykeeps the budget-store fixture aligned with the current response shape.web/src/__tests__/stores/agents.test.ts (1)
53-53: Shared test fixture is correctly updated.Adding
authorityinmakeAgentkeeps all dependent store tests aligned with the current type contract.web/src/pages/org-edit/AgentEditDrawer.stories.tsx (1)
22-22: Fixture update matches the newAgentConfigcontract.
authority: {}is correctly added in the story mock.web/src/pages/org-edit/DepartmentEditDrawer.tsx (1)
88-88: Prop rename is correctly wired to the new health schema.
Usingutilization_percenthere is consistent with the updatedDepartmentHealthmodel.web/src/pages/org-edit/AgentsTab.stories.tsx (1)
23-23: Story fixtures are consistently aligned with the updated agent type.
Good update addingauthorityon each mock agent entry.Also applies to: 44-44, 65-65
web/src/pages/agents/AgentIdentityHeader.tsx (1)
15-15: Null-safe handling is correct for optional runtime fields.
Defaulting missingstatusand guardinghiring_daterendering both look solid.Also applies to: 39-46
web/src/utils/dashboard.ts (1)
99-99:computeOrgHealthmigration is correctly implemented.
Validation, warning payload, and aggregation now consistently useutilization_percent/department_name.Also applies to: 102-103, 107-107
web/src/pages/org/DepartmentGroupNode.tsx (1)
42-42:DepartmentStatsBarprop update is correctly applied.
Passingcost7dhere matches the updated stats-bar interface.web/src/__tests__/utils/agents.test.ts (1)
54-54: Test updates correctly cover the new agent schema and status fallback behavior.
Good addition ofauthorityin the factory and explicit undefined-status filtering coverage.Also applies to: 203-207
web/src/pages/AgentDetailPage.tsx (1)
74-74: Good runtime narrowing fortools.allowed.This keeps
ToolBadgesinsulated from the newRecord<string, unknown>shape and safely drops malformed entries.web/src/pages/org/DepartmentGroupNode.stories.tsx (1)
55-55: Story args match the renamed department stats field.Switching these fixtures to
cost7dkeeps the stories aligned withDepartmentGroupDataandDepartmentStatsBar, so they should keep catching prop drift after the schema rename.Also applies to: 68-68, 81-81, 94-94
web/src/pages/org-edit/YamlEditorPanel.stories.tsx (1)
28-28: The story fixture stays in sync with requiredAgentConfig.authority.Good update—this prevents the panel story from drifting away from the runtime schema.
web/src/pages/org-edit/DepartmentsTab.tsx (1)
80-80: The department-health rename is wired through consistently.Using
department_namefor the lookup andutilization_percentforDeptHealthBarmatches the backend model cleanly.Also applies to: 114-114
web/src/pages/org-edit/AgentCreateDialog.stories.tsx (1)
22-22: The stub agent matches the updated schema.Adding
authorityhere keeps the create-dialog story aligned with the requiredAgentConfigshape.web/src/pages/agents/AgentGridView.tsx (1)
28-29: Optional agent fields are handled cleanly here.Defaulting a missing
status, skippingformatRelativeTimewhenhiring_dateis absent, and falling back toagent.namefor the key make the grid resilient to partial agent payloads.Also applies to: 55-55
web/src/pages/org-edit/AgentEditDrawer.tsx (1)
56-56: The optionalstatusandhiring_datepaths are handled well.These fallbacks keep both the read-only header and the edit controls stable when older payloads omit those fields.
Also applies to: 69-69, 115-116, 148-148
web/src/pages/dashboard/DashboardPage.stories.tsx (1)
55-60: LGTM!The
mockDepartmentsfixture correctly implements the newDepartmentHealthschema with all required fields (department_name,agent_count,active_agent_count,currency,avg_performance_score,department_cost_7d,cost_trend,collaboration_score,utilization_percent). The nullablecollaboration_score: nullfor operations is valid per the interface definition.web/src/pages/agents/AgentGridView.stories.tsx (1)
33-33: LGTM!The
authority: {}field is correctly added to align with the updatedAgentConfigschema that now requiresauthority. The removal of theskillsdefault (no longer present) is consistent with the type changes.web/src/pages/org-edit/AgentsTab.tsx (1)
50-50: LGTM on UI-local fallbacks.Using
agent.id ?? agent.namefor sortable IDs andagent.status ?? 'active'for runtime status are appropriate fallbacks for UI rendering and local drag-and-drop behavior. These ensure the component doesn't break when optional fields are absent.Also applies to: 73-73
web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx (1)
14-24: LGTM!The
mockHealthfixture correctly implements all fields from the updatedDepartmentHealthinterface as defined inweb/src/api/types.ts:558-572. The a11y testing configuration (test: 'error') is properly set per Storybook 10 standards.web/src/pages/org/DepartmentStatsBar.stories.tsx (1)
23-45: LGTM!The stories correctly use the new
cost7dprop replacingtaskCountandcostUsd. The test coverage includes relevant edge cases: normal value,null, and zero. This aligns with the component's actual usage inDepartmentGroupNode.tsx.web/src/pages/org-edit/DepartmentsTab.stories.tsx (2)
25-25: LGTM!The
authority: {}field is correctly added to the agent config to match the updatedAgentConfigschema.
36-39: LGTM!The
mockHealthsfixture correctly implements the newDepartmentHealthschema. The product department appropriately hasnullforavg_performance_scoreandcollaboration_scoresince it has no agents (agent_count: 0).web/src/utils/agents.ts (3)
71-73: LGTM!Treating
undefinedstatus as'active'in the filter is a sensible default that aligns with the PR objective of makingstatusoptional inAgentConfig.
106-116: LGTM on status sorting fallback.The
a.status ?? 'active'fallback for status sorting is consistent with the filter logic. The generic field fallback to''handles missing fields gracefully for string comparisons.
249-267: LGTM!Typing
ACTIVITY_ICON_MAPasPartial<Record<ActivityEventType, LucideIcon>>correctly expresses that not all event types have dedicated icons. The cast ingetActivityEventIconis safe because the fallback?? FALLBACK_ICONhandles missing entries.web/src/__tests__/utils/dashboard.property.test.ts (1)
105-115: LGTM!The
arbDeptHealthgenerator correctly produces values matching the updatedDepartmentHealthinterface. The use offc.option(..., { nil: null })for nullable fields (avg_performance_score,collaboration_score) and constrained floats forutilization_percentensures valid test data generation.web/src/stores/company.ts (1)
284-320: Consistent fallback identity handling in optimistic reorder/rollbackGood update: using
a.id ?? a.nameconsistently in both forward reorder and rollback paths avoids dropping agents whenidis absent.web/src/__tests__/utils/dashboard.test.ts (1)
207-228: Org health tests are correctly aligned toutilization_percentCoverage for rounding plus non-finite filtering (
NaN/Infinity) is solid and matches the updated aggregation behavior.web/src/__tests__/pages/org/DepartmentStatsBar.test.tsx (1)
7-34: Tests now correctly match theDepartmentStatsBarAPIThe updated assertions for
cost7d,Cost (7d), and default EUR behavior are consistent with the component changes.web/src/__tests__/pages/dashboard/OrgHealthSection.test.tsx (1)
11-57: Fixture shape and currency assertions are correctly migratedThe updated department fixture fields and per-department currency checks match the new
DepartmentHealthcontract and rendering path.web/src/pages/dashboard/OrgHealthSection.stories.tsx (1)
7-15: Story fixture migration is clean and consistent
makeDeptsnow emits the correct backend-alignedDepartmentHealthfields, which keeps stories in sync with runtime types.web/src/__tests__/stores/analytics.test.ts (1)
46-55: Analytics store mocks/assertions are correctly aligned toutilization_percentThe migrated health fixtures and assertion updates are consistent with the new type contract and org-health derivation flow.
Also applies to: 142-157, 204-211
web/src/__tests__/stores/company.test.ts (1)
53-61: Department health test fixtures are correctly updatedThe fixture now matches the backend-aligned
DepartmentHealthshape, and the assertion switch todepartment_nameis correct.Also applies to: 158-158
web/src/pages/dashboard/OrgHealthSection.tsx (1)
15-29: Nice reuse of the shared dashboard primitives.Extracting
DepartmentRowkeeps the.map()block small, and the section continues to composeSectionCard,DeptHealthBar,ProgressGauge, andEmptyStateinstead of rebuilding that pattern inline.As per coding guidelines, "Web Dashboard: ALWAYS reuse existing components from
web/src/components/ui/before creating new ones." and "Do NOT create complex (>8 line) JSX inside.map()—extract to a shared component."Also applies to: 32-53
| ? { role: 'meter', 'aria-valuenow': clamped, 'aria-valuemin': 0, 'aria-valuemax': 100 } | ||
| : {})} | ||
| aria-label={`${name} health: ${clamped != null ? `${clamped}%` : 'unavailable'}`} | ||
| aria-label={`${name} utilization: ${clamped != null ? `${clamped}%` : 'unavailable'}`} |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider aligning prop naming with utilization terminology.
The aria label now correctly uses “utilization,” but the public prop is still health. A follow-up rename (e.g., utilizationPercent) would reduce domain ambiguity for future consumers.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/components/ui/dept-health-bar.tsx` at line 42, The component prop
name 'health' in DeptHealthBar should be renamed to a domain-accurate name
(e.g., utilizationPercent) to match the aria-label wording; update the
DeptHealthBar component signature, its props interface/type, any defaultProps or
destructuring that uses 'health', and all call sites that pass the prop to use
utilizationPercent instead (or add a short-lived alias mapping from health =>
utilizationPercent to preserve backwards compatibility while you update
callers); also update the aria-label usage at the line with aria-label={`${name}
utilization: ${clamped != null ? `${clamped}%` : 'unavailable'}`} to read the
new prop name.
| import type { AgentConfig, WsChannel } from '@/api/types' | ||
|
|
||
| const AGENTS_POLL_INTERVAL = 30_000 | ||
| const WS_DEBOUNCE_MS = 300 |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Centralize the WS refetch debounce policy.
This hook now duplicates the same 300ms constant and timeout/reset logic that was added to web/src/hooks/useAgentDetailData.ts. The next tuning change will require touching both hooks and both debounce test suites. A tiny shared helper/constant would keep list/detail behavior from drifting.
Also applies to: 53-67
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/hooks/useAgentsData.ts` at line 9, The WS refetch debounce value and
logic duplicated by WS_DEBOUNCE_MS in useAgentsData.ts (and the identical
timeout/reset logic in useAgentDetailData.ts) should be centralized: extract the
numeric debounce constant and any small helper debounce/reset function into a
shared module (e.g., export WS_DEBOUNCE_MS and a debounceReset helper) and
import those symbols into useAgentsData.ts and useAgentDetailData.ts, replacing
the local WS_DEBOUNCE_MS and duplicated timeout/reset logic so both hooks use
the single shared source of truth for debounce timing and behavior.
There was a problem hiding this comment.
Actionable comments posted: 8
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
web/src/hooks/useAgentDetailData.ts (1)
110-126:⚠️ Potential issue | 🟠 MajorWebSocket binding lifecycle is broken when
agentNamechanges.At Line [123],
bindingschanges withagentName, butuseWebSocketwires handlers from mount-time bindings only. This can miss WS setup on'' -> 'alice'transitions and can keep old handlers active whenagentNamebecomes empty, leading tofetchAgentDetail('')from Line [118].🔧 Suggested fix
- const bindings: ChannelBinding[] = useMemo( - () => - agentName - ? DETAIL_CHANNELS.map((channel) => ({ - channel, - handler: () => { - if (wsDebounceRef.current) clearTimeout(wsDebounceRef.current) - wsDebounceRef.current = setTimeout(() => { - useAgentsStore.getState().fetchAgentDetail(agentNameRef.current) - }, WS_DEBOUNCE_MS) - }, - })) - : EMPTY_BINDINGS, - [agentName], - ) - - const { connected: wsConnected, setupError: wsSetupError } = useWebSocket({ bindings }) + const bindings: ChannelBinding[] = useMemo( + () => + DETAIL_CHANNELS.map((channel) => ({ + channel, + handler: () => { + const currentName = agentNameRef.current + if (!currentName) return + if (wsDebounceRef.current) clearTimeout(wsDebounceRef.current) + wsDebounceRef.current = setTimeout(() => { + useAgentsStore.getState().fetchAgentDetail(currentName) + }, WS_DEBOUNCE_MS) + }, + })), + [], + ) + + const { connected: wsConnected, setupError: wsSetupError } = useWebSocket({ + bindings, + enabled: Boolean(agentName), + })🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/hooks/useAgentDetailData.ts` around lines 110 - 126, The WebSocket bindings capture agentName at creation and cause stale/no-op behavior on transitions; fix by making the handler read the live ref and guard empty names instead of closing over agentName: in useAgentDetailData update the binding factory (used to build bindings passed to useWebSocket) so each handler does const name = agentNameRef.current; if (!name) return; then perform the debounce and call useAgentsStore.getState().fetchAgentDetail(name) (still using wsDebounceRef and WS_DEBOUNCE_MS); keep DETAIL_CHANNELS and bindings structure but ensure handlers never close over the outer agentName value so handlers remain valid across agentName changes and won't call fetchAgentDetail('').
🤖 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__/hooks/useAgentDetailData.test.ts`:
- Around line 179-259: Add tests that exercise agentName prop transitions by
rendering useAgentDetailData with '' then rerendering to 'alice' and vice versa
to verify WebSocket handler setup/teardown; specifically, in the WebSocket
debounce suite add two cases: one that calls renderHook(() =>
useAgentDetailData('')) then rerender('alice'), fetches the new bindings from
vi.mocked(useWebSocket).mock.calls[0][0].bindings, triggers the handler(s),
advances timers and asserts mockFetchAgentDetail was called with 'alice'; and a
reverse case that starts with 'alice', rerenders to '' and asserts the previous
handlers were cleaned up (trigger handler after rerender/unmount and advance
timers to confirm mockFetchAgentDetail is not called). Ensure you reuse
setupHandler pattern (useWebSocket, wsHandler/agentsHandler/tasksHandler), clear
mockFetchAgentDetail between steps, and call
vi.useFakeTimers()/vi.useRealTimers() as in existing tests.
In `@web/src/__tests__/pages/org/build-org-tree.test.ts`:
- Around line 337-348: The test currently masks the optional id by using
"undefined as unknown as string"; instead update the fixture and test to
preserve the optional id typing: change makeAgent to accept id?: string
(optional) and in this test call makeAgent({ id: undefined, name: 'NoIdAgent',
... }) or simply omit the id field so the helper passes undefined naturally;
keep buildOrgTree usage unchanged and assert on the node id/data as before.
In `@web/src/__tests__/utils/agents.test.ts`:
- Around line 219-229: Update the test "treats agent with undefined status as
active for sorting" to include an explicit onboarding case so undefined isn't
accidentally treated as onboarding; add makeAgent({ name: 'Onboarding Agent',
status: 'onboarding' }) to the withUndefined array and then assert that
names.indexOf('No Status') is less than names.indexOf('Onboarding Agent') (and
keep/assert that it's also less than names.indexOf('Terminated Agent') if
desired) to ensure undefined is treated as active.
In `@web/src/__tests__/utils/dashboard.test.ts`:
- Around line 187-191: The helper function name dh is too terse and hurts
readability; rename the function dh to a more descriptive name like
makeDepartmentHealth or createDeptHealth across the file and update all call
sites in the test suite accordingly (e.g., rename the function declaration for
dh and replace every invocation of dh(...) with makeDepartmentHealth(...) so
imports/uses remain consistent while preserving the same parameters and return
type DepartmentHealth).
In `@web/src/pages/AgentDetailPage.tsx`:
- Line 74: Extract the inline type-filtering expression into a clearly named
local variable before the JSX return to improve readability: compute a const
like allowedTools (or similar) by checking Array.isArray(agent.tools['allowed'])
then filtering (agent.tools['allowed'] as unknown[]).filter((t): t is string =>
typeof t === 'string'), and pass that variable into <ToolBadges> instead of the
inline expression; update the JSX to ToolBadges tools={allowedTools} so the
logic is isolated from presentation.
In `@web/src/pages/org-edit/AgentEditDrawer.tsx`:
- Line 69: The current memoized formatter uses new Date(agent.hiring_date) which
can shift date-only strings across timezones and doesn't guard invalid dates;
update the function that returns agent?.hiring_date ? ... to parse date-only
strings safely (e.g., detect YYYY-MM-DD and construct a Date with Date.UTC(year,
month-1, day) or create with local year/month/day components), check the
resulting Date with isNaN(date.getTime()) and return '' for invalid values, and
change the memo dependency from [agent] to [agent?.hiring_date] so the memo only
recalculates when the hiring_date value changes.
In `@web/src/pages/org/build-org-tree.ts`:
- Around line 97-98: agent.name is being used as a fallback key when agent.id is
missing which can cause collisions; create a deterministic unique key generator
(e.g., function generateAgentKey(agent, index) or method on the agent object)
that returns agent.id when present and otherwise returns a synthesized stable
key built from multiple immutable agent properties (for example name + type +
createdAt or email) and/or the agent's index, and replace all usages that
currently fall back to agent.name (including the resolveRuntimeStatus call and
the other occurrences referenced) to use generateAgentKey(...) so runtime-status
lookups, node ids, edge ids, and CEO/head exclusion use this stable unique key.
In `@web/src/pages/org/DepartmentStatsBar.tsx`:
- Around line 16-25: Remove the hardcoded fallback currency and ensure the cost
pill only renders when a real currency is provided: update
DepartmentStatsBarProps/DepartmentStatsBar to not default currency = 'EUR' (make
currency required or optional without a default), and change the conditional
around the cost pill to check both cost7d is finite and currency is present
(e.g., if cost7d !== null && Number.isFinite(cost7d) && currency). Use
formatCurrency(cost7d, currency) only inside that guarded branch and keep
StatPill usage unchanged.
---
Outside diff comments:
In `@web/src/hooks/useAgentDetailData.ts`:
- Around line 110-126: The WebSocket bindings capture agentName at creation and
cause stale/no-op behavior on transitions; fix by making the handler read the
live ref and guard empty names instead of closing over agentName: in
useAgentDetailData update the binding factory (used to build bindings passed to
useWebSocket) so each handler does const name = agentNameRef.current; if (!name)
return; then perform the debounce and call
useAgentsStore.getState().fetchAgentDetail(name) (still using wsDebounceRef and
WS_DEBOUNCE_MS); keep DETAIL_CHANNELS and bindings structure but ensure handlers
never close over the outer agentName value so handlers remain valid across
agentName changes and won't call fetchAgentDetail('').
🪄 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: 3d2f9ea4-33ff-49cc-a42a-31821d8bcbbf
📒 Files selected for processing (18)
web/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/__tests__/hooks/useAgentDetailData.test.tsweb/src/__tests__/hooks/useAgentsData.test.tsweb/src/__tests__/pages/org-edit/DepartmentsTab.test.tsxweb/src/__tests__/pages/org/build-org-tree.test.tsweb/src/__tests__/stores/company.test.tsweb/src/__tests__/utils/agents.property.test.tsweb/src/__tests__/utils/agents.test.tsweb/src/__tests__/utils/dashboard.test.tsweb/src/hooks/useAgentDetailData.tsweb/src/hooks/useAgentsData.tsweb/src/pages/AgentDetailPage.tsxweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/pages/org/DepartmentGroupNode.stories.tsxweb/src/pages/org/DepartmentGroupNode.tsxweb/src/pages/org/DepartmentStatsBar.tsxweb/src/pages/org/build-org-tree.tsweb/src/stores/budget.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: Build Web
- GitHub Check: Build Backend
- GitHub Check: Build Sandbox
- GitHub Check: Dashboard Test
- GitHub Check: Dependency Review
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (6)
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
web/src/**/*.{ts,tsx}: Web Dashboard: ALWAYS reuse existing components fromweb/src/components/ui/before creating new ones. Reuse StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton variants, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup, Drawer, InputField, SelectField, SliderField, ToggleField, TaskStatusIndicator, PriorityBadge, ProviderHealthBadge, TokenUsageBar, CodeMirrorEditor, SegmentedControl, ThemeToggle.
Do NOT recreate status dots inline—use<StatusBadge>. Do NOT build card-with-header layouts from scratch—use<SectionCard>. Do NOT create metric displays withtext-metric font-bold—use<MetricCard>. Do NOT render initials circles manually—use<Avatar>. Do NOT create complex (>8 line) JSX inside.map()—extract to a shared component.
Web design system enforcement: A PostToolUse hook (scripts/check_web_design_system.py) runs automatically on every Edit/Write toweb/src/files. It catches hardcoded hex colors, rgba values, font-family declarations, missing Storybook stories, duplicate patterns, and complex map blocks. Fix all violations before proceeding.
React 19 + shadcn/ui + Tailwind CSS 4 dashboard: Use React Router, Zustand stores,@tanstack/react-query,@xyflow/react,@dagrejs/dagre, d3-force,@dnd-kit, Recharts, Framer Motion, cmdk, js-yaml, Axios.
Files:
web/src/pages/AgentDetailPage.tsxweb/src/stores/budget.tsweb/src/__tests__/pages/org-edit/DepartmentsTab.test.tsxweb/src/__tests__/utils/agents.property.test.tsweb/src/__tests__/utils/agents.test.tsweb/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/pages/org/DepartmentGroupNode.stories.tsxweb/src/pages/org/DepartmentGroupNode.tsxweb/src/hooks/useAgentDetailData.tsweb/src/__tests__/stores/company.test.tsweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/__tests__/hooks/useAgentsData.test.tsweb/src/pages/org/DepartmentStatsBar.tsxweb/src/__tests__/pages/org/build-org-tree.test.tsweb/src/pages/org/build-org-tree.tsweb/src/hooks/useAgentsData.tsweb/src/__tests__/utils/dashboard.test.tsweb/src/__tests__/hooks/useAgentDetailData.test.ts
web/src/**/*.{ts,tsx,css}
📄 CodeRabbit inference engine (CLAUDE.md)
Design tokens: Colors use Tailwind semantic classes (
text-foreground,bg-card,text-accent, etc.) or CSS variables (var(--so-accent)). NEVER hardcode hex values. Typography usesfont-sansorfont-mono. Spacing uses density-aware tokens or standard Tailwind. Shadows/borders use token variables.
Files:
web/src/pages/AgentDetailPage.tsxweb/src/stores/budget.tsweb/src/__tests__/pages/org-edit/DepartmentsTab.test.tsxweb/src/__tests__/utils/agents.property.test.tsweb/src/__tests__/utils/agents.test.tsweb/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/pages/org/DepartmentGroupNode.stories.tsxweb/src/pages/org/DepartmentGroupNode.tsxweb/src/hooks/useAgentDetailData.tsweb/src/__tests__/stores/company.test.tsweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/__tests__/hooks/useAgentsData.test.tsweb/src/pages/org/DepartmentStatsBar.tsxweb/src/__tests__/pages/org/build-org-tree.test.tsweb/src/pages/org/build-org-tree.tsweb/src/hooks/useAgentsData.tsweb/src/__tests__/utils/dashboard.test.tsweb/src/__tests__/hooks/useAgentDetailData.test.ts
**/*.{ts,tsx,json}
📄 CodeRabbit inference engine (CLAUDE.md)
TypeScript 6.0: Remove
baseUrl(deprecated);pathsentries are relative to tsconfig directory.esModuleInteropalways true—cannot be set tofalse.typesdefaults to[]—must explicitly list (e.g.'types': ['vitest/globals']).DOM.Iterablemerged intoDOM.moduleResolution: 'classic'and'node10'removed—use'bundler'or'nodenext'.strictdefaults totrue.noUncheckedSideEffectImportsdefaults totrue.
Files:
web/src/pages/AgentDetailPage.tsxweb/src/stores/budget.tsweb/src/__tests__/pages/org-edit/DepartmentsTab.test.tsxweb/src/__tests__/utils/agents.property.test.tsweb/src/__tests__/utils/agents.test.tsweb/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/pages/org/DepartmentGroupNode.stories.tsxweb/src/pages/org/DepartmentGroupNode.tsxweb/src/hooks/useAgentDetailData.tsweb/src/__tests__/stores/company.test.tsweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/__tests__/hooks/useAgentsData.test.tsweb/src/pages/org/DepartmentStatsBar.tsxweb/src/__tests__/pages/org/build-org-tree.test.tsweb/src/pages/org/build-org-tree.tsweb/src/hooks/useAgentsData.tsweb/src/__tests__/utils/dashboard.test.tsweb/src/__tests__/hooks/useAgentDetailData.test.ts
**/*.{py,ts,tsx,go}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{py,ts,tsx,go}: Lint & format: Python via ruff (uv run ruff check+uv run ruff format). TypeScript web via ESLint (zero warnings enforced:npm --prefix web run lint). Go CLI via golangci-lint andgo vet. All enforced in pre-commit hooks.
Type checking: Python via mypy strict mode (uv run mypy src/ tests/). TypeScript vianpm --prefix web run type-check. Go viago -C cli vet ./....
Files:
web/src/pages/AgentDetailPage.tsxweb/src/stores/budget.tsweb/src/__tests__/pages/org-edit/DepartmentsTab.test.tsxweb/src/__tests__/utils/agents.property.test.tsweb/src/__tests__/utils/agents.test.tsweb/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/pages/org/DepartmentGroupNode.stories.tsxweb/src/pages/org/DepartmentGroupNode.tsxweb/src/hooks/useAgentDetailData.tsweb/src/__tests__/stores/company.test.tsweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/__tests__/hooks/useAgentsData.test.tsweb/src/pages/org/DepartmentStatsBar.tsxweb/src/__tests__/pages/org/build-org-tree.test.tsweb/src/pages/org/build-org-tree.tsweb/src/hooks/useAgentsData.tsweb/src/__tests__/utils/dashboard.test.tsweb/src/__tests__/hooks/useAgentDetailData.test.ts
web/src/pages/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Web dashboard pages: Lazy-loaded page components (one per route). Page-scoped sub-components in
pages/<page-name>/subdirs. Feature dirs added as pages are built. Stores for auth, WebSocket, toast, analytics, setup wizard, company, agents, budget, tasks, settings, providers, theme.
Files:
web/src/pages/AgentDetailPage.tsxweb/src/pages/org/DepartmentGroupNode.stories.tsxweb/src/pages/org/DepartmentGroupNode.tsxweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/pages/org/DepartmentStatsBar.tsxweb/src/pages/org/build-org-tree.ts
web/**/*.stories.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Storybook 10: ESM-only—no CJS support. Packages like
@storybook/addon-essentials,@storybook/addon-interactions,@storybook/test,@storybook/blocksno longer published.@storybook/addon-docsis separate. Import fromstorybook/test(not@storybook/test),storybook/actions. UsedefineMainfrom@storybook/react-vite/nodeanddefinePreviewfrom@storybook/react-vite. Backgrounds API: useparameters.backgrounds.options(object) +initialGlobals.backgrounds.value. a11y testing: useparameters.a11y.test: 'error' | 'todo' | 'off'. Minimum versions: Node 20.19+, Vite 5+, Vitest 3+, TypeScript 4.9+.
Files:
web/src/pages/org/DepartmentGroupNode.stories.tsx
🧠 Learnings (7)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/**/*.{ts,tsx} : Web Dashboard: ALWAYS reuse existing components from `web/src/components/ui/` before creating new ones. Reuse StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton variants, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup, Drawer, InputField, SelectField, SliderField, ToggleField, TaskStatusIndicator, PriorityBadge, ProviderHealthBadge, TokenUsageBar, CodeMirrorEditor, SegmentedControl, ThemeToggle.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/**/*.{ts,tsx} : React 19 + shadcn/ui + Tailwind CSS 4 dashboard: Use React Router, Zustand stores, tanstack/react-query, xyflow/react, dagrejs/dagre, d3-force, dnd-kit, Recharts, Framer Motion, cmdk, js-yaml, Axios.
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do NOT recreate status dots inline—use `<StatusBadge>`. Do NOT build card-with-header layouts from scratch—use `<SectionCard>`. Do NOT create metric displays with `text-metric font-bold`—use `<MetricCard>`. Do NOT render initials circles manually—use `<Avatar>`. Do NOT create complex (>8 line) JSX inside `.map()`—extract to a shared component.
Applied to files:
web/src/pages/AgentDetailPage.tsxweb/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/pages/org/DepartmentStatsBar.tsx
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/**/*.{ts,tsx} : Web Dashboard: ALWAYS reuse existing components from `web/src/components/ui/` before creating new ones. Reuse StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton variants, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup, Drawer, InputField, SelectField, SliderField, ToggleField, TaskStatusIndicator, PriorityBadge, ProviderHealthBadge, TokenUsageBar, CodeMirrorEditor, SegmentedControl, ThemeToggle.
Applied to files:
web/src/pages/AgentDetailPage.tsxweb/src/hooks/useAgentDetailData.tsweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/hooks/useAgentsData.tsweb/src/__tests__/utils/dashboard.test.ts
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to web/src/__tests__/**/*.{ts,js} : Dashboard testing: Vitest unit tests organized by feature under `web/src/__tests__/`. Use fast-check for property-based testing (`fc.assert` + `fc.property`).
Applied to files:
web/src/__tests__/pages/org-edit/DepartmentsTab.test.tsxweb/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/__tests__/utils/dashboard.test.ts
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : Web components: Place new shared components in `web/src/components/ui/` with kebab-case filenames. Create `.stories.tsx` with all states. Export props as TypeScript interface. Use design tokens exclusively—no hardcoded colors, fonts, or spacing. Import `cn` from `@/lib/utils` for conditional class merging.
Applied to files:
web/src/__tests__/components/ui/dept-health-bar.test.tsx
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/**/*.stories.{ts,tsx} : Storybook 10: ESM-only—no CJS support. Packages like `storybook/addon-essentials`, `storybook/addon-interactions`, `storybook/test`, `storybook/blocks` no longer published. `storybook/addon-docs` is separate. Import from `storybook/test` (not `storybook/test`), `storybook/actions`. Use `defineMain` from `storybook/react-vite/node` and `definePreview` from `storybook/react-vite`. Backgrounds API: use `parameters.backgrounds.options` (object) + `initialGlobals.backgrounds.value`. a11y testing: use `parameters.a11y.test: 'error' | 'todo' | 'off'`. Minimum versions: Node 20.19+, Vite 5+, Vitest 3+, TypeScript 4.9+.
Applied to files:
web/src/pages/org/DepartmentGroupNode.stories.tsx
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/**/*.{ts,tsx} : React 19 + shadcn/ui + Tailwind CSS 4 dashboard: Use React Router, Zustand stores, tanstack/react-query, xyflow/react, dagrejs/dagre, d3-force, dnd-kit, Recharts, Framer Motion, cmdk, js-yaml, Axios.
Applied to files:
web/src/pages/org/DepartmentGroupNode.tsxweb/src/hooks/useAgentDetailData.tsweb/src/hooks/useAgentsData.ts
🔇 Additional comments (15)
web/src/__tests__/pages/org-edit/DepartmentsTab.test.tsx (2)
13-13: Good fixture alignment with renamed health schema.Line 13 correctly uses
utilization_percentin test data, which matches the updatedDepartmentHealthshape and avoids legacy-field drift.
46-52: Nice strengthening of value-level rendering coverage.Lines 51-52 correctly assert
72%, so the test now verifies the mappedutilization_percentoutput—not just meter presence.web/src/stores/budget.ts (1)
125-130: Identifier fallback mapping is now correctly hardened.This block now indexes
agentNameMap/agentDeptMapby bothagent.nameandagent.id(when present), which aligns with downstream lookups keyed byagent_idand avoids silentUnknown/raw-ID fallbacks.web/src/__tests__/stores/company.test.ts (3)
53-61: DepartmentHealth fixture alignment looks correct.The updated mock shape (
department_name,department_cost_7d,utilization_percent, etc.) is consistent with the backend-alignedDepartmentHealthcontract.
158-158: Assertion key rename is correct.Using
department_namehere keeps the failed-fetch filtering test aligned with the updated health payload structure.
392-392: Reorder ID fallback expectation is correctly updated.Switching to
a.id ?? a.namematches the store’sidOflogic, so this test now correctly covers agents withoutid.Also applies to: 400-400, 406-406
web/src/__tests__/components/ui/dept-health-bar.test.tsx (2)
31-63: Prop-aligned test updates look correct.These updated renders are consistent with the current
DeptHealthBarAPI (notaskCount) and keep each behavior test focused.
89-101: Great nullable-health coverage added.Validating both
nullandundefinedwithN/Aplus nometerrole is a solid regression guard for fallback behavior.web/src/__tests__/utils/agents.property.test.ts (1)
40-40:arbAgentnow correctly mirrors the updatedAgentConfigshape.Optional
id/statusgeneration and requiredauthorityinclusion are aligned with the backend-synced type contract.Also applies to: 48-48, 74-74
web/src/__tests__/utils/dashboard.test.ts (2)
187-203: Past review feedback has been addressed.The helper function now correctly types the
nameparameter asDepartmentHealth['department_name']and removes the inline cast, making the fixture strongly typed.
210-232: Excellent test coverage forcomputeOrgHealth.The tests comprehensively cover the production behavior: aggregation of
utilization_percent, rounding, and filtering of non-finite values (NaN/Infinity). The new tests at lines 222-232 correctly verify the filtering logic that matches the production implementation's use ofNumber.isFinite(d.utilization_percent).web/src/hooks/useAgentsData.ts (1)
54-68: Debounced WS refetch flow looks correct.Good trailing-edge debounce wiring and cleanup; this avoids burst refetch storms and prevents post-unmount timer execution.
web/src/__tests__/hooks/useAgentsData.test.ts (1)
132-195: Debounce test matrix is solid.Nice coverage of trailing behavior, coalescing, reset semantics, and unmount cleanup for WS-triggered refetches.
web/src/pages/org-edit/AgentEditDrawer.tsx (2)
56-56: Consistent nullish-status fallback is solid.Using
'active'as the shared fallback in state init, badge display, and select value keeps behavior consistent with optionalAgentConfig.status.Also applies to: 122-123, 155-155
72-78: Model display narrowing is correctly hardened.Filtering to string-only
provider/model_idparts and rendering conditionally avoids[object Object]output and stray separators.Also applies to: 162-164
| describe('WebSocket debounce', () => { | ||
| let wsHandler: (...args: unknown[]) => void | ||
|
|
||
| async function setupHandler() { | ||
| const { useWebSocket } = await import('@/hooks/useWebSocket') | ||
| renderHook(() => useAgentDetailData('alice')) | ||
| const bindings = vi.mocked(useWebSocket).mock.calls[0]![0].bindings | ||
| wsHandler = bindings[0]!.handler as (...args: unknown[]) => void | ||
| // Clear the initial fetchAgentDetail call from mount | ||
| mockFetchAgentDetail.mockClear() | ||
| } | ||
|
|
||
| beforeEach(() => { | ||
| vi.useFakeTimers() | ||
| }) | ||
|
|
||
| afterEach(() => { | ||
| vi.useRealTimers() | ||
| }) | ||
|
|
||
| it('does not call fetchAgentDetail synchronously on WS event', async () => { | ||
| await setupHandler() | ||
| wsHandler() | ||
| expect(mockFetchAgentDetail).not.toHaveBeenCalled() | ||
| }) | ||
|
|
||
| it('calls fetchAgentDetail after 300ms debounce', async () => { | ||
| await setupHandler() | ||
| wsHandler() | ||
| vi.advanceTimersByTime(300) | ||
| expect(mockFetchAgentDetail).toHaveBeenCalledTimes(1) | ||
| expect(mockFetchAgentDetail).toHaveBeenCalledWith('alice') | ||
| }) | ||
|
|
||
| it('coalesces burst events into a single fetch', async () => { | ||
| await setupHandler() | ||
| for (let i = 0; i < 5; i++) wsHandler() | ||
| vi.advanceTimersByTime(300) | ||
| expect(mockFetchAgentDetail).toHaveBeenCalledTimes(1) | ||
| }) | ||
|
|
||
| it('resets debounce timer on subsequent event within window', async () => { | ||
| await setupHandler() | ||
| wsHandler() | ||
| vi.advanceTimersByTime(200) | ||
| wsHandler() // resets the 300ms window | ||
| vi.advanceTimersByTime(200) | ||
| expect(mockFetchAgentDetail).not.toHaveBeenCalled() // only 200ms since last event | ||
| vi.advanceTimersByTime(100) | ||
| expect(mockFetchAgentDetail).toHaveBeenCalledTimes(1) | ||
| expect(mockFetchAgentDetail).toHaveBeenCalledWith('alice') | ||
| }) | ||
|
|
||
| it('cleans up timeout on unmount', async () => { | ||
| const { useWebSocket } = await import('@/hooks/useWebSocket') | ||
| const { unmount } = renderHook(() => useAgentDetailData('alice')) | ||
| const bindings = vi.mocked(useWebSocket).mock.calls[0]![0].bindings | ||
| const handler = bindings[0]!.handler as (...args: unknown[]) => void | ||
| mockFetchAgentDetail.mockClear() | ||
|
|
||
| handler() | ||
| unmount() | ||
| vi.advanceTimersByTime(300) | ||
| expect(mockFetchAgentDetail).not.toHaveBeenCalled() | ||
| }) | ||
|
|
||
| it('coalesces events across agents and tasks channels', async () => { | ||
| const { useWebSocket } = await import('@/hooks/useWebSocket') | ||
| renderHook(() => useAgentDetailData('alice')) | ||
| const bindings = vi.mocked(useWebSocket).mock.calls[0]![0].bindings | ||
| const agentsHandler = bindings[0]!.handler as (...args: unknown[]) => void | ||
| const tasksHandler = bindings[1]!.handler as (...args: unknown[]) => void | ||
| mockFetchAgentDetail.mockClear() | ||
|
|
||
| agentsHandler() | ||
| tasksHandler() | ||
| agentsHandler() | ||
| vi.advanceTimersByTime(300) | ||
| expect(mockFetchAgentDetail).toHaveBeenCalledTimes(1) | ||
| }) | ||
| }) |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Add transition regression tests for agentName lifecycle.
Please add a rerender case for '' -> 'alice' and 'alice' -> '' to validate WS handler setup/teardown behavior across param transitions, not only steady-state debounce timing.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/__tests__/hooks/useAgentDetailData.test.ts` around lines 179 - 259,
Add tests that exercise agentName prop transitions by rendering
useAgentDetailData with '' then rerendering to 'alice' and vice versa to verify
WebSocket handler setup/teardown; specifically, in the WebSocket debounce suite
add two cases: one that calls renderHook(() => useAgentDetailData('')) then
rerender('alice'), fetches the new bindings from
vi.mocked(useWebSocket).mock.calls[0][0].bindings, triggers the handler(s),
advances timers and asserts mockFetchAgentDetail was called with 'alice'; and a
reverse case that starts with 'alice', rerenders to '' and asserts the previous
handlers were cleaned up (trigger handler after rerender/unmount and advance
timers to confirm mockFetchAgentDetail is not called). Ensure you reuse
setupHandler pattern (useWebSocket, wsHandler/agentsHandler/tasksHandler), clear
mockFetchAgentDetail between steps, and call
vi.useFakeTimers()/vi.useRealTimers() as in existing tests.
| it('treats agent with undefined status as active for sorting', () => { | ||
| const withUndefined = [ | ||
| makeAgent({ name: 'Active Agent', status: 'active' }), | ||
| makeAgent({ name: 'No Status', status: undefined }), | ||
| makeAgent({ name: 'Terminated Agent', status: 'terminated' }), | ||
| ] | ||
| const result = sortAgents(withUndefined, 'status', 'asc') | ||
| // undefined status treated as 'active' -- should sort with active agents, before terminated | ||
| const names = result.map((a) => a.name) | ||
| expect(names.indexOf('Terminated Agent')).toBeGreaterThan(names.indexOf('No Status')) | ||
| }) |
There was a problem hiding this comment.
Cover the undefined => active sort case more precisely.
This only proves undefined sorts before terminated. If sortAgents accidentally ranked a missing status as onboarding, this test would still pass. Add an explicit onboarding case and assert No Status sorts ahead of it.
💡 Tighten the expectation
const withUndefined = [
makeAgent({ name: 'Active Agent', status: 'active' }),
makeAgent({ name: 'No Status', status: undefined }),
+ makeAgent({ name: 'Onboarding Agent', status: 'onboarding' }),
makeAgent({ name: 'Terminated Agent', status: 'terminated' }),
]
const result = sortAgents(withUndefined, 'status', 'asc')
- // undefined status treated as 'active' -- should sort with active agents, before terminated
const names = result.map((a) => a.name)
+ expect(names.indexOf('No Status')).toBeLessThan(names.indexOf('Onboarding Agent'))
expect(names.indexOf('Terminated Agent')).toBeGreaterThan(names.indexOf('No Status'))📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| it('treats agent with undefined status as active for sorting', () => { | |
| const withUndefined = [ | |
| makeAgent({ name: 'Active Agent', status: 'active' }), | |
| makeAgent({ name: 'No Status', status: undefined }), | |
| makeAgent({ name: 'Terminated Agent', status: 'terminated' }), | |
| ] | |
| const result = sortAgents(withUndefined, 'status', 'asc') | |
| // undefined status treated as 'active' -- should sort with active agents, before terminated | |
| const names = result.map((a) => a.name) | |
| expect(names.indexOf('Terminated Agent')).toBeGreaterThan(names.indexOf('No Status')) | |
| }) | |
| it('treats agent with undefined status as active for sorting', () => { | |
| const withUndefined = [ | |
| makeAgent({ name: 'Active Agent', status: 'active' }), | |
| makeAgent({ name: 'No Status', status: undefined }), | |
| makeAgent({ name: 'Onboarding Agent', status: 'onboarding' }), | |
| makeAgent({ name: 'Terminated Agent', status: 'terminated' }), | |
| ] | |
| const result = sortAgents(withUndefined, 'status', 'asc') | |
| const names = result.map((a) => a.name) | |
| expect(names.indexOf('No Status')).toBeLessThan(names.indexOf('Onboarding Agent')) | |
| expect(names.indexOf('Terminated Agent')).toBeGreaterThan(names.indexOf('No Status')) | |
| }) |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/__tests__/utils/agents.test.ts` around lines 219 - 229, Update the
test "treats agent with undefined status as active for sorting" to include an
explicit onboarding case so undefined isn't accidentally treated as onboarding;
add makeAgent({ name: 'Onboarding Agent', status: 'onboarding' }) to the
withUndefined array and then assert that names.indexOf('No Status') is less than
names.indexOf('Onboarding Agent') (and keep/assert that it's also less than
names.indexOf('Terminated Agent') if desired) to ensure undefined is treated as
active.
| function dh( | ||
| name: DepartmentHealth['department_name'], | ||
| utilization: number, | ||
| agents = 1, | ||
| ): DepartmentHealth { |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider a more descriptive helper function name.
The helper name dh is quite terse. While short names are sometimes acceptable in test files, a more descriptive name like makeDepartmentHealth or createDeptHealth would improve readability and make the test code more self-documenting.
♻️ Proposed refactor
-function dh(
+function makeDepartmentHealth(
name: DepartmentHealth['department_name'],
utilization: number,
agents = 1,
): DepartmentHealth {Then update all call sites:
- expect(computeOrgHealth([dh('engineering', 85, 4)])).toBe(85)
+ expect(computeOrgHealth([makeDepartmentHealth('engineering', 85, 4)])).toBe(85)📝 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.
| function dh( | |
| name: DepartmentHealth['department_name'], | |
| utilization: number, | |
| agents = 1, | |
| ): DepartmentHealth { | |
| function makeDepartmentHealth( | |
| name: DepartmentHealth['department_name'], | |
| utilization: number, | |
| agents = 1, | |
| ): DepartmentHealth { |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/__tests__/utils/dashboard.test.ts` around lines 187 - 191, The helper
function name dh is too terse and hurts readability; rename the function dh to a
more descriptive name like makeDepartmentHealth or createDeptHealth across the
file and update all call sites in the test suite accordingly (e.g., rename the
function declaration for dh and replace every invocation of dh(...) with
makeDepartmentHealth(...) so imports/uses remain consistent while preserving the
same parameters and return type DepartmentHealth).
| ) | ||
| const hiredDate = useMemo( | ||
| () => agent ? new Date(agent.hiring_date).toLocaleDateString() : '', | ||
| () => agent?.hiring_date ? new Date(agent.hiring_date).toLocaleDateString() : '', |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "1) Locate AgentConfig + hiring_date typing"
rg -n -C3 --type=ts '\b(AgentConfig|hiring_date)\b'
echo
echo "2) Find date-only hiring_date fixtures/usages (YYYY-MM-DD)"
rg -n -C2 --type=ts --type=tsx "hiring_date\\s*[:=]\\s*['\"][0-9]{4}-[0-9]{2}-[0-9]{2}['\"]"
echo
echo "3) Find datetime hiring_date fixtures/usages (ISO timestamp)"
rg -n -C2 --type=ts --type=tsx "hiring_date\\s*[:=]\\s*['\"][0-9]{4}-[0-9]{2}-[0-9]{2}T"Repository: Aureliolo/synthorg
Length of output: 50419
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== 1. Find formatDate implementation ==="
rg -n "export.*formatDate" web/src/utils/
echo
echo "=== 2. Show formatDate full implementation ==="
rg -A 15 "export.*function formatDate" web/src/utils/
echo
echo "=== 3. Check how AgentIdentityHeader uses formatDate ==="
rg -B 2 -A 2 "formatDate.*hiring" web/src/pages/agents/
echo
echo "=== 4. See if there's a formatRelativeTime as well ==="
rg -A 10 "export.*function formatRelativeTime" web/src/utils/
echo
echo "=== 5. Check actual test coverage for date parsing edge cases ==="
rg -n "hiring_date" web/src/__tests__/utils/ | head -20Repository: Aureliolo/synthorg
Length of output: 3579
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Check AgentEditDrawer usage of hiredDate ==="
rg -A 20 "const hiredDate = useMemo" web/src/pages/org-edit/AgentEditDrawer.tsx
echo
echo "=== Check where hiredDate is rendered ==="
rg -B 3 -A 3 "hiredDate" web/src/pages/org-edit/AgentEditDrawer.tsx
echo
echo "=== Full context of the file around line 69 ==="
sed -n '60,180p' web/src/pages/org-edit/AgentEditDrawer.tsxRepository: Aureliolo/synthorg
Length of output: 5391
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Check backend schema/docs for hiring_date format ==="
fd -type f \( -name "*.md" -o -name "*.py" \) | xargs grep -l "hiring_date" 2>/dev/null | head -5
echo
echo "=== Search for backend schema definition ==="
find . -path ./node_modules -prune -o -name "schema*.py" -o -name "*config*.py" | xargs grep -l "hiring_date" 2>/dev/null
echo
echo "=== Check API response examples/fixtures ==="
rg "hiring_date" web/src/api/ -A 2 -B 2
echo
echo "=== Check if there's any documentation or migration notes ==="
fd -type f \( -name "CHANGELOG*" -o -name "README*" \) | xargs grep -i "hiring" 2>/dev/nullRepository: Aureliolo/synthorg
Length of output: 308
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Search for backend schema files ==="
find . -name "*.py" -path "*/config/*" -o -name "schema.py" 2>/dev/null | head -10
echo
echo "=== Check for hiring_date in backend config/schema ==="
rg "hiring_date" --type=py -B 3 -A 3 2>/dev/null | head -40
echo
echo "=== Look for API documentation or README ==="
ls -la README* CHANGELOG* 2>/dev/null | head -5
echo
echo "=== Check if date-only format is referenced in tests or comments ==="
rg "hiring_date.*YYYY|date-only|2026-01-01[^T]" -B 2 -A 2 2>/dev/nullRepository: Aureliolo/synthorg
Length of output: 3247
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Check AgentConfig schema in backend ==="
rg -A 20 "class AgentConfig" src/synthorg/config/schema.py
echo
echo "=== Check how hiring_date is typed and serialized ==="
rg -B 5 -A 5 "hiring_date" src/synthorg/config/schema.py
echo
echo "=== Check API serialization/JSON encoding ==="
fd -name "*.py" -path "*/api/*" -o -path "*/routes/*" | xargs grep -l "hiring_date" 2>/dev/null | head -3Repository: Aureliolo/synthorg
Length of output: 1602
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Check AgentIdentity class for hiring_date ==="
rg "class AgentIdentity" src/synthorg/ -A 30 | head -50
echo
echo "=== Find where hiring_date is set/serialized ==="
rg "hiring_date" src/synthorg/ --type=py | grep -E "(date\(|datetime|isoformat|strftime)" | head -10
echo
echo "=== Check core agent model ==="
rg -A 5 "hiring_date" src/synthorg/core/agent.py | head -30Repository: Aureliolo/synthorg
Length of output: 2800
Add Invalid Date guard and use date-only safe parsing for hiring_date.
Date-only strings like "2026-01-01" parsed via new Date() will shift backward in US timezones (interpreted as UTC midnight). Add a guard for invalid dates and parse date-only values safely:
Suggested fix
- const hiredDate = useMemo(
- () => agent?.hiring_date ? new Date(agent.hiring_date).toLocaleDateString() : '',
- [agent],
- )
+ const hiredDate = useMemo(() => {
+ if (!agent?.hiring_date) return ''
+ const parsed = new Date(agent.hiring_date)
+ return Number.isNaN(parsed.getTime()) ? '' : parsed.toLocaleDateString()
+ }, [agent?.hiring_date])Also update the dependency array from [agent] to [agent?.hiring_date] to avoid unnecessary memoization.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/pages/org-edit/AgentEditDrawer.tsx` at line 69, The current memoized
formatter uses new Date(agent.hiring_date) which can shift date-only strings
across timezones and doesn't guard invalid dates; update the function that
returns agent?.hiring_date ? ... to parse date-only strings safely (e.g., detect
YYYY-MM-DD and construct a Date with Date.UTC(year, month-1, day) or create with
local year/month/day components), check the resulting Date with
isNaN(date.getTime()) and return '' for invalid values, and change the memo
dependency from [agent] to [agent?.hiring_date] so the memo only recalculates
when the hiring_date value changes.
| cost7d, | ||
| currency = 'EUR', | ||
| className, | ||
| }: DepartmentStatsBarProps) { | ||
| return ( | ||
| <div className={cn('flex flex-wrap gap-1.5', className)} data-testid="dept-stats-bar"> | ||
| <StatPill label="Agents" value={agentCount} /> | ||
| <StatPill label="Active" value={activeCount} /> | ||
| <StatPill label="Tasks" value={taskCount} /> | ||
| {costUsd !== null && <StatPill label="Cost" value={formatCurrency(costUsd, currency)} />} | ||
| {cost7d !== null && Number.isFinite(cost7d) && ( | ||
| <StatPill label="Cost (7d)" value={formatCurrency(cost7d, currency)} /> |
There was a problem hiding this comment.
Don't hardcode 'EUR' as the fallback currency.
cost7d and currency now travel together from backend health data. Defaulting to EUR here turns a missing prop into a plausible-but-wrong money value instead of surfacing the data bug. Require currency when rendering the pill, or skip the cost pill when it's absent.
💡 Safer rendering contract
interface DepartmentStatsBarProps {
agentCount: number
activeCount: number
cost7d: number | null
- currency?: string
+ currency?: string | null
className?: string
}
export function DepartmentStatsBar({
agentCount,
activeCount,
cost7d,
- currency = 'EUR',
+ currency,
className,
}: DepartmentStatsBarProps) {
return (
<div className={cn('flex flex-wrap gap-1.5', className)} data-testid="dept-stats-bar">
<StatPill label="Agents" value={agentCount} />
<StatPill label="Active" value={activeCount} />
- {cost7d !== null && Number.isFinite(cost7d) && (
+ {cost7d !== null && Number.isFinite(cost7d) && currency && (
<StatPill label="Cost (7d)" value={formatCurrency(cost7d, currency)} />
)}
</div>
)
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/pages/org/DepartmentStatsBar.tsx` around lines 16 - 25, Remove the
hardcoded fallback currency and ensure the cost pill only renders when a real
currency is provided: update DepartmentStatsBarProps/DepartmentStatsBar to not
default currency = 'EUR' (make currency required or optional without a default),
and change the conditional around the cost pill to check both cost7d is finite
and currency is present (e.g., if cost7d !== null && Number.isFinite(cost7d) &&
currency). Use formatCurrency(cost7d, currency) only inside that guarded branch
and keep StatPill usage unchanged.
Update DepartmentHealth interface to match the backend Pydantic model: rename name->department_name, health_percent->utilization_percent, cost_usd->department_cost_7d; remove display_name and task_count; add active_agent_count, currency, avg_performance_score, cost_trend, collaboration_score. Cascade to all 20+ consumer files including DeptHealthBar (remove taskCount prop), DepartmentStatsBar (remove taskCount, rename costUsd->cost7d), DepartmentGroupData, OrgHealthSection, build-org-tree, stores, tests, and stories. Closes #892 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…878) Make id, status, hiring_date optional (they are AgentIdentity runtime fields not present in backend AgentConfig). Remove skills (unused by any component). Change personality, model, memory, tools from typed interfaces to Record<string, unknown> to match backend dict[str, Any]. Add optional authority field. All consumers now use fallbacks: agent.id ?? agent.name, agent.status ?? 'active', conditional render for hiring_date. Cascade to build-org-tree, AgentGridView, AgentIdentityHeader, AgentEditDrawer, AgentsTab, company store, budget store, utils/agents, and all stories/tests. Closes #878 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ching (#877) Define ActivityEventType with 13 known values matching backend event types (hired, fired, promoted, demoted, onboarded, offboarded, status_changed, task_completed, task_started, cost_incurred, tool_used, delegation_sent, delegation_received). Use in AgentActivityEvent with (string & {}) fallback for unknown future types. Type-narrow ACTIVITY_ICON_MAP to Partial<Record<ActivityEventType, LucideIcon>>. Closes #877 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add 300ms debounce to WebSocket event handlers in useAgentsData and useAgentDetailData to coalesce burst events (e.g. batch agent updates) into a single API refetch. Uses useRef for the timeout ID so the handler closure captured on mount always accesses the current timer. Cleanup effect clears pending timeouts on unmount. Add 4 tests per hook: sync suppression, debounce firing, burst coalescing, and unmount cleanup. Closes #876 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pre-reviewed by 5 agents, 11 findings addressed:
- Remove dead interfaces (PersonalityConfig, ModelConfig, SkillSet,
MemoryConfig, ToolPermissions) from types.ts
- Make authority required on AgentConfig (backend always sends {})
- Replace as string[] cast with runtime type filter in AgentDetailPage
- Fix wsDebouncRef typo -> wsDebounceRef in both hooks
- Update CLAUDE.md and brand-and-ux.md DeptHealthBar inventory
- Add JSDoc on range-bounded DepartmentHealth fields
- Add tests for id:undefined and status:undefined fallback paths
- Add cross-channel debounce coalescing test
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ents Thread per-department currency through org-chart nodes (was hardcoding EUR fallback). Use typeof guards for Record<string, unknown> property access in AgentEditDrawer and AgentDetailPage. Harden budget store agent lookup maps to register both id and name keys. Clear debounce timer on agentName changes (not just unmount). Guard DepartmentStatsBar cost rendering against NaN/Infinity. Add 5 new tests: DeptHealthBar null/undefined health, sortAgents with undefined status, trailing-edge debounce reset for both hooks. Fix company store test assertions to use id ?? name fallback. Tighten dashboard test fixture typing. Add undefined status to property test arbitrary. Assert rendered percentage in DepartmentsTab test. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Strengthen undefined-status sort test to verify same-rank ordering with explicit active agents. Extract dense tools.allowed filtering in AgentDetailPage to a named variable for readability. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
8d5a59a to
df79937
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
web/src/pages/agents/AgentGridView.stories.tsx (1)
49-61: 🧹 Nitpick | 🔵 TrivialConsider adding a11y testing parameter.
Other stories in this PR include
parameters: { a11y: { test: 'error' } }. Adding this would ensure accessibility testing consistency across stories.♻️ Suggested addition
const meta = { title: 'Agents/AgentGridView', component: AgentGridView, + parameters: { + a11y: { test: 'error' }, + }, decorators: [🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/pages/agents/AgentGridView.stories.tsx` around lines 49 - 61, The story meta for AgentGridView is missing the accessibility testing parameter; update the meta object (the const meta that satisfies Meta<typeof AgentGridView>) to include parameters: { a11y: { test: 'error' } } so the story enables the same a11y testing as other stories in the PR.web/src/pages/org-edit/AgentEditDrawer.tsx (1)
48-62:⚠️ Potential issue | 🟠 MajorMove state synchronization out of render.
Lines 48–62 call
setForm,setSubmitError,setDeleteOpen, andsetDeletingduring render, which violates React's rules and can cause Strict Mode warnings and render churn. Move this logic intouseEffect([agent]).♻️ Proposed fix
-import { useCallback, useMemo, useRef, useState } from 'react' +import { useCallback, useEffect, useMemo, useState } from 'react' @@ - const prevAgentRef = useRef<typeof agent | undefined>(undefined) - if (agent !== prevAgentRef.current) { - prevAgentRef.current = agent - if (agent) { - setForm({ - name: agent.name, - role: agent.role, - department: agent.department, - level: agent.level, - status: agent.status ?? 'active', - }) - setSubmitError(null) - } - setDeleteOpen(false) - setDeleting(false) - } + useEffect(() => { + if (agent) { + setForm({ + name: agent.name, + role: agent.role, + department: agent.department, + level: agent.level, + status: agent.status ?? 'active', + }) + setSubmitError(null) + } + setDeleteOpen(false) + setDeleting(false) + }, [agent])🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/pages/org-edit/AgentEditDrawer.tsx` around lines 48 - 62, The state-sync block that compares agent to prevAgentRef.current and calls setForm, setSubmitError, setDeleteOpen, and setDeleting should be moved out of render into a useEffect that depends on agent; create a useEffect(() => { ... }, [agent]) which performs the same logic (update prevAgentRef.current, call setForm with agent fields, setSubmitError(null), setDeleteOpen(false), setDeleting(false)) and remove the inline state updates from the render path so only pure reads occur during render and all side-effects run inside the useEffect.
♻️ Duplicate comments (6)
web/src/hooks/useAgentsData.ts (1)
9-10: 🧹 Nitpick | 🔵 TrivialExtract shared WS debounce policy into a single helper/module.
WS_DEBOUNCE_MSand timeout reset logic are duplicated here and inuseAgentDetailData. Centralizing avoids behavioral drift when tuning debounce behavior later.♻️ Suggested direction
-const WS_DEBOUNCE_MS = 300 +import { WS_DEBOUNCE_MS, resetDebounceTimer } from '@/hooks/wsDebounce' - if (wsDebounceRef.current) clearTimeout(wsDebounceRef.current) - wsDebounceRef.current = setTimeout(() => { - useAgentsStore.getState().fetchAgents() - }, WS_DEBOUNCE_MS) + wsDebounceRef.current = resetDebounceTimer(wsDebounceRef.current, () => { + useAgentsStore.getState().fetchAgents() + }, WS_DEBOUNCE_MS)Also applies to: 54-58, 65-68
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/hooks/useAgentsData.ts` around lines 9 - 10, WS_DEBOUNCE_MS and the timeout reset logic are duplicated between useAgentsData.ts and useAgentDetailData.ts; extract a single shared helper (e.g., createWsDebounce or a constant and a resetDebounce function) into a new module and import it from both hooks so both use the same debounce value and reset behavior. Update references to WS_DEBOUNCE_MS in useAgentsData.ts and useAgentDetailData.ts to import the constant, move any timeout/reset logic into the shared helper (referencing the same function name like resetWsDebounce or useWsDebounce) and replace the local implementations in both hooks to call the shared utility.web/src/__tests__/hooks/useAgentDetailData.test.ts (1)
179-259: 🧹 Nitpick | 🔵 TrivialAdd
agentNametransition tests for handler setup/teardown.Please add rerender cases for
'' → 'alice'and'alice' → ''to validate debounce handler lifecycle across route-param transitions, not only steady state.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/__tests__/hooks/useAgentDetailData.test.ts` around lines 179 - 259, Add two tests that exercise agentName transitions for useAgentDetailData: (1) initial '' -> 'alice' should set up a new debounce handler when rerendered to 'alice' and trigger fetchAgentDetail after 300ms when its handler is invoked; (2) initial 'alice' -> '' should tear down the debounce handler so invoking the previous handler after rerender/unmount does not call fetchAgentDetail. Implement each by using renderHook(({ name }) => useAgentDetailData(name), { initialProps: '<start>' }) and then rerender({ name: '<end>' }); capture handlers from vi.mocked(useWebSocket).mock.calls after each render/rerender, call the appropriate handler(s), use vi.advanceTimersByTime to assert debounce behavior (no immediate call, single call after 300ms), and assert timeouts are cleared when transitioning away or unmounting (mockFetchAgentDetail remains not called).web/src/pages/org/DepartmentStatsBar.tsx (1)
17-26:⚠️ Potential issue | 🟠 MajorDo not default missing currency to
'EUR'when rendering cost.Line [17] can silently mislabel monetary values. Require a real
currencyfor Line [25], otherwise skip the cost pill.💡 Proposed fix
interface DepartmentStatsBarProps { agentCount: number activeCount: number cost7d: number | null - currency?: string + currency?: string | null className?: string } @@ export function DepartmentStatsBar({ agentCount, activeCount, cost7d, - currency = 'EUR', + currency, className, }: DepartmentStatsBarProps) { @@ - {cost7d !== null && Number.isFinite(cost7d) && ( + {cost7d !== null && Number.isFinite(cost7d) && currency && ( <StatPill label="Cost (7d)" value={formatCurrency(cost7d, currency)} /> )}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/pages/org/DepartmentStatsBar.tsx` around lines 17 - 26, The component currently defaults currency to 'EUR' which can silently mislabel amounts; remove the defaulting in the DepartmentStatsBar parameter (don't set currency = 'EUR') and update the cost pill render condition to only show when cost7d is not null, Number.isFinite(cost7d) is true, and a real currency is provided (e.g. currency != null && currency !== ''). Adjust the DepartmentStatsBarProps type if necessary so callers must supply currency when they want a cost shown, and keep using StatPill and formatCurrency(cost7d, currency) unchanged when the currency is present.web/src/pages/org-edit/AgentEditDrawer.tsx (1)
69-71:⚠️ Potential issue | 🟡 MinorParse
hiring_datesafely for date-only values.Line [69] uses
new Date(agent.hiring_date), which can shiftYYYY-MM-DDby timezone and produce incorrect day in US locales. Also, Line [70] should depend onagent?.hiring_daterather than the fullagentobject.🛡️ Proposed fix
- const hiredDate = useMemo( - () => agent?.hiring_date ? new Date(agent.hiring_date).toLocaleDateString() : '', - [agent], - ) + const hiredDate = useMemo(() => { + if (!agent?.hiring_date) return '' + const raw = agent.hiring_date + const dateOnly = /^(\d{4})-(\d{2})-(\d{2})$/.exec(raw) + const parsed = dateOnly + ? new Date(Number(dateOnly[1]), Number(dateOnly[2]) - 1, Number(dateOnly[3])) + : new Date(raw) + return Number.isNaN(parsed.getTime()) ? '' : parsed.toLocaleDateString() + }, [agent?.hiring_date])#!/bin/bash # Verify current hiring_date parsing and memo dependencies rg -n -C2 "new Date\(agent\.hiring_date\)|const hiredDate = useMemo|\[agent\]" web/src/pages/org-edit/AgentEditDrawer.tsx🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/pages/org-edit/AgentEditDrawer.tsx` around lines 69 - 71, The current useMemo that formats the hiring date uses new Date(agent.hiring_date) which can shift YYYY-MM-DD across timezones and also depends on the whole agent object; update the memo to depend on agent?.hiring_date and parse the string as a date-only value (e.g., split "YYYY-MM-DD" into year/month/day and construct a UTC/local date without time) before calling toLocaleDateString() so the displayed day never shifts across timezones; reference the existing useMemo block that formats agent.hiring_date and the toLocaleDateString() call to locate where to change the parsing and the dependency array.web/src/__tests__/pages/org/build-org-tree.test.ts (1)
337-340:⚠️ Potential issue | 🟡 MinorRemove the
undefined as unknown as stringescape hatch.This cast bypasses the optional
AgentConfig.idbehavior the test is supposed to cover. If the helper ever starts supplying a defaultidagain, this test can keep compiling while no longer exercising the missing-id path. Make the helper acceptid?: stringand passundefined(or omit the field) here.💡 Keep the fixture type-safe
-function makeAgent(overrides: Partial<AgentConfig> & { id: string; name: string }): AgentConfig { +function makeAgent(overrides: Partial<AgentConfig> & { name: string; id?: string }): AgentConfig {- makeAgent({ id: undefined as unknown as string, name: 'NoIdAgent', department: 'engineering', level: 'mid' }), + makeAgent({ id: undefined, name: 'NoIdAgent', department: 'engineering', level: 'mid' }),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/__tests__/pages/org/build-org-tree.test.ts` around lines 337 - 340, Update the test fixture and helper so the test truly exercises a missing id: change the makeAgent helper signature to accept id?: string (make AgentConfig.id optional) and in the test "uses agent.name as node id when agent.id is undefined" pass undefined directly or omit the id field instead of using the cast `undefined as unknown as string`; update references to makeAgent and any types (e.g., AgentConfig) to reflect optional id so the test compiles and correctly covers the missing-id path.web/src/pages/org/build-org-tree.ts (1)
97-98:⚠️ Potential issue | 🟠 Major
agent.nameis still not a safe surrogate key here.
idis now optional, so everyid ?? namefallback in this graph builder treats names as globally unique. Two missing-id agents with the same name will collide inteamMemberSet, node ids, edge ids, runtime-status lookups, and CEO/head exclusion, which can merge nodes or wire edges to the wrong person. Generate one deterministic unique key per agent and reuse it everywhere instead of falling back toagent.name.Also applies to: 126-129, 136-152, 161-197, 237-239
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/pages/org/build-org-tree.ts` around lines 97 - 98, The code improperly treats agent.name as a surrogate key whenever agent.id is missing (seen around resolveRuntimeStatus usage and in teamMemberSet, node id/edge id generation, runtime-status lookups, and CEO/head exclusion), causing collisions for multiple agents with the same name; fix by computing a deterministic unique key for each agent (e.g., a stable concat/hash of available unique fields such as name + email/role + array index) once when building the graph and reuse that key everywhere instead of using id ?? name — update the places referencing id ?? name (including resolveRuntimeStatus calls, teamMemberSet population, node/edge id creation, runtime-status map lookups, and CEO/head exclusion logic) to use the new agentKey value so all lookups and sets remain consistent and collision-free.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/design/brand-and-ux.md`:
- Line 203: Update the docs for DeptHealthBar in dept-health-bar.tsx: replace
the phrase "utilization percentage (null-safe -- shows N/A when health
unavailable)" with wording that uses "utilization unavailable" to match the API
semantics (for example: "utilization (null-safe — shows N/A when utilization
unavailable)"). Keep the rest of the description (animated horizontal fill,
color auto-mapped via getHealthColor()) unchanged.
---
Outside diff comments:
In `@web/src/pages/agents/AgentGridView.stories.tsx`:
- Around line 49-61: The story meta for AgentGridView is missing the
accessibility testing parameter; update the meta object (the const meta that
satisfies Meta<typeof AgentGridView>) to include parameters: { a11y: { test:
'error' } } so the story enables the same a11y testing as other stories in the
PR.
In `@web/src/pages/org-edit/AgentEditDrawer.tsx`:
- Around line 48-62: The state-sync block that compares agent to
prevAgentRef.current and calls setForm, setSubmitError, setDeleteOpen, and
setDeleting should be moved out of render into a useEffect that depends on
agent; create a useEffect(() => { ... }, [agent]) which performs the same logic
(update prevAgentRef.current, call setForm with agent fields,
setSubmitError(null), setDeleteOpen(false), setDeleting(false)) and remove the
inline state updates from the render path so only pure reads occur during render
and all side-effects run inside the useEffect.
---
Duplicate comments:
In `@web/src/__tests__/hooks/useAgentDetailData.test.ts`:
- Around line 179-259: Add two tests that exercise agentName transitions for
useAgentDetailData: (1) initial '' -> 'alice' should set up a new debounce
handler when rerendered to 'alice' and trigger fetchAgentDetail after 300ms when
its handler is invoked; (2) initial 'alice' -> '' should tear down the debounce
handler so invoking the previous handler after rerender/unmount does not call
fetchAgentDetail. Implement each by using renderHook(({ name }) =>
useAgentDetailData(name), { initialProps: '<start>' }) and then rerender({ name:
'<end>' }); capture handlers from vi.mocked(useWebSocket).mock.calls after each
render/rerender, call the appropriate handler(s), use vi.advanceTimersByTime to
assert debounce behavior (no immediate call, single call after 300ms), and
assert timeouts are cleared when transitioning away or unmounting
(mockFetchAgentDetail remains not called).
In `@web/src/__tests__/pages/org/build-org-tree.test.ts`:
- Around line 337-340: Update the test fixture and helper so the test truly
exercises a missing id: change the makeAgent helper signature to accept id?:
string (make AgentConfig.id optional) and in the test "uses agent.name as node
id when agent.id is undefined" pass undefined directly or omit the id field
instead of using the cast `undefined as unknown as string`; update references to
makeAgent and any types (e.g., AgentConfig) to reflect optional id so the test
compiles and correctly covers the missing-id path.
In `@web/src/hooks/useAgentsData.ts`:
- Around line 9-10: WS_DEBOUNCE_MS and the timeout reset logic are duplicated
between useAgentsData.ts and useAgentDetailData.ts; extract a single shared
helper (e.g., createWsDebounce or a constant and a resetDebounce function) into
a new module and import it from both hooks so both use the same debounce value
and reset behavior. Update references to WS_DEBOUNCE_MS in useAgentsData.ts and
useAgentDetailData.ts to import the constant, move any timeout/reset logic into
the shared helper (referencing the same function name like resetWsDebounce or
useWsDebounce) and replace the local implementations in both hooks to call the
shared utility.
In `@web/src/pages/org-edit/AgentEditDrawer.tsx`:
- Around line 69-71: The current useMemo that formats the hiring date uses new
Date(agent.hiring_date) which can shift YYYY-MM-DD across timezones and also
depends on the whole agent object; update the memo to depend on
agent?.hiring_date and parse the string as a date-only value (e.g., split
"YYYY-MM-DD" into year/month/day and construct a UTC/local date without time)
before calling toLocaleDateString() so the displayed day never shifts across
timezones; reference the existing useMemo block that formats agent.hiring_date
and the toLocaleDateString() call to locate where to change the parsing and the
dependency array.
In `@web/src/pages/org/build-org-tree.ts`:
- Around line 97-98: The code improperly treats agent.name as a surrogate key
whenever agent.id is missing (seen around resolveRuntimeStatus usage and in
teamMemberSet, node id/edge id generation, runtime-status lookups, and CEO/head
exclusion), causing collisions for multiple agents with the same name; fix by
computing a deterministic unique key for each agent (e.g., a stable concat/hash
of available unique fields such as name + email/role + array index) once when
building the graph and reuse that key everywhere instead of using id ?? name —
update the places referencing id ?? name (including resolveRuntimeStatus calls,
teamMemberSet population, node/edge id creation, runtime-status map lookups, and
CEO/head exclusion logic) to use the new agentKey value so all lookups and sets
remain consistent and collision-free.
In `@web/src/pages/org/DepartmentStatsBar.tsx`:
- Around line 17-26: The component currently defaults currency to 'EUR' which
can silently mislabel amounts; remove the defaulting in the DepartmentStatsBar
parameter (don't set currency = 'EUR') and update the cost pill render condition
to only show when cost7d is not null, Number.isFinite(cost7d) is true, and a
real currency is provided (e.g. currency != null && currency !== ''). Adjust the
DepartmentStatsBarProps type if necessary so callers must supply currency when
they want a cost shown, and keep using StatPill and formatCurrency(cost7d,
currency) unchanged when the currency is present.
🪄 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: 96eb691f-b153-4de0-95ed-3ef1942be427
📒 Files selected for processing (52)
CLAUDE.mddocs/design/brand-and-ux.mdweb/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/__tests__/helpers/factories.tsweb/src/__tests__/hooks/useAgentDetailData.test.tsweb/src/__tests__/hooks/useAgentsData.test.tsweb/src/__tests__/pages/dashboard/OrgHealthSection.test.tsxweb/src/__tests__/pages/org-edit/DepartmentsTab.test.tsxweb/src/__tests__/pages/org/DepartmentStatsBar.test.tsxweb/src/__tests__/pages/org/build-org-tree.test.tsweb/src/__tests__/stores/agents.test.tsweb/src/__tests__/stores/analytics.test.tsweb/src/__tests__/stores/budget.test.tsweb/src/__tests__/stores/company.test.tsweb/src/__tests__/utils/agents.property.test.tsweb/src/__tests__/utils/agents.test.tsweb/src/__tests__/utils/dashboard.property.test.tsweb/src/__tests__/utils/dashboard.test.tsweb/src/api/types.tsweb/src/components/ui/dept-health-bar.stories.tsxweb/src/components/ui/dept-health-bar.tsxweb/src/hooks/useAgentDetailData.tsweb/src/hooks/useAgentsData.tsweb/src/pages/AgentDetailPage.tsxweb/src/pages/DashboardPage.tsxweb/src/pages/agents/AgentGridView.stories.tsxweb/src/pages/agents/AgentGridView.tsxweb/src/pages/agents/AgentIdentityHeader.stories.tsxweb/src/pages/agents/AgentIdentityHeader.tsxweb/src/pages/dashboard/DashboardPage.stories.tsxweb/src/pages/dashboard/OrgHealthSection.stories.tsxweb/src/pages/dashboard/OrgHealthSection.tsxweb/src/pages/org-edit/AgentCreateDialog.stories.tsxweb/src/pages/org-edit/AgentEditDrawer.stories.tsxweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/pages/org-edit/AgentsTab.stories.tsxweb/src/pages/org-edit/AgentsTab.tsxweb/src/pages/org-edit/DepartmentEditDrawer.stories.tsxweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/pages/org-edit/DepartmentsTab.stories.tsxweb/src/pages/org-edit/DepartmentsTab.tsxweb/src/pages/org-edit/YamlEditorPanel.stories.tsxweb/src/pages/org/DepartmentGroupNode.stories.tsxweb/src/pages/org/DepartmentGroupNode.tsxweb/src/pages/org/DepartmentStatsBar.stories.tsxweb/src/pages/org/DepartmentStatsBar.tsxweb/src/pages/org/build-org-tree.tsweb/src/pages/setup/ThemePreview.tsxweb/src/stores/budget.tsweb/src/stores/company.tsweb/src/utils/agents.tsweb/src/utils/dashboard.ts
💤 Files with no reviewable changes (1)
- web/src/pages/DashboardPage.tsx
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: Dashboard Test
- GitHub Check: Build Sandbox
- GitHub Check: Build Web
- GitHub Check: Build Backend
- GitHub Check: Dependency Review
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (9)
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
web/src/**/*.{ts,tsx}: Web Dashboard: ALWAYS reuse existing components fromweb/src/components/ui/before creating new ones. Reuse StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton variants, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup, Drawer, InputField, SelectField, SliderField, ToggleField, TaskStatusIndicator, PriorityBadge, ProviderHealthBadge, TokenUsageBar, CodeMirrorEditor, SegmentedControl, ThemeToggle.
Do NOT recreate status dots inline—use<StatusBadge>. Do NOT build card-with-header layouts from scratch—use<SectionCard>. Do NOT create metric displays withtext-metric font-bold—use<MetricCard>. Do NOT render initials circles manually—use<Avatar>. Do NOT create complex (>8 line) JSX inside.map()—extract to a shared component.
Web design system enforcement: A PostToolUse hook (scripts/check_web_design_system.py) runs automatically on every Edit/Write toweb/src/files. It catches hardcoded hex colors, rgba values, font-family declarations, missing Storybook stories, duplicate patterns, and complex map blocks. Fix all violations before proceeding.
React 19 + shadcn/ui + Tailwind CSS 4 dashboard: Use React Router, Zustand stores,@tanstack/react-query,@xyflow/react,@dagrejs/dagre, d3-force,@dnd-kit, Recharts, Framer Motion, cmdk, js-yaml, Axios.
Files:
web/src/__tests__/stores/agents.test.tsweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/components/ui/dept-health-bar.stories.tsxweb/src/pages/agents/AgentIdentityHeader.tsxweb/src/pages/agents/AgentIdentityHeader.stories.tsxweb/src/pages/org-edit/YamlEditorPanel.stories.tsxweb/src/__tests__/pages/org-edit/DepartmentsTab.test.tsxweb/src/pages/org-edit/AgentEditDrawer.stories.tsxweb/src/pages/org-edit/AgentCreateDialog.stories.tsxweb/src/stores/budget.tsweb/src/pages/agents/AgentGridView.tsxweb/src/pages/AgentDetailPage.tsxweb/src/pages/org-edit/AgentsTab.stories.tsxweb/src/pages/org/DepartmentGroupNode.stories.tsxweb/src/pages/setup/ThemePreview.tsxweb/src/pages/org-edit/DepartmentsTab.tsxweb/src/pages/dashboard/DashboardPage.stories.tsxweb/src/utils/agents.tsweb/src/utils/dashboard.tsweb/src/hooks/useAgentsData.tsweb/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/pages/org-edit/DepartmentsTab.stories.tsxweb/src/__tests__/utils/agents.property.test.tsweb/src/pages/dashboard/OrgHealthSection.stories.tsxweb/src/pages/agents/AgentGridView.stories.tsxweb/src/__tests__/utils/dashboard.test.tsweb/src/__tests__/pages/dashboard/OrgHealthSection.test.tsxweb/src/pages/org/DepartmentGroupNode.tsxweb/src/components/ui/dept-health-bar.tsxweb/src/__tests__/hooks/useAgentDetailData.test.tsweb/src/__tests__/utils/agents.test.tsweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/hooks/useAgentDetailData.tsweb/src/pages/org/DepartmentStatsBar.tsxweb/src/pages/org/DepartmentStatsBar.stories.tsxweb/src/stores/company.tsweb/src/__tests__/utils/dashboard.property.test.tsweb/src/api/types.tsweb/src/__tests__/pages/org/build-org-tree.test.tsweb/src/pages/org/build-org-tree.tsweb/src/pages/dashboard/OrgHealthSection.tsxweb/src/__tests__/stores/analytics.test.tsweb/src/pages/org-edit/DepartmentEditDrawer.stories.tsxweb/src/__tests__/pages/org/DepartmentStatsBar.test.tsxweb/src/__tests__/hooks/useAgentsData.test.tsweb/src/__tests__/stores/budget.test.tsweb/src/__tests__/helpers/factories.tsweb/src/pages/org-edit/AgentsTab.tsxweb/src/__tests__/stores/company.test.ts
web/src/**/*.{ts,tsx,css}
📄 CodeRabbit inference engine (CLAUDE.md)
Design tokens: Colors use Tailwind semantic classes (
text-foreground,bg-card,text-accent, etc.) or CSS variables (var(--so-accent)). NEVER hardcode hex values. Typography usesfont-sansorfont-mono. Spacing uses density-aware tokens or standard Tailwind. Shadows/borders use token variables.
Files:
web/src/__tests__/stores/agents.test.tsweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/components/ui/dept-health-bar.stories.tsxweb/src/pages/agents/AgentIdentityHeader.tsxweb/src/pages/agents/AgentIdentityHeader.stories.tsxweb/src/pages/org-edit/YamlEditorPanel.stories.tsxweb/src/__tests__/pages/org-edit/DepartmentsTab.test.tsxweb/src/pages/org-edit/AgentEditDrawer.stories.tsxweb/src/pages/org-edit/AgentCreateDialog.stories.tsxweb/src/stores/budget.tsweb/src/pages/agents/AgentGridView.tsxweb/src/pages/AgentDetailPage.tsxweb/src/pages/org-edit/AgentsTab.stories.tsxweb/src/pages/org/DepartmentGroupNode.stories.tsxweb/src/pages/setup/ThemePreview.tsxweb/src/pages/org-edit/DepartmentsTab.tsxweb/src/pages/dashboard/DashboardPage.stories.tsxweb/src/utils/agents.tsweb/src/utils/dashboard.tsweb/src/hooks/useAgentsData.tsweb/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/pages/org-edit/DepartmentsTab.stories.tsxweb/src/__tests__/utils/agents.property.test.tsweb/src/pages/dashboard/OrgHealthSection.stories.tsxweb/src/pages/agents/AgentGridView.stories.tsxweb/src/__tests__/utils/dashboard.test.tsweb/src/__tests__/pages/dashboard/OrgHealthSection.test.tsxweb/src/pages/org/DepartmentGroupNode.tsxweb/src/components/ui/dept-health-bar.tsxweb/src/__tests__/hooks/useAgentDetailData.test.tsweb/src/__tests__/utils/agents.test.tsweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/hooks/useAgentDetailData.tsweb/src/pages/org/DepartmentStatsBar.tsxweb/src/pages/org/DepartmentStatsBar.stories.tsxweb/src/stores/company.tsweb/src/__tests__/utils/dashboard.property.test.tsweb/src/api/types.tsweb/src/__tests__/pages/org/build-org-tree.test.tsweb/src/pages/org/build-org-tree.tsweb/src/pages/dashboard/OrgHealthSection.tsxweb/src/__tests__/stores/analytics.test.tsweb/src/pages/org-edit/DepartmentEditDrawer.stories.tsxweb/src/__tests__/pages/org/DepartmentStatsBar.test.tsxweb/src/__tests__/hooks/useAgentsData.test.tsweb/src/__tests__/stores/budget.test.tsweb/src/__tests__/helpers/factories.tsweb/src/pages/org-edit/AgentsTab.tsxweb/src/__tests__/stores/company.test.ts
**/*.{ts,tsx,json}
📄 CodeRabbit inference engine (CLAUDE.md)
TypeScript 6.0: Remove
baseUrl(deprecated);pathsentries are relative to tsconfig directory.esModuleInteropalways true—cannot be set tofalse.typesdefaults to[]—must explicitly list (e.g.'types': ['vitest/globals']).DOM.Iterablemerged intoDOM.moduleResolution: 'classic'and'node10'removed—use'bundler'or'nodenext'.strictdefaults totrue.noUncheckedSideEffectImportsdefaults totrue.
Files:
web/src/__tests__/stores/agents.test.tsweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/components/ui/dept-health-bar.stories.tsxweb/src/pages/agents/AgentIdentityHeader.tsxweb/src/pages/agents/AgentIdentityHeader.stories.tsxweb/src/pages/org-edit/YamlEditorPanel.stories.tsxweb/src/__tests__/pages/org-edit/DepartmentsTab.test.tsxweb/src/pages/org-edit/AgentEditDrawer.stories.tsxweb/src/pages/org-edit/AgentCreateDialog.stories.tsxweb/src/stores/budget.tsweb/src/pages/agents/AgentGridView.tsxweb/src/pages/AgentDetailPage.tsxweb/src/pages/org-edit/AgentsTab.stories.tsxweb/src/pages/org/DepartmentGroupNode.stories.tsxweb/src/pages/setup/ThemePreview.tsxweb/src/pages/org-edit/DepartmentsTab.tsxweb/src/pages/dashboard/DashboardPage.stories.tsxweb/src/utils/agents.tsweb/src/utils/dashboard.tsweb/src/hooks/useAgentsData.tsweb/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/pages/org-edit/DepartmentsTab.stories.tsxweb/src/__tests__/utils/agents.property.test.tsweb/src/pages/dashboard/OrgHealthSection.stories.tsxweb/src/pages/agents/AgentGridView.stories.tsxweb/src/__tests__/utils/dashboard.test.tsweb/src/__tests__/pages/dashboard/OrgHealthSection.test.tsxweb/src/pages/org/DepartmentGroupNode.tsxweb/src/components/ui/dept-health-bar.tsxweb/src/__tests__/hooks/useAgentDetailData.test.tsweb/src/__tests__/utils/agents.test.tsweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/hooks/useAgentDetailData.tsweb/src/pages/org/DepartmentStatsBar.tsxweb/src/pages/org/DepartmentStatsBar.stories.tsxweb/src/stores/company.tsweb/src/__tests__/utils/dashboard.property.test.tsweb/src/api/types.tsweb/src/__tests__/pages/org/build-org-tree.test.tsweb/src/pages/org/build-org-tree.tsweb/src/pages/dashboard/OrgHealthSection.tsxweb/src/__tests__/stores/analytics.test.tsweb/src/pages/org-edit/DepartmentEditDrawer.stories.tsxweb/src/__tests__/pages/org/DepartmentStatsBar.test.tsxweb/src/__tests__/hooks/useAgentsData.test.tsweb/src/__tests__/stores/budget.test.tsweb/src/__tests__/helpers/factories.tsweb/src/pages/org-edit/AgentsTab.tsxweb/src/__tests__/stores/company.test.ts
**/*.{py,ts,tsx,go}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{py,ts,tsx,go}: Lint & format: Python via ruff (uv run ruff check+uv run ruff format). TypeScript web via ESLint (zero warnings enforced:npm --prefix web run lint). Go CLI via golangci-lint andgo vet. All enforced in pre-commit hooks.
Type checking: Python via mypy strict mode (uv run mypy src/ tests/). TypeScript vianpm --prefix web run type-check. Go viago -C cli vet ./....
Files:
web/src/__tests__/stores/agents.test.tsweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/components/ui/dept-health-bar.stories.tsxweb/src/pages/agents/AgentIdentityHeader.tsxweb/src/pages/agents/AgentIdentityHeader.stories.tsxweb/src/pages/org-edit/YamlEditorPanel.stories.tsxweb/src/__tests__/pages/org-edit/DepartmentsTab.test.tsxweb/src/pages/org-edit/AgentEditDrawer.stories.tsxweb/src/pages/org-edit/AgentCreateDialog.stories.tsxweb/src/stores/budget.tsweb/src/pages/agents/AgentGridView.tsxweb/src/pages/AgentDetailPage.tsxweb/src/pages/org-edit/AgentsTab.stories.tsxweb/src/pages/org/DepartmentGroupNode.stories.tsxweb/src/pages/setup/ThemePreview.tsxweb/src/pages/org-edit/DepartmentsTab.tsxweb/src/pages/dashboard/DashboardPage.stories.tsxweb/src/utils/agents.tsweb/src/utils/dashboard.tsweb/src/hooks/useAgentsData.tsweb/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/pages/org-edit/DepartmentsTab.stories.tsxweb/src/__tests__/utils/agents.property.test.tsweb/src/pages/dashboard/OrgHealthSection.stories.tsxweb/src/pages/agents/AgentGridView.stories.tsxweb/src/__tests__/utils/dashboard.test.tsweb/src/__tests__/pages/dashboard/OrgHealthSection.test.tsxweb/src/pages/org/DepartmentGroupNode.tsxweb/src/components/ui/dept-health-bar.tsxweb/src/__tests__/hooks/useAgentDetailData.test.tsweb/src/__tests__/utils/agents.test.tsweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/hooks/useAgentDetailData.tsweb/src/pages/org/DepartmentStatsBar.tsxweb/src/pages/org/DepartmentStatsBar.stories.tsxweb/src/stores/company.tsweb/src/__tests__/utils/dashboard.property.test.tsweb/src/api/types.tsweb/src/__tests__/pages/org/build-org-tree.test.tsweb/src/pages/org/build-org-tree.tsweb/src/pages/dashboard/OrgHealthSection.tsxweb/src/__tests__/stores/analytics.test.tsweb/src/pages/org-edit/DepartmentEditDrawer.stories.tsxweb/src/__tests__/pages/org/DepartmentStatsBar.test.tsxweb/src/__tests__/hooks/useAgentsData.test.tsweb/src/__tests__/stores/budget.test.tsweb/src/__tests__/helpers/factories.tsweb/src/pages/org-edit/AgentsTab.tsxweb/src/__tests__/stores/company.test.ts
web/src/pages/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Web dashboard pages: Lazy-loaded page components (one per route). Page-scoped sub-components in
pages/<page-name>/subdirs. Feature dirs added as pages are built. Stores for auth, WebSocket, toast, analytics, setup wizard, company, agents, budget, tasks, settings, providers, theme.
Files:
web/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/pages/agents/AgentIdentityHeader.tsxweb/src/pages/agents/AgentIdentityHeader.stories.tsxweb/src/pages/org-edit/YamlEditorPanel.stories.tsxweb/src/pages/org-edit/AgentEditDrawer.stories.tsxweb/src/pages/org-edit/AgentCreateDialog.stories.tsxweb/src/pages/agents/AgentGridView.tsxweb/src/pages/AgentDetailPage.tsxweb/src/pages/org-edit/AgentsTab.stories.tsxweb/src/pages/org/DepartmentGroupNode.stories.tsxweb/src/pages/setup/ThemePreview.tsxweb/src/pages/org-edit/DepartmentsTab.tsxweb/src/pages/dashboard/DashboardPage.stories.tsxweb/src/pages/org-edit/DepartmentsTab.stories.tsxweb/src/pages/dashboard/OrgHealthSection.stories.tsxweb/src/pages/agents/AgentGridView.stories.tsxweb/src/pages/org/DepartmentGroupNode.tsxweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/pages/org/DepartmentStatsBar.tsxweb/src/pages/org/DepartmentStatsBar.stories.tsxweb/src/pages/org/build-org-tree.tsweb/src/pages/dashboard/OrgHealthSection.tsxweb/src/pages/org-edit/DepartmentEditDrawer.stories.tsxweb/src/pages/org-edit/AgentsTab.tsx
web/src/components/ui/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Web components: Place new shared components in
web/src/components/ui/with kebab-case filenames. Create.stories.tsxwith all states. Export props as TypeScript interface. Use design tokens exclusively—no hardcoded colors, fonts, or spacing. Importcnfrom@/lib/utilsfor conditional class merging.
Files:
web/src/components/ui/dept-health-bar.stories.tsxweb/src/components/ui/dept-health-bar.tsx
web/**/*.stories.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Storybook 10: ESM-only—no CJS support. Packages like
@storybook/addon-essentials,@storybook/addon-interactions,@storybook/test,@storybook/blocksno longer published.@storybook/addon-docsis separate. Import fromstorybook/test(not@storybook/test),storybook/actions. UsedefineMainfrom@storybook/react-vite/nodeanddefinePreviewfrom@storybook/react-vite. Backgrounds API: useparameters.backgrounds.options(object) +initialGlobals.backgrounds.value. a11y testing: useparameters.a11y.test: 'error' | 'todo' | 'off'. Minimum versions: Node 20.19+, Vite 5+, Vitest 3+, TypeScript 4.9+.
Files:
web/src/components/ui/dept-health-bar.stories.tsxweb/src/pages/agents/AgentIdentityHeader.stories.tsxweb/src/pages/org-edit/YamlEditorPanel.stories.tsxweb/src/pages/org-edit/AgentEditDrawer.stories.tsxweb/src/pages/org-edit/AgentCreateDialog.stories.tsxweb/src/pages/org-edit/AgentsTab.stories.tsxweb/src/pages/org/DepartmentGroupNode.stories.tsxweb/src/pages/dashboard/DashboardPage.stories.tsxweb/src/pages/org-edit/DepartmentsTab.stories.tsxweb/src/pages/dashboard/OrgHealthSection.stories.tsxweb/src/pages/agents/AgentGridView.stories.tsxweb/src/pages/org/DepartmentStatsBar.stories.tsxweb/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
docs/design/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
When approved deviations occur, update the relevant
docs/design/page to reflect the new reality.
Files:
docs/design/brand-and-ux.md
docs/**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
Documentation:
docs/(Markdown, built with Zensical, config:mkdocs.yml). Design spec indocs/design/(11 pages), architecture indocs/architecture/, roadmap indocs/roadmap/. REST API reference auto-generated byscripts/export_openapi.py. Library reference auto-generated via mkdocstrings + Griffe. Landing page insite/(Astro).
Files:
docs/design/brand-and-ux.md
🧠 Learnings (12)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/**/*.{ts,tsx} : Web Dashboard: ALWAYS reuse existing components from `web/src/components/ui/` before creating new ones. Reuse StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton variants, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup, Drawer, InputField, SelectField, SliderField, ToggleField, TaskStatusIndicator, PriorityBadge, ProviderHealthBadge, TokenUsageBar, CodeMirrorEditor, SegmentedControl, ThemeToggle.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/**/*.{ts,tsx} : React 19 + shadcn/ui + Tailwind CSS 4 dashboard: Use React Router, Zustand stores, tanstack/react-query, xyflow/react, dagrejs/dagre, d3-force, dnd-kit, Recharts, Framer Motion, cmdk, js-yaml, Axios.
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/**/*.{ts,tsx} : Web Dashboard: ALWAYS reuse existing components from `web/src/components/ui/` before creating new ones. Reuse StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton variants, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup, Drawer, InputField, SelectField, SliderField, ToggleField, TaskStatusIndicator, PriorityBadge, ProviderHealthBadge, TokenUsageBar, CodeMirrorEditor, SegmentedControl, ThemeToggle.
Applied to files:
web/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/components/ui/dept-health-bar.stories.tsxCLAUDE.mdweb/src/__tests__/pages/org-edit/DepartmentsTab.test.tsxweb/src/pages/AgentDetailPage.tsxweb/src/pages/setup/ThemePreview.tsxweb/src/pages/org-edit/DepartmentsTab.tsxweb/src/pages/dashboard/DashboardPage.stories.tsxweb/src/utils/dashboard.tsweb/src/hooks/useAgentsData.tsweb/src/pages/dashboard/OrgHealthSection.stories.tsxweb/src/__tests__/utils/dashboard.test.tsweb/src/__tests__/pages/dashboard/OrgHealthSection.test.tsxweb/src/components/ui/dept-health-bar.tsxweb/src/hooks/useAgentDetailData.tsweb/src/pages/org/DepartmentStatsBar.tsxdocs/design/brand-and-ux.mdweb/src/pages/dashboard/OrgHealthSection.tsx
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/**/*.{ts,tsx} : React 19 + shadcn/ui + Tailwind CSS 4 dashboard: Use React Router, Zustand stores, tanstack/react-query, xyflow/react, dagrejs/dagre, d3-force, dnd-kit, Recharts, Framer Motion, cmdk, js-yaml, Axios.
Applied to files:
web/src/pages/org-edit/DepartmentEditDrawer.tsxCLAUDE.mdweb/src/pages/org-edit/DepartmentsTab.tsxweb/src/pages/dashboard/DashboardPage.stories.tsxweb/src/hooks/useAgentsData.tsweb/src/pages/org/DepartmentGroupNode.tsxweb/src/components/ui/dept-health-bar.tsxweb/src/hooks/useAgentDetailData.ts
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do NOT recreate status dots inline—use `<StatusBadge>`. Do NOT build card-with-header layouts from scratch—use `<SectionCard>`. Do NOT create metric displays with `text-metric font-bold`—use `<MetricCard>`. Do NOT render initials circles manually—use `<Avatar>`. Do NOT create complex (>8 line) JSX inside `.map()`—extract to a shared component.
Applied to files:
web/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/components/ui/dept-health-bar.stories.tsxCLAUDE.mdweb/src/pages/agents/AgentIdentityHeader.tsxweb/src/pages/agents/AgentGridView.tsxweb/src/pages/AgentDetailPage.tsxweb/src/pages/setup/ThemePreview.tsxweb/src/pages/org-edit/DepartmentsTab.tsxweb/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/components/ui/dept-health-bar.tsxweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/pages/org/DepartmentStatsBar.tsxdocs/design/brand-and-ux.mdweb/src/pages/dashboard/OrgHealthSection.tsx
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/**/*.stories.{ts,tsx} : Storybook 10: ESM-only—no CJS support. Packages like `storybook/addon-essentials`, `storybook/addon-interactions`, `storybook/test`, `storybook/blocks` no longer published. `storybook/addon-docs` is separate. Import from `storybook/test` (not `storybook/test`), `storybook/actions`. Use `defineMain` from `storybook/react-vite/node` and `definePreview` from `storybook/react-vite`. Backgrounds API: use `parameters.backgrounds.options` (object) + `initialGlobals.backgrounds.value`. a11y testing: use `parameters.a11y.test: 'error' | 'todo' | 'off'`. Minimum versions: Node 20.19+, Vite 5+, Vitest 3+, TypeScript 4.9+.
Applied to files:
web/src/components/ui/dept-health-bar.stories.tsxweb/src/pages/org-edit/YamlEditorPanel.stories.tsxweb/src/pages/org-edit/AgentEditDrawer.stories.tsxweb/src/pages/org-edit/AgentsTab.stories.tsxweb/src/pages/org/DepartmentGroupNode.stories.tsxweb/src/pages/org-edit/DepartmentsTab.stories.tsxweb/src/pages/agents/AgentGridView.stories.tsxweb/src/pages/org/DepartmentStatsBar.stories.tsx
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : Web components: Place new shared components in `web/src/components/ui/` with kebab-case filenames. Create `.stories.tsx` with all states. Export props as TypeScript interface. Use design tokens exclusively—no hardcoded colors, fonts, or spacing. Import `cn` from `@/lib/utils` for conditional class merging.
Applied to files:
web/src/components/ui/dept-health-bar.stories.tsxCLAUDE.mdweb/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/pages/dashboard/OrgHealthSection.stories.tsxweb/src/pages/org/DepartmentStatsBar.stories.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:
CLAUDE.mddocs/design/brand-and-ux.md
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/pages/**/*.{ts,tsx} : Web dashboard pages: Lazy-loaded page components (one per route). Page-scoped sub-components in `pages/<page-name>/` subdirs. Feature dirs added as pages are built. Stores for auth, WebSocket, toast, analytics, setup wizard, company, agents, budget, tasks, settings, providers, theme.
Applied to files:
CLAUDE.mdweb/src/pages/dashboard/DashboardPage.stories.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__/pages/org-edit/DepartmentsTab.test.tsxweb/src/__tests__/components/ui/dept-health-bar.test.tsxweb/src/__tests__/utils/dashboard.test.tsweb/src/__tests__/pages/dashboard/OrgHealthSection.test.tsxweb/src/__tests__/utils/dashboard.property.test.tsweb/src/__tests__/pages/org/build-org-tree.test.tsweb/src/__tests__/pages/org/DepartmentStatsBar.test.tsx
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to src/synthorg/hr/**/*.py : HR: Hiring, firing, onboarding, agent registry, performance tracking, activity timeline, career history, promotion/demotion.
Applied to files:
web/src/api/types.ts
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to web/src/**/*.{ts,tsx,css} : Design tokens: Colors use Tailwind semantic classes (`text-foreground`, `bg-card`, `text-accent`, etc.) or CSS variables (`var(--so-accent)`). NEVER hardcode hex values. Typography uses `font-sans` or `font-mono`. Spacing uses density-aware tokens or standard Tailwind. Shadows/borders use token variables.
Applied to files:
docs/design/brand-and-ux.md
📚 Learning: 2026-03-29T11:41:44.483Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-29T11:41:44.483Z
Learning: Applies to **/*.{ts,tsx,json} : TypeScript 6.0: Remove `baseUrl` (deprecated); `paths` entries are relative to tsconfig directory. `esModuleInterop` always true—cannot be set to `false`. `types` defaults to `[]`—must explicitly list (e.g. `'types': ['vitest/globals']`). `DOM.Iterable` merged into `DOM`. `moduleResolution: 'classic'` and `'node10'` removed—use `'bundler'` or `'nodenext'`. `strict` defaults to `true`. `noUncheckedSideEffectImports` defaults to `true`.
Applied to files:
web/src/__tests__/helpers/factories.ts
docs/design/brand-and-ux.md
Outdated
| | `SectionCard` | `section-card.tsx` | `title`, `icon?`, `action?`, `children` | Titled card wrapper with Lucide icon, action slot, and content area. Use for every content section. | | ||
| | `AgentCard` | `agent-card.tsx` | `name`, `role`, `department`, `status`, `currentTask?`, `timestamp?` | Consistent agent display. Composes Avatar + StatusBadge internally. Must look identical everywhere. | | ||
| | `DeptHealthBar` | `dept-health-bar.tsx` | `name`, `health?`, `agentCount`, `taskCount?` | Animated horizontal fill bar with health percentage (null-safe -- shows N/A when health unavailable). Color auto-mapped via `getHealthColor()`. | | ||
| | `DeptHealthBar` | `dept-health-bar.tsx` | `name`, `health?`, `agentCount` | Animated horizontal fill bar with utilization percentage (null-safe -- shows N/A when health unavailable). Color auto-mapped via `getHealthColor()`. | |
There was a problem hiding this comment.
Fix stale terminology in DeptHealthBar docs.
Line [203] mixes “utilization percentage” with “health unavailable.” Use “utilization unavailable” for consistency with current API semantics.
✏️ Proposed wording tweak
-| `DeptHealthBar` | `dept-health-bar.tsx` | `name`, `health?`, `agentCount` | Animated horizontal fill bar with utilization percentage (null-safe -- shows N/A when health unavailable). Color auto-mapped via `getHealthColor()`. |
+| `DeptHealthBar` | `dept-health-bar.tsx` | `name`, `health?`, `agentCount` | Animated horizontal fill bar with utilization percentage (null-safe -- shows N/A when utilization is unavailable). Color auto-mapped via `getHealthColor()`. |📝 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.
| | `DeptHealthBar` | `dept-health-bar.tsx` | `name`, `health?`, `agentCount` | Animated horizontal fill bar with utilization percentage (null-safe -- shows N/A when health unavailable). Color auto-mapped via `getHealthColor()`. | | |
| | `DeptHealthBar` | `dept-health-bar.tsx` | `name`, `health?`, `agentCount` | Animated horizontal fill bar with utilization percentage (null-safe -- shows N/A when utilization is unavailable). Color auto-mapped via `getHealthColor()`. | |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/design/brand-and-ux.md` at line 203, Update the docs for DeptHealthBar
in dept-health-bar.tsx: replace the phrase "utilization percentage (null-safe --
shows N/A when health unavailable)" with wording that uses "utilization
unavailable" to match the API semantics (for example: "utilization (null-safe —
shows N/A when utilization unavailable)"). Keep the rest of the description
(animated horizontal fill, color auto-mapped via getHealthColor()) unchanged.
Update DeptHealthBar docs to say "utilization unavailable" instead of "health unavailable". Add a11y test parameter to AgentGridView stories. Fix build-org-tree test makeAgent signature to accept optional id instead of using unsafe undefined cast. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
🤖 I have created a release *beep* *boop* --- #MAJOR CHANGES; We got a somewhat working webui :) ## [0.5.0](v0.4.9...v0.5.0) (2026-03-30) ### Features * add analytics trends and budget forecast API endpoints ([#798](#798)) ([16b61f5](16b61f5)) * add department policies to default templates ([#852](#852)) ([7a41548](7a41548)) * add remaining activity event types (task_started, tool_used, delegation, cost_incurred) ([#832](#832)) ([4252fac](4252fac)) * agent performance, activity, and history API endpoints ([#811](#811)) ([9b75c1d](9b75c1d)) * Agent Profiles and Detail pages (biography, career, performance) ([#874](#874)) ([62d7880](62d7880)) * app shell, Storybook, and CI/CD pipeline ([#819](#819)) ([d4dde90](d4dde90)) * Approvals page with risk grouping, urgency indicators, batch actions ([#889](#889)) ([4e9673d](4e9673d)) * Budget Panel page (P&L dashboard, breakdown charts, forecast) ([#890](#890)) ([b63b0f1](b63b0f1)) * build infrastructure layer (API client, auth, WebSocket) ([#815](#815)) ([9f01d3e](9f01d3e)) * CLI global options infrastructure, UI modes, exit codes, env vars ([#891](#891)) ([fef4fc5](fef4fc5)) * CodeMirror editor and theme preferences toggle ([#905](#905), [#807](#807)) ([#909](#909)) ([41fbedc](41fbedc)) * Company page (department/agent management) ([#888](#888)) ([cfb88b0](cfb88b0)) * comprehensive hint coverage across all CLI commands ([#900](#900)) ([937974e](937974e)) * config system extensions, per-command flags for init/start/stop/status/logs ([#895](#895)) ([32f83fe](32f83fe)) * configurable currency system replacing hardcoded USD ([#854](#854)) ([b372551](b372551)) * Dashboard page (metric cards, activity feed, budget burn) ([#861](#861)) ([7d519d5](7d519d5)) * department health, provider status, and activity feed endpoints ([#818](#818)) ([6d5f196](6d5f196)) * design tokens and core UI components ([#833](#833)) ([ed887f2](ed887f2)) * extend approval, meeting, and budget API responses ([#834](#834)) ([31472bf](31472bf)) * frontend polish -- real-time UX, accessibility, responsive, performance ([#790](#790), [#792](#792), [#791](#791), [#793](#793)) ([#917](#917)) ([f04a537](f04a537)) * implement human roles and access control levels ([#856](#856)) ([d6d8a06](d6d8a06)) * implement semantic conflict detection in workspace merge ([#860](#860)) ([d97283b](d97283b)) * interaction components and animation patterns ([#853](#853)) ([82d4b01](82d4b01)) * Login page + first-run bootstrap + Company page ([#789](#789), [#888](#888)) ([#896](#896)) ([8758e8d](8758e8d)) * Meetings page with timeline viz, token bars, contribution formatting ([#788](#788)) ([#904](#904)) ([b207f46](b207f46)) * Messages page with threading, channel badges, sender indicators ([#787](#787)) ([#903](#903)) ([28293ad](28293ad)) * Org Chart force-directed view and drag-drop reassignment ([#872](#872), [#873](#873)) ([#912](#912)) ([a68a938](a68a938)) * Org Chart page (living nodes, status, CRUD, department health) ([#870](#870)) ([0acbdae](0acbdae)) * per-command flags for remaining commands, auto-behavior wiring, help/discoverability ([#897](#897)) ([3f7afa2](3f7afa2)) * Providers page with backend rework -- health, CRUD, subscription auth ([#893](#893)) ([9f8dd98](9f8dd98)) * scaffold React + Vite + TypeScript + Tailwind project ([#799](#799)) ([bd151aa](bd151aa)) * Settings page with search, dependency indicators, grouped rendering ([#784](#784)) ([#902](#902)) ([a7b9870](a7b9870)) * Setup Wizard rebuild with template comparison, cost estimator, theme customization ([#879](#879)) ([ae8b50b](ae8b50b)) * setup wizard UX -- template filters, card metadata, provider form reuse ([#910](#910)) ([7f04676](7f04676)) * setup wizard UX overhaul -- mode choice, step reorder, provider fixes ([#907](#907)) ([ee964c4](ee964c4)) * structured ModelRequirement in template agent configs ([#795](#795)) ([7433548](7433548)) * Task Board page (rich Kanban, filtering, dependency viz) ([#871](#871)) ([04a19b0](04a19b0)) ### Bug Fixes * align frontend types with backend and debounce WS refetches ([#916](#916)) ([134c11b](134c11b)) * auto-cleanup targets newly pulled images instead of old ones ([#884](#884)) ([50e6591](50e6591)) * correct wipe backup-skip flow and harden error handling ([#808](#808)) ([c05860f](c05860f)) * improve provider setup in wizard, subscription auth, dashboard bugs ([#914](#914)) ([87bf8e6](87bf8e6)) * improve update channel detection and add config get command ([#814](#814)) ([6b137f0](6b137f0)) * resolve all ESLint warnings, add zero-warnings enforcement ([#899](#899)) ([079b46a](079b46a)) * subscription auth uses api_key, base URL optional for cloud providers ([#915](#915)) ([f0098dd](f0098dd)) ### Refactoring * semantic analyzer cleanup -- shared filtering, concurrency, extraction ([#908](#908)) ([81372bf](81372bf)) ### Documentation * brand identity and UX design system from [#765](#765) exploration ([#804](#804)) ([389a9f4](389a9f4)) * page structure and information architecture for v0.5.0 dashboard ([#809](#809)) ([f8d6d4a](f8d6d4a)) * write UX design guidelines with WCAG-verified color system ([#816](#816)) ([4a4594e](4a4594e)) ### Tests * add unit tests for agent hooks and page components ([#875](#875)) ([#901](#901)) ([1d81546](1d81546)) ### CI/CD * bump actions/deploy-pages from 4.0.5 to 5.0.0 in the major group ([#831](#831)) ([01c19de](01c19de)) * bump astral-sh/setup-uv from 7.6.0 to 8.0.0 in /.github/actions/setup-python-uv in the all group ([#920](#920)) ([5f6ba54](5f6ba54)) * bump codecov/codecov-action from 5.5.3 to 6.0.0 in the major group ([#868](#868)) ([f22a181](f22a181)) * bump github/codeql-action from 4.34.1 to 4.35.0 in the all group ([#883](#883)) ([87a4890](87a4890)) * bump sigstore/cosign-installer from 4.1.0 to 4.1.1 in the minor-and-patch group ([#830](#830)) ([7a69050](7a69050)) * bump the all group with 3 updates ([#923](#923)) ([ff27c8e](ff27c8e)) * bump wrangler from 4.76.0 to 4.77.0 in /.github in the minor-and-patch group ([#822](#822)) ([07d43eb](07d43eb)) * bump wrangler from 4.77.0 to 4.78.0 in /.github in the all group ([#882](#882)) ([f84118d](f84118d)) ### Maintenance * add design system enforcement hook and component inventory ([#846](#846)) ([15abc43](15abc43)) * add dev-only auth bypass for frontend testing ([#885](#885)) ([6cdcd8a](6cdcd8a)) * add pre-push rebase check hook ([#855](#855)) ([b637a04](b637a04)) * backend hardening -- eviction/size-caps and model validation ([#911](#911)) ([81253d9](81253d9)) * bump axios from 1.13.6 to 1.14.0 in /web in the all group across 1 directory ([#922](#922)) ([b1b0232](b1b0232)) * bump brace-expansion from 5.0.4 to 5.0.5 in /web ([#862](#862)) ([ba4a565](ba4a565)) * bump eslint-plugin-react-refresh from 0.4.26 to 0.5.2 in /web ([#801](#801)) ([7574bb5](7574bb5)) * bump faker from 40.11.0 to 40.11.1 in the minor-and-patch group ([#803](#803)) ([14d322e](14d322e)) * bump https://github.com/astral-sh/ruff-pre-commit from v0.15.7 to 0.15.8 ([#864](#864)) ([f52901e](f52901e)) * bump nginxinc/nginx-unprivileged from `6582a34` to `f99cc61` in /docker/web in the all group ([#919](#919)) ([df85e4f](df85e4f)) * bump nginxinc/nginx-unprivileged from `ccbac1a` to `6582a34` in /docker/web ([#800](#800)) ([f4e9450](f4e9450)) * bump node from `44bcbf4` to `71be405` in /docker/sandbox ([#827](#827)) ([91bec67](91bec67)) * bump node from `5209bca` to `cf38e1f` in /docker/web ([#863](#863)) ([66d6043](66d6043)) * bump picomatch in /site ([#842](#842)) ([5f20bcc](5f20bcc)) * bump recharts 2->3 and @types/node 22->25 in /web ([#802](#802)) ([a908800](a908800)) * Bump requests from 2.32.5 to 2.33.0 ([#843](#843)) ([41daf69](41daf69)) * bump smol-toml from 1.6.0 to 1.6.1 in /site ([#826](#826)) ([3e5dbe4](3e5dbe4)) * bump the all group with 3 updates ([#921](#921)) ([7bace0b](7bace0b)) * bump the minor-and-patch group across 1 directory with 2 updates ([#829](#829)) ([93e611f](93e611f)) * bump the minor-and-patch group across 1 directory with 3 updates ([#841](#841)) ([7010c8e](7010c8e)) * bump the minor-and-patch group across 1 directory with 3 updates ([#869](#869)) ([548cee5](548cee5)) * bump the minor-and-patch group in /site with 2 updates ([#865](#865)) ([9558101](9558101)) * bump the minor-and-patch group with 2 updates ([#867](#867)) ([4830706](4830706)) * consolidate Dependabot groups to 1 PR per ecosystem ([06d2556](06d2556)) * consolidate Dependabot groups to 1 PR per ecosystem ([#881](#881)) ([06d2556](06d2556)) * improve worktree skill with full dep sync and status enhancements ([#906](#906)) ([772c625](772c625)) * remove Vue remnants and document framework decision ([#851](#851)) ([bf2adf6](bf2adf6)) * update web dependencies and fix brace-expansion CVE ([#880](#880)) ([a7a0ed6](a7a0ed6)) * upgrade to Storybook 10 and TypeScript 6 ([#845](#845)) ([52d95f2](52d95f2)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Summary
DepartmentHealthtype with backend model -- renamename->department_name,health_percent->utilization_percent,cost_usd->department_cost_7d; removedisplay_nameandtask_count; addactive_agent_count,currency,avg_performance_score,cost_trend,collaboration_score. Cascade to 20+ consumer files includingDeptHealthBar(removetaskCountprop),DepartmentStatsBar,DepartmentGroupData, stores, tests, and stories.AgentConfigtype with backend -- makeid/status/hiring_dateoptional (runtime fields fromAgentIdentity), removeskills(unused), change typed sub-objects toRecord<string, unknown>, add requiredauthorityfield. All consumers use fallbacks (agent.id ?? agent.name,agent.status ?? 'active'). Remove dead interfaces (PersonalityConfig,ModelConfig,SkillSet,MemoryConfig,ToolPermissions).ActivityEventTypeliteral union (13 values) with(string & {})extensibility pattern. Type-narrowACTIVITY_ICON_MAPtoPartial<Record<ActivityEventType, LucideIcon>>.useAgentsDataanduseAgentDetailDatato coalesce burst events into a single API refetch. UsesuseReffor timeout handle with cleanup on unmount.Test plan
npm --prefix web run type-check-- zero errorsnpm --prefix web run lint-- zero warningsnpm --prefix web run test-- 2101 tests pass (12 new)agent.id: undefinedfalls back toagent.name,agent.status: undefinedtreated as'active'Pre-reviewed by 5 agents (frontend-reviewer, type-design-analyzer, test-analyzer, docs-consistency, issue-resolution-verifier), 11 findings addressed.
Closes #892
Closes #878
Closes #877
Closes #876
🤖 Generated with Claude Code