Documentation Index
Fetch the complete documentation index at: https://grantmaster.dev/llms.txt
Use this file to discover all available pages before exploring further.
Full-Stack Platform Bootstrap Specification
You are setting up a new production-grade full-stack platform. Follow this specification exactly to establish the architecture, patterns, and conventions. Replace [AppName] with the actual application name and [domain] with the business domain throughout.
1. Monorepo Structure (npm workspaces + Turbo)
[app-name]/
├── src/ # Main React web app
├── functions/ # Firebase Cloud Functions (separate workspace)
├── packages/
│ ├── shared/ # Shared types, utilities, constants across all workspaces
│ ├── domain-schema/ # Zod schemas as single source of truth for all entities
│ ├── domain-client/ # Typed tRPC client hooks consumed by web + mobile
│ └── tsconfig/ # Shared TypeScript configurations
├── portal/ # External-facing portal app (separate build/hosting)
├── apps/ # Future apps (mobile via Expo, etc.)
├── scripts/ # Automation, migration, validation scripts
├── tests/ # E2E test setup and auth states
├── .github/workflows/ # CI/CD pipelines
├── package.json # Root: workspaces config + orchestration scripts
├── turbo.json # Turbo build pipeline config
├── firebase.json # Firebase project config (emulators, hosting, rules)
├── .firebaserc # Environment → Firebase project mappings
├── firestore.rules # Firestore security rules
├── storage.rules # Storage security rules
├── firestore.indexes.json # Composite query indexes
├── vite.config.ts # Vite bundler config
├── vitest.config.ts # Unit test config
├── playwright.config.ts # E2E test config
├── tailwind.config.ts # Tailwind CSS config
└── CLAUDE.md # AI assistant project guide
Root package.json key scripts:
{
"workspaces": ["packages/*", "functions", "portal", "apps/*"],
"scripts": {
"dev": "vite --port 3000",
"dev:start": "firebase emulators:start --import=./seed-data --export-on-exit",
"dev:all": "concurrently \"npm run dev:start\" \"npm run dev\"",
"dev:all:fresh": "concurrently \"npm run dev:start -- --clear\" \"npm run dev\"",
"build": "tsc --noEmit && vite build",
"build:staging": "vite build --mode staging",
"test": "vitest",
"test:coverage": "vitest run --coverage",
"test:e2e": "playwright test",
"lint": "eslint . --ext .ts,.tsx",
"lint:fix": "eslint . --ext .ts,.tsx --fix"
}
}
2. Tech Stack
| Layer | Technology | Purpose |
|---|
| Frontend | React 19 + TypeScript | UI framework |
| Bundler | Vite 6 | Dev server + production builds |
| Styling | Tailwind CSS 4 + shadcn/ui (Radix primitives) | Design system |
| State (server) | @tanstack/react-query + tRPC | Server state + type-safe API |
| State (client) | React Context (3-tier provider hierarchy) | App/UI state |
| Forms | react-hook-form + Zod | Validation + form state |
| Tables | @tanstack/react-table + react-window | Virtual scrollable tables |
| Backend | Firebase (Auth, Firestore, Storage, Functions) | BaaS |
| API | Hono + tRPC + REST | Three-protocol API gateway |
| AI | Google Gemini via Genkit | AI features |
| i18n | i18next + react-i18next | Internationalization |
| Notifications | Internal system (src/features/notifications/) + Postmark (email) + FCM (push) | Multi-channel notifications; bell badge + toasts + email |
| Monitoring | Sentry | Error tracking + performance |
| Animations | motion (v12+) | Micro-interactions |
| PDF/Docs | @react-pdf/renderer, exceljs, docx, jspdf | Document generation |
| Testing | Vitest (unit) + Playwright (E2E) | Full test coverage |
| Payments | Stripe | Subscriptions + billing |
3. Core Architecture Patterns
3a. BaseService<T> — Abstract Service Layer
Every Firestore-backed feature uses a service that extends BaseService<T>:
// src/core/BaseService.ts
export abstract class BaseService<T extends BaseEntity> {
protected collectionName: string;
protected logger: Logger;
protected eventBus: EventBus;
// Built-in: tenant isolation, validation, logging, quota checks
abstract validate(data: Partial<T>): ValidationResult;
async create(data: Omit<T, 'id'>, organizationId: string): Promise<T> { /* ... */ }
async update(id: string, data: Partial<T>, organizationId: string): Promise<T> { /* ... */ }
async delete(id: string, organizationId: string): Promise<void> { /* ... */ }
async getById(id: string, organizationId: string): Promise<T | null> { /* ... */ }
async query(filters: QueryFilters, organizationId: string): Promise<T[]> { /* ... */ }
}
Rules:
- ALL services extend BaseService — no direct Firestore calls from components
- organizationId is ALWAYS required (enforces multi-tenancy at service layer)
- Zod schemas validate on write, explicit read-intent on read
- Services emit EventBus events for significant mutations
3b. EventBus — Decoupled Event System
// src/core/EventBus.ts
class EventBus {
emit<T>(event: TypedEvent<T>): void;
on<T>(eventType: string, handler: (payload: T) => void): Unsubscribe;
}
MUST emit events for:
- Approval/rejection workflows
- Entity lifecycle (created, archived, deleted)
- Business thresholds (budget 80%/90%/100%, deadlines)
- Compliance & audit events
- Submissions requiring attention
Do NOT emit for: reads, trivial updates, internal optimizations.
3c. Three-Tier Provider Hierarchy
<GlobalProviders> ← Auth, Theme, i18n, Sentry, QueryClient
<DomainProviders> ← Organization/Tenant, Feature flags, Permissions
<FeatureProviders> ← Per-feature contexts (grants, expenses, etc.)
<App />
</FeatureProviders>
</DomainProviders>
</GlobalProviders>
- GlobalProviders: Framework-level, always available
- DomainProviders: Business-level, requires authentication
- FeatureProviders: Feature-specific, lazy-loaded with routes
3d. Three-Protocol API Gateway (Cloud Functions)
Cloud Function (Hono) ─┬─ /api/trpc/* → tRPC routers (type-safe RPC for own apps)
├─ /api/v1/* → REST endpoints (third-party integrations)
└─ /api/mcp/* → MCP protocol (AI agent tools)
Three authentication paths converge to unified AuthContext:
- Firebase JWT → web/mobile apps
- API Key → third-party integrations
- Portal Token → external portal users
4. Feature Module Pattern
Every feature is self-contained:
src/features/[feature]/
├── components/
│ ├── pages/ # Route-level page components
│ ├── tabs/ # Tab content components
│ ├── sections/ # Page sections
│ └── cards/ # Feature-specific cards
├── hooks/
│ ├── use[Feature].ts # Main query/mutation hook
│ └── use[Feature]Selectors.ts # Derived/filtered data hooks
├── services/
│ └── [feature]Service.ts # BaseService<T> extension
├── schemas/
│ └── [feature].schema.ts # Zod schemas (source of truth for types)
├── contracts.ts # Enums, interfaces, state types
├── [Feature]Context.tsx # Feature-level React Context (if needed)
└── index.ts # Barrel export — PUBLIC API only
Rules:
- Features NEVER import from another feature’s internals — only via barrel export
- Schemas are the single source of truth — types are inferred from schemas
- Hooks handle server state (React Query), Context handles UI state
- Services handle all Firestore operations and business logic
5. UI Component Library
Layout Components (custom)
| Component | Purpose |
|---|
<PageLayout> | Wraps every page — consistent padding, max-width, scroll |
<PageHeader> | Page title + breadcrumbs + actions — never manual <h1> |
<PageTabs> | Tab navigation — render content externally, not via tab prop |
<SecondarySidebar> | Left sidebar nav for sub-pages — items or groups prop |
<PageShell> | App shell with sidebar + header |
shadcn/ui Base (install from shadcn CLI)
Button, Card, Input, Textarea, Select, Dialog, Sheet, Tabs, Badge, Table, Checkbox, Switch, Label, Alert, Popover, DropdownMenu, Command, Progress, Skeleton, ScrollArea, AlertDialog
Extended Components (build custom)
| Component | Purpose |
|---|
<StatusBadge> | Status indicators with mapDomainStatus() mapping |
<CardVariants> / <StatCard> | Metric display cards |
<DataTable> | @tanstack/react-table + virtual scrolling + bulk actions |
<FormField> / <FormSection> / <FormGrid> / <FormActions> | Form layout system |
<KpiCard> | KPI metric display |
<KanbanBoard> | Kanban view |
<EmptyState> | Empty state illustrations |
<AsyncBoundary> | Suspense + Error boundary combo |
<RouteErrorBoundary> | Route-level error handling |
Skeleton Loaders (one per page type)
PageSkeleton, DashboardSkeleton, TablePageSkeleton, FormPageSkeleton, WorkspaceSkeleton
UI Rules
- Colors: slate-* only (never gray-), primary- tokens (never blue-), emerald- for success, rose-* for error, amber-* for warning
- Dark mode:
dark:bg-{color}-500/10 for subtle tints
- Buttons: Always
<Button> component with isLoading + loadingText props
- Icons: lucide-react only. Sizes: buttons w-4 h-4, headers w-5 h-5, stats w-8 h-8
- Typography: h1=text-3xl font-bold, h2=text-xl font-semibold, h3=text-lg font-medium, body=text-sm text-slate-600
6. Routing System
// src/routes/lazyComponents.tsx — all route components lazy-loaded
export const DashboardPage = React.lazy(() => import('@/features/dashboard/components/pages/DashboardPage'));
export const ProjectsPage = React.lazy(() => import('@/features/projects/components/pages/ProjectsPage'));
// src/routes/routeConfig.tsx — centralized route definitions
export const routes: RouteConfig[] = [
{ path: '/dashboard', element: <DashboardPage />, label: 'Dashboard', icon: LayoutDashboard },
{ path: '/projects', element: <ProjectsPage />, label: 'Projects', icon: FolderOpen },
// ...
];
Rules:
- ALL route components use React.lazy() for code splitting
- Route config is centralized — never scatter route definitions
- Navigation config derives from route config
7. Firebase Cloud Functions
Dynamic Module Loading Pattern
// functions/src/index.ts
function registerModuleExports(modulePath: string) {
const mod = require(modulePath);
Object.assign(module.exports, mod);
Object.assign(exports, mod);
}
// Production: load all modules
registerModuleExports('./stripe');
registerModuleExports('./genkit');
// ... 20+ modules
// Emulator: load selectively for fast cold starts
if (process.env.FUNCTIONS_EMULATOR) {
registerModuleExports('./genkit'); // minimal set
}
Sentry Lazy-Loading (critical)
// functions/src/core/sentry.ts
// MUST lazy-load Sentry — @sentry/node registers OpenTelemetry hooks at import time
// which causes Firebase CLI's 10s function-discovery timeout to be exceeded
let _sentry: typeof import('@sentry/node') | null = null;
export function getSentry() {
if (!_sentry) _sentry = require('@sentry/node');
return _sentry;
}
Pre-Deployment Vendoring Pipeline
# scripts/prepare-functions.sh — firebase.json predeploy hook
# Step 1: Validate lockfile integrity
# Step 2: Sync shared packages → functions/shared/
# Step 3: Compile shared with esbuild
# Step 4: Vendor into functions/node_modules/ (Cloud Build can only see functions/)
# Step 5: Build functions with esbuild → lib/
# Step 6: Cold-start load budget verification (target 12s, hard limit 18s)
8. Security Model
Firestore Rules Pattern
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// ALL collections nested under organization for tenant isolation
match /organizations/{orgId} {
// Helper functions
function belongsToOrg() { return request.auth.token.organizationId == orgId; }
function isAdmin() { return belongsToOrg() && request.auth.token.role in ['admin', 'superadmin']; }
function isManagerOrHigher() { return belongsToOrg() && request.auth.token.role in ['manager', 'admin', 'superadmin']; }
match /[collection]/{docId} {
allow read: if belongsToOrg();
allow create: if isManagerOrHigher();
allow update: if isManagerOrHigher() && !request.resource.data.diff(resource.data).affectedKeys().hasAny(['organizationId']);
allow delete: if isAdmin();
}
}
}
}
Rules:
- NEVER allow cross-tenant queries without organizationId filter
- NEVER allow writes that change organizationId
- Role hierarchy: employee < manager < admin < superadmin
- Portal tokens use separate auth path with limited permissions
Storage Rules
- Path-based access:
/organizations/{orgId}/[type]/{docId}/{fileName}
- User self-managed paths (avatars)
- Admin-only platform paths
- Signed URL bypass for portal submissions
9. Testing Infrastructure
Unit Tests (Vitest)
// vitest.config.ts
export default defineConfig({
test: {
environment: 'jsdom',
setupFiles: ['tests/setup.ts'],
coverage: {
provider: 'v8',
thresholds: { lines: 30, functions: 24, branches: 20, statements: 30 }
},
testTimeout: 15000
}
});
E2E Tests (Playwright) — Role-Based
// playwright.config.ts
projects: [
{ name: 'setup', testMatch: '*.setup.ts' }, // Auth state setup
{ name: 'admin', testMatch: '*admin.spec.ts', dependencies: ['setup'] },
{ name: 'manager', testMatch: '*manager.spec.ts', dependencies: ['setup'] },
{ name: 'employee', testMatch: '*employee.spec.ts', dependencies: ['setup'] },
]
// Auth states stored in tests/.auth/{role}.json
10. CI/CD Pipeline (GitHub Actions)
Job Dependency Graph
lint (20+ checks) ─────────────────┐
unit-tests ─────────────────────────┤
ai-evals ──────────────────────────┤
e2e-tests (with Firebase emulators) ┤──→ test-summary ──→ build (staging/prod)
visual-regression-tests ────────────┤
functions-build ───────────────────┤
portal-quality ────────────────────┤
security-scan (npm audit + secrets) ┘
Lint Gates (enforce architecture)
- ESLint + TypeScript type checking
- Bundle size limits
- Architecture enforcement (no layer violations)
- Feature contracts validation
- Public API contracts
- i18n completeness
- Dead export detection
- BaseService adoption enforcement
- Firestore path validation
11. Environment Management
Firebase Projects
// .firebaserc
{
"projects": {
"default": "[app]-local",
"production": "[app]-prod",
"staging": "[app]-staging"
}
}
Emulator Ports (hardcoded, never change)
| Service | Port |
|---|
| Vite Dev Server | 3000 |
| Firebase Emulator UI | 4000 |
| Firestore | 8080 |
| Auth | 9099 |
| Storage | 9199 |
| Functions | 5001 |
Required Environment Variables
VITE_FIREBASE_API_KEY=
VITE_FIREBASE_AUTH_DOMAIN=
VITE_FIREBASE_PROJECT_ID=
VITE_FIREBASE_STORAGE_BUCKET=
VITE_FIREBASE_MESSAGING_SENDER_ID=
VITE_FIREBASE_APP_ID=
VITE_GEMINI_API_KEY=
VITE_SENTRY_DSN=
VITE_STRIPE_PUBLISHABLE_KEY=
Note: Novu was removed — the internal notification system replaces it. No VITE_NOVU_APP_ID needed.
12. Shared Hooks Library
Build these reusable hooks from day one:
| Hook | Purpose |
|---|
useZodForm | react-hook-form + Zod validation + toast + error handling |
useDataFetch | Generic data fetching with loading/error states |
usePaginatedData | Pagination logic |
useOptimisticUpdate | Optimistic UI updates |
useAsyncAction | Async action with loading/error/success states |
useEventBusRefresh | Refresh queries when EventBus events fire |
useKeyboardShortcuts | Keyboard shortcut registration |
useDeviceType | Responsive device detection |
useNetworkStatus | Online/offline detection |
useTouchGestures | Mobile gesture handling |
useAccessibility | a11y utilities |
useRoutePrefetch | Prefetch route data on hover/focus |
useTRPC | tRPC client hook setup |
13. Development Workflow
Getting Started
- Clone repo, run
npm install
- Copy
.env.example → .env.local, fill in keys
npm run dev:all — starts Firebase emulators + Vite dev server
- Navigate to localhost:3000
Adding a New Feature
- Create
src/features/[feature]/ with standard structure
- Add Zod schemas in
packages/domain-schema/
- Extend
BaseService<T> for the service layer
- Add route to
src/routes/routeConfig.tsx + lazy component
- Add feature context to FeatureProviders if needed
- Add Firestore security rules under
/organizations/{orgId}/
- Add composite indexes to
firestore.indexes.json
- Write unit tests colocated with source
- Write E2E tests in role-appropriate spec files
Adding a Cloud Function
- Create module in
functions/src/[module].ts
- Register in
functions/src/index.ts via registerModuleExports()
- Add to emulator selective load if needed for dev
- Add corresponding tRPC router if it serves API data
14. Bootstrap Execution Order
When setting up the new project, execute in this order:
- Initialize monorepo: package.json with workspaces, install Turbo
- Set up TypeScript configs: base configs in packages/tsconfig/
- Set up Vite + React: src/main.tsx, src/App.tsx, vite.config.ts
- Set up Tailwind + shadcn/ui: Install, configure theme tokens
- Set up Firebase: firebase.json, .firebaserc, emulator config
- Build core layer: BaseService, EventBus, Firebase client, error handling
- Build provider hierarchy: Global → Domain → Feature providers
- Build layout shell: Sidebar, Header, PageLayout, PageHeader
- Build routing system: lazyComponents, routeConfig, navigation
- Build UI component library: All shared components from Section 5
- Build shared hooks: All hooks from Section 12
- Set up Cloud Functions: Hono + tRPC + dynamic module loading
- Write security rules: Firestore + Storage with tenant isolation
- Set up testing: Vitest config + Playwright config + role-based setup
- Set up CI/CD: GitHub Actions with all lint gates
- Build first feature: Follow the feature module pattern end-to-end
- Set up monitoring: Sentry integration + error boundaries
- Set up i18n: i18next config + translation files
- Write CLAUDE.md: Project guide for AI assistants
Verification
To verify the prompt works:
Use it to bootstrap a new project (even a test one)
Check that the monorepo structure matches
Verify the provider hierarchy renders correctly
Confirm the feature module pattern is followed for the first feature
Run npm run dev:all and verify emulators + dev server start
Run npm test and verify the test infrastructure works
Deploy to staging and verify the pre-deployment pipeline runs