diff --git a/apps/web/src/layouts/AppLayout.test.tsx b/apps/web/src/layouts/AppLayout.test.tsx
new file mode 100644
index 0000000..8d12ba8
--- /dev/null
+++ b/apps/web/src/layouts/AppLayout.test.tsx
@@ -0,0 +1,66 @@
+// @vitest-environment happy-dom
+import { render, screen, within } from '@testing-library/react'
+import '@testing-library/jest-dom/vitest'
+import { vi } from 'vitest'
+import { AppLayout } from './AppLayout'
+
+// Mock dependencies
+vi.mock('@tanstack/react-router', () => ({
+ Link: ({ children, to }: any) => {
+ const content = typeof children === 'function' ? children({ isActive: false }) : children
+ return {content}
+ },
+}))
+vi.mock('@/components/ThemeToggle', () => ({ ThemeToggle: () => null }))
+vi.mock('@/components/HealthDot', () => ({ HealthDot: () => null }))
+vi.mock('@/components/NavBadge', () => ({
+ NavBadge: ({ count }: { count: number }) => (
+ count > 0 ? {count} : null
+ ),
+}))
+
+const mockUseQuery = vi.hoisted(() => vi.fn())
+vi.mock('@/lib/trpc', () => ({
+ trpc: {
+ listAgents: { useQuery: mockUseQuery },
+ },
+}))
+
+beforeEach(() => {
+ vi.clearAllMocks()
+ mockUseQuery.mockReturnValue({ data: [] })
+})
+
+describe('AppLayout navItems', () => {
+ it('renders HQ nav link', () => {
+ render({null})
+ expect(screen.getByRole('link', { name: /hq/i })).toBeInTheDocument()
+ })
+
+ it('does not render Inbox nav link', () => {
+ render({null})
+ expect(screen.queryByRole('link', { name: /inbox/i })).not.toBeInTheDocument()
+ })
+
+ it('shows badge on HQ when agents are waiting_for_input', () => {
+ mockUseQuery.mockReturnValue({
+ data: [
+ { id: '1', status: 'waiting_for_input' },
+ { id: '2', status: 'running' },
+ ],
+ })
+ render({null})
+ // NavBadge rendered next to HQ link (count=1)
+ const hqLink = screen.getByRole('link', { name: /hq/i })
+ const badge = within(hqLink).getByTestId('nav-badge')
+ expect(badge).toHaveTextContent('1')
+ })
+
+ it('does not show questions badge on any Inbox link (Inbox removed)', () => {
+ mockUseQuery.mockReturnValue({
+ data: [{ id: '1', status: 'waiting_for_input' }],
+ })
+ render({null})
+ expect(screen.queryByRole('link', { name: /inbox/i })).not.toBeInTheDocument()
+ })
+})
diff --git a/apps/web/src/layouts/AppLayout.tsx b/apps/web/src/layouts/AppLayout.tsx
index 7dbc2eb..aa41888 100644
--- a/apps/web/src/layouts/AppLayout.tsx
+++ b/apps/web/src/layouts/AppLayout.tsx
@@ -7,11 +7,10 @@ import { trpc } from '@/lib/trpc'
import type { ConnectionState } from '@/hooks/useConnectionStatus'
const navItems = [
- { label: 'HQ', to: '/hq', badgeKey: null },
+ { label: 'HQ', to: '/hq', badgeKey: 'questions' as const },
{ label: 'Initiatives', to: '/initiatives', badgeKey: null },
{ label: 'Agents', to: '/agents', badgeKey: 'running' as const },
{ label: 'Radar', to: '/radar', badgeKey: null },
- { label: 'Inbox', to: '/inbox', badgeKey: 'questions' as const },
{ label: 'Settings', to: '/settings', badgeKey: null },
] as const