AI Agent Guide
AI Agent Guide
Section titled “AI Agent Guide”This guide is for AI coding agents working in the Cosella codebase. It defines the rules, patterns, and constraints that every agent must follow to produce correct, reviewable contributions.
Pre-Flight Checklist
Section titled “Pre-Flight Checklist”Before writing any code, every agent MUST:
- Run
pnpm typecheckto confirm the baseline is clean - Run
pnpm testto confirm tests pass - Identify which package(s) are affected
- Never modify more packages than necessary
Repository Architecture
Section titled “Repository Architecture”cosella/├── apps/ # Runnable applications (6)│ ├── dashboard/ # Rep-facing sales dashboard (React + Vite + TanStack Router)│ ├── admin/ # Internal admin panel (React + Vite + TanStack Router)│ ├── desktop/ # Electron desktop copilot (Electron + React + Vite)│ ├── docs/ # Documentation site (Astro + Starlight)│ ├── marketing/ # Public landing page (Astro + React)│ └── storybook/ # Component stories (Storybook 9 + Vite)├── packages/ # Shared libraries (13)│ ├── ui/ # React component library (25+ components)│ ├── tokens/ # Design tokens (CSS + JS)│ ├── domain/ # Shared domain types with Zod schemas│ ├── api-client/ # Generated OpenAPI types + TanStack Query hooks│ ├── realtime/ # Typed WebSocket client│ ├── auth/ # Auth provider, session store│ ├── permissions/ # Capability-based permission helpers│ ├── testing/ # MSW server, handlers, fixtures│ ├── observability/ # Error boundaries, Sentry integration│ ├── app-shell/ # AppShell layout, Sidebar, TopBar│ ├── forms/ # React Hook Form + Zod integration│ ├── audio-capture/ # Audio capture (Web + Electron)│ └── audio-engine/ # Audio pipeline, VAD, Opus encoding└── tooling/ # Build config packages (3) ├── eslint-config/ # Shared ESLint configs ├── ts-config/ # Shared TypeScript configs └── plop-templates/ # Handlebars templates for generatorsDependency Rules (Hard Constraints)
Section titled “Dependency Rules (Hard Constraints)”These are enforced by eslint-plugin-boundaries at lint time. Violations will fail CI.
apps ──────> packages (one direction only, never reverse)
tokens ─────> (nothing internal)domain ─────> (nothing internal)ui ─────────> tokens ONLYapi-client ─> domainrealtime ───> domainauth ───────> domainpermissions > domain + auth (declared exception)testing ───> domainobservability > ui + domain (NEVER auth)app-shell ──> auth + tokens + uiforms ──────> uiaudio-capture > domainaudio-engine > domainRule: Apps may depend on packages — never the reverse. Packages may not depend on sibling apps.
TypeScript Non-Negotiables
Section titled “TypeScript Non-Negotiables”The tsconfig uses strict mode with these additional strictness flags:
exactOptionalPropertyTypes: true—undefinedmust be explicit in union typesnoUncheckedIndexedAccess: true— index access returnsT | undefinednoUnusedLocals: true— no unused imports or variablesnoUnusedParameters: true— no unused function parameters
Forbidden Patterns
Section titled “Forbidden Patterns”// ❌ DO NOT use `as` castsconst x = data as SomeType;
// ❌ DO NOT use `any`const x: any = data;
// ❌ DO NOT use `!` non-null assertionsconst el = document.getElementById('x')!;
// ❌ DO NOT use forwardRef (React 19)const Button = forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => ...);Required Patterns
Section titled “Required Patterns”// ✅ Use type narrowingfunction isString(value: unknown): value is string { return typeof value === 'string';}
// ✅ Use ref as prop (React 19)function Button({ ref, ...props }: ButtonProps & { ref?: React.Ref<HTMLButtonElement> }) { return <button ref={ref} {...props} />;}
// ✅ Use cn() for className mergingimport { cn } from '../lib/utils.js';<div className={cn('base-class', conditional && 'active', className)} />
// ✅ Use consistent-type importsimport type { ButtonProps } from './Button.js';Code Style
Section titled “Code Style”File Naming
Section titled “File Naming”packages/ui/src/├── button/│ ├── Button.tsx # PascalCase component file│ ├── Button.stories.tsx # Storybook stories│ └── Button.test.tsx # Vitest testsImport Order
Section titled “Import Order”// 1. React / external librariesimport { useState, useEffect } from 'react';import { cva, type VariantProps } from 'class-variance-authority';
// 2. Internal packages (workspace references)import { cn } from '../lib/utils.js';import type { ButtonProps } from './Button.js';
// 3. Local importsimport { someLocalUtil } from './utils.js';Component Pattern
Section titled “Component Pattern”import { cva, type VariantProps } from 'class-variance-authority';import { cn } from '../lib/utils.js';
const componentVariants = cva('base-classes', { variants: { variant: { primary: 'primary-classes', secondary: 'secondary-classes', }, }, defaultVariants: { variant: 'primary', },});
export interface ComponentProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof componentVariants> { customProp?: boolean;}
export function Component({ className, variant, customProp, children, ref, ...props}: ComponentProps & { ref?: React.Ref<HTMLDivElement> }) { return ( <div ref={ref} className={cn(componentVariants({ variant }), className)} {...props} > {children} </div> );}Package Creation
Section titled “Package Creation”Use the Plop generator — never create a package by hand:
pnpm gen packageThis creates:
packages/<name>/package.jsonpackages/<name>/tsconfig.jsonpackages/<name>/src/index.ts
Then add the new package as a workspace:* dependency where needed and re-run pnpm install.
Component Creation
Section titled “Component Creation”pnpm gen componentThis creates:
packages/ui/src/<kebab-name>/<Name>.tsxpackages/ui/src/<kebab-name>/<Name>.test.tsx- Export appended to
packages/ui/src/index.ts
Component Rules
Section titled “Component Rules”- Always use
cn()for className merging (never string concatenation) - Always use
cva()for variant-based styling - Always accept
classNameprop for consumer overrides - Always export the props interface alongside the component
- Always use
refas prop (React 19 — noforwardRef) - Always use WAI-ARIA patterns for accessibility
- Always use Lucide React for icons
Testing Requirements
Section titled “Testing Requirements”Visual Primitives (packages/ui)
Section titled “Visual Primitives (packages/ui)”Ship with a Storybook story. Add play interactions when there is behaviour worth asserting. Vitest unit tests are optional and usually redundant with play.
Logic-Bearing Packages
Section titled “Logic-Bearing Packages”Every new exported function needs a Vitest test. Includes:
permissionsobservabilityrealtimeapi-clientauth- Any future package whose value is behaviour rather than markup
Feature slices with meaningful logic get tests; pure-render route components generally do not.
API Interactions
Section titled “API Interactions”Must use the MSW handlers from @cosella/testing:
import { http, HttpResponse } from 'msw';import { server } from '@cosella/testing';
// In your testserver.use( http.get('/api/calls', () => { return HttpResponse.json({ calls: [] }); }),);Testing Library Queries
Section titled “Testing Library Queries”Use queries by role, text, label — not test IDs:
// ✅ Goodscreen.getByRole('button', { name: /submit/i });screen.getByText('Welcome back');screen.getByLabelText('Email');
// ❌ Badscreen.getByTestId('submit-button');Commit Convention
Section titled “Commit Convention”Follow Conventional Commits. The commit message must pass commitlint. Format:
feat(package-name): short description under 72 charsScope = the package or app name without the @cosella/ prefix.
Examples
Section titled “Examples”feat(ui): add ProgressBar componentfix(api-client): handle network timeout errorsdocs(readme): update installation guiderefactor(permissions): simplify capability checkstest(realtime): add WebSocket reconnection testsstyle(ui): adjust Button hover transitionArchitecture Decision Records
Section titled “Architecture Decision Records”ADRs live at apps/docs/src/content/docs/engineering/adr-NNNN.mdx. When making a load-bearing architectural decision, write (or update) an ADR before the implementation PR.
ADR Template
Section titled “ADR Template”---title: ADR-NNNN: Decision Titledescription: Brief summary of the decision.---
# ADR-NNNN: Decision Title
## Status
Proposed | Accepted | Deprecated | Superseded by [ADR-NNNN](/engineering/adr-NNNN)
## Context
What is the issue that we're seeing that motivates this decision?
## Decision
What is the change that we're proposing and/or doing?
## Consequences
What becomes easier or more difficult to do because of this change?
### Positive
- ...
### Negative
- ...AI Agent Specific Rules
Section titled “AI Agent Specific Rules”For Claude Code / Codex
Section titled “For Claude Code / Codex”- Read
CLAUDE.mdandAGENTS.mdbefore making any changes - Run
pnpm typecheckafter every change - Never modify
routeTree.gen.tsmanually - Never modify
packages/api-client/src/api.gen.tsmanually - Never connect
packages/authto a real backend without explicit instruction
For Copilot / Cursor
Section titled “For Copilot / Cursor”- Respect the file boundaries — don’t suggest changes outside the current package
- Use the existing patterns — look at neighboring files before writing new code
- Import types with
import typewhen the import is type-only - Don’t suggest
ascasts — suggest type narrowing instead
For Any Agent
Section titled “For Any Agent”- Never commit secrets or keys to the repository
- Never disable ESLint rules — fix the underlying issue instead
- Never skip tests by inferring a “convention” from missing files
- Always verify with
pnpm typecheckandpnpm lintbefore suggesting completion - Always follow the existing code patterns in the surrounding context
What NOT to Do
Section titled “What NOT to Do”- Do not run
pnpm addwith arbitrary package versions — usecatalog:entries frompnpm-workspace.yaml - Do not edit
routeTree.gen.tsmanually — it is regenerated onpnpm dev/pnpm build - Do not edit
packages/api-client/src/api.gen.tsmanually — regenerate withpnpm gen:api - Do not connect
packages/authto a real backend without explicit instruction - Do not create
docs/engineering/adr/at the repo root — ADRs go in the Starlight docs source - Do not skip tests by inferring a “convention” from missing files
- Do not use
ascasts unless absolutely necessary and always add a comment explaining why - Do not use
anyexcept in generated files (routeTree.gen.ts,api.gen.ts) - Do not use
!non-null assertions — narrow the type properly
Quick Reference Commands
Section titled “Quick Reference Commands”# Developmentpnpm dev # Start all appspnpm --filter @cosella/dashboard dev # Start dashboard onlypnpm --filter @cosella/storybook dev # Start Storybook
# Code Qualitypnpm typecheck # Type check all packagespnpm lint # Lint all packagespnpm test # Run all tests
# Code Generationpnpm gen component # Generate new UI componentpnpm gen package # Generate new packagepnpm gen:api # Regenerate API client from OpenAPI spec
# Buildpnpm build # Build all packages and appsFile Reference
Section titled “File Reference”| File | Purpose |
|---|---|
CLAUDE.md | Claude Code specific instructions |
AGENTS.md | AI agent general instructions |
docs/REVIEW.md | Code review standard (354 lines) |
packages/ui/src/index.ts | All UI component exports |
packages/tokens/src/theme.css | All design tokens |
packages/tokens/src/components.css | Shared CSS component classes |
pnpm-workspace.yaml | Workspace definition + catalog versions |
turbo.json | Turborepo pipeline config |
plopfile.mjs | Code generator definitions |