Part of CLAUDE.md Critical Conventions
When to load: Working on NextJS application, UI components, or React Query integration
- Framework: Next.js 14 (App Router)
- UI Library: Shadcn UI (built on Radix UI primitives)
- Styling: Tailwind CSS
- Data Fetching: TanStack React Query
- Primary Directory:
components/frontend/src/
FORBIDDEN:
// ❌ BAD
function processData(data: any) { ... }REQUIRED:
// ✅ GOOD - use proper types
function processData(data: AgenticSession) { ... }
// ✅ GOOD - use unknown if type truly unknown
function processData(data: unknown) {
if (isAgenticSession(data)) { ... }
}FORBIDDEN: Creating custom UI components from scratch for buttons, inputs, dialogs, etc.
REQUIRED: Use @/components/ui/* components
// ❌ BAD
<button className="px-4 py-2 bg-blue-500">Click</button>
// ✅ GOOD
import { Button } from "@/components/ui/button"
<Button>Click</Button>Available Shadcn components: button, card, dialog, form, input, select, table, toast, etc.
Check: components/frontend/src/components/ui/ for full list
FORBIDDEN: Manual fetch() calls in components
REQUIRED: Use hooks from @/services/queries/*
// ❌ BAD
const [sessions, setSessions] = useState([])
useEffect(() => {
fetch('/api/sessions').then(r => r.json()).then(setSessions)
}, [])
// ✅ GOOD
import { useSessions } from "@/services/queries/sessions"
const { data: sessions, isLoading } = useSessions(projectName)REQUIRED: Always prefer type for type definitions
// ❌ AVOID
interface User { name: string }
// ✅ PREFERRED
type User = { name: string }FORBIDDEN: Creating components in shared directories if only used once
REQUIRED: Keep page-specific components with their pages
app/
projects/
[projectName]/
sessions/
_components/ # Components only used in sessions pages
session-card.tsx
page.tsx # Uses session-cardFORBIDDEN: useFlag() from @unleash/proxy-client-react — it doesn't work with workspace overrides.
REQUIRED: useWorkspaceFlag(projectName, flagName) for workspace-scoped flags (shared hook, 15s staleTime). Evaluates: ConfigMap override > Unleash default.
// ❌ BAD
import { useFlag } from "@unleash/proxy-client-react"
const enabled = useFlag("my-feature")
// ✅ GOOD
import { useWorkspaceFlag } from "@/services/queries/feature-flags"
const enabled = useWorkspaceFlag(projectName, "my-feature")If a specific page needs instant flag freshness, use useQuery with evaluateFeatureFlag() directly and set staleTime: 0, refetchOnMount: 'always' — don't modify the shared hook.
New flags go in components/manifests/base/core/flags.json with scope:workspace tag. Use /unleash-flag to scaffold.
// app/projects/[projectName]/sessions/page.tsx
import { useSessions } from "@/services/queries/sessions"
import { Button } from "@/components/ui/button"
import { Card } from "@/components/ui/card"
export default function SessionsPage({
params,
}: {
params: { projectName: string }
}) {
const { data: sessions, isLoading, error } = useSessions(params.projectName)
if (isLoading) return <div>Loading...</div>
if (error) return <div>Error: {error.message}</div>
if (!sessions?.length) return <div>No sessions found</div>
return (
<div>
{sessions.map(session => (
<Card key={session.metadata.name}>
{/* ... */}
</Card>
))}
</div>
)
}// services/queries/sessions.ts
import { useQuery, useMutation } from "@tanstack/react-query"
import { sessionApi } from "@/services/api/sessions"
export function useSessions(projectName: string) {
return useQuery({
queryKey: ["sessions", projectName],
queryFn: () => sessionApi.list(projectName),
})
}
export function useCreateSession(projectName: string) {
return useMutation({
mutationFn: (data: CreateSessionRequest) =>
sessionApi.create(projectName, data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["sessions", projectName] })
},
})
}The + button dropdown in new-session-view.tsx is the single entry point for all per-session configuration.
File: src/app/projects/[name]/sessions/[sessionName]/components/new-session-view.tsx
- Add state:
const [myToggle, setMyToggle] = useState(false) - Add
DropdownMenuCheckboxItemafter<DropdownMenuSeparator /> - Wire into
handleSubmit→onCreateSession({ ..., myToggle: myToggle || undefined }) - Update
NewSessionViewProps.onCreateSessionconfig type - Wire through
page.tsxinto the mutation payload - Bump
MENU_VERSIONconstant (triggers the discovery dot)
- Create form component (e.g.,
components/my-config.tsx) with save/preview/dirty-guard - Add
DropdownMenuItemthat opens aDialog - Render the
Dialogoutside the dropdown (at component root level) - Wire saved values into
handleSubmit→onCreateSession - Bump
MENU_VERSIONconstant
MENU_VERSION constant (date string) + useLocalStorage with key acp-menu-seen-version. Bump the version when adding new menu items — the dot appears until the user opens the menu.
New options flow: onCreateSession config → page.tsx → createSessionMutation.mutate() → POST /api/projects/{project}/agentic-sessions → backend handler → CR spec. For booleans, add to CreateAgenticSessionRequest in types/session.go. For complex options, serialize to env var on the CR and parse in the runner.
When adding a new *ConnectionCard to the platform:
- Add the status field to
IntegrationsStatusinsrc/services/api/integrations.ts. - Add one
IntegrationEntrytoINTEGRATION_REGISTRYinsrc/components/onboarding/integration-registry.ts:idmatching the new key inIntegrationsStatusname,descriptionfor displayisConnectedfunction deriving boolean from the status shaperenderCardwrapping the new*ConnectionCardcomponent
- No other onboarding files need to change. The integrations step iterates the registry automatically.
The compile-time guard will error if you add a status field without a registry entry.
- Zero
anytypes (or justified with eslint-disable) - All UI uses Shadcn components
- All data operations use React Query
- Components under 200 lines
- Single-use components colocated
- All buttons have loading states
- All lists have empty states
- All nested pages have breadcrumbs
-
npm run buildpasses with 0 errors, 0 warnings - All types use
typeinstead ofinterface
components/frontend/DESIGN_GUIDELINES.md- Comprehensive patternscomponents/frontend/COMPONENT_PATTERNS.md- Architecture patternssrc/components/ui/- Shadcn UI componentssrc/services/queries/- React Query hookssrc/services/api/- API client layer