# Build Agent Learnings

> Rules learned from corrections. Check BEFORE every build.

## FIRESTORE INDEXES (Never Ask Humans)

- **NEVER** send humans a Firebase Console link to create indexes manually
- Create indexes yourself via CLI:
  ```bash
  # Add to firestore.indexes.json, then:
  firebase deploy --only firestore:indexes --project symph-{slug}
  ```
- Or use gcloud directly:
  ```bash
  gcloud firestore indexes composite create \
    --collection-group=dev_pages \
    --field-config=field-path=organizationId,order=ASCENDING \
    --field-config=field-path=createdAt,order=DESCENDING \
    --project=symph-{slug}
  ```
- This applies to ALL infra tasks — if you can automate it, don't ask a human to click buttons

## FIREBASE STORAGE (File Uploads)

- Use Firebase Storage for ANY file uploads (images, documents, submissions)
- **Bucket name format:** `symph-{slug}.appspot.com` (NOT `.firebasestorage.app`)
- **Enable via script (fully automated now!):**
  ```bash
  source /tmp/firebase-auth-env/bin/activate
  python /home/aria/.openclaw/workspace/skills/firebase/scripts/enable_storage.py symph-{slug}
  firebase deploy --only storage --project symph-{slug}
  ```
- See `agency-app-starter/CLAUDE.md` for patterns

### CORS Configuration (CRITICAL)
- Firebase Storage REQUIRES CORS config for browser uploads
- **Without CORS, uploads will fail silently with preflight errors**
- Run CORS setup script after enabling storage:
  ```bash
  python /home/aria/.openclaw/workspace/skills/firebase/scripts/set_storage_cors.py symph-{slug} https://your-domain.com
  ```
- Add localhost origins for local development

## DETAIL VIEWS (REQUIRED)

- Every list page MUST have a corresponding detail page
- If there's `/items` listing, there MUST be `/items/[id]` detail view
- List cards should be clickable links to detail pages
- Detail pages should include: full content, Edit button, Delete button

**Pattern:**
```
/lessons → /lessons/[id]
/assignments → /assignments/[id]
/grading → /grading/[id]
```

## NO MOCK DATA (CRITICAL)

- **NEVER** use hardcoded arrays for lists/dashboards
- **ALWAYS** query real data from Firestore
- **ALWAYS** show empty states when collections are empty
- Mock data = half-baked product = QA failure

**Test for mock data:**
1. Fresh account should show empty states, not pre-populated data
2. Created items must persist after refresh
3. Created items must persist after logout/login
4. **Detail pages ([id] routes)** must fetch real data, not hardcoded content
5. Click through to detail views and verify content matches what was created

```typescript
// ❌ WRONG
const lessons = [{ id: "1", title: "Mock Lesson" }];

// ✅ CORRECT  
const lessons = await getLessonsForUser(user.uid);
if (lessons.length === 0) return <EmptyState />;
```

## Template (CRITICAL)

- **NEVER** use `create-next-app` or `npx create-*`
- **ALWAYS** clone `git@github.com:symphco/agency-app-starter.git`
- The template has Firebase Auth, security rules, and subagents pre-configured

## Authentication (REQUIRED)

- Every app MUST have Firebase Auth with Email/Password
- **Enable auth via script (no console needed):**
  ```bash
  source /tmp/firebase-auth-env/bin/activate
  python /home/aria/.openclaw/workspace/skills/firebase/scripts/enable_email_auth.py symph-{slug}
  ```
- Configure auth BEFORE building features
- Use `(auth)` route group for protected routes
- Use `(public)` route group for landing page

## Landing Page (REQUIRED)

- Every app MUST have a landing page at `/`
- Landing page is public (no auth required)
- Landing page explains the product and has CTA to sign up/login
- App routes go under `(auth)` group (e.g., `/dashboard`, `/settings`)

## QA Testing (REQUIRED)

- Build is NOT complete until QA Agent passes **end-to-end tests**
- QA Agent uses browser automation for real testing
- **Test EVERY interaction**, not just page loads:
  - Click every button — does it work?
  - Click every list item — is there a detail view?
  - Try every form — does it submit?
  - Check all labels — are instructions clear?
- **Test actual functionality**, not just page loads:
  - ❌ "Login page renders" → too shallow
  - ✅ "Submitted login form, verified redirect to dashboard" → real test
- **Auth must work first** — if auth fails, QA stops and reports blocker
- Test against PRD user stories with real form submissions
- QA is part of Ralph Wiggum loop:
  1. QA finds issue → reports to coding agent
  2. Coding agent fixes → rebuilds
  3. QA re-tests → repeat until pass
- Check for:
  - Auth flow works end-to-end (signup, login, session, logout)
  - Console errors
  - Form submissions succeed
  - CRUD operations work
  - Error states handled

## Ralph Wiggum Loop

Use iterative development:
1. Read PRD and progress
2. Implement feature
3. Run code-review agent
4. Run security agent  
5. npm run build
6. Run QA agent with browser
7. Fix issues found
8. Loop until all completion criteria met

## Subagents (Use Them!)

Available in `.claude/agents/`:
- `qa.md` - Browser automation QA
- `code-review.md` - Code quality
- `security.md` - OWASP compliance
- `architect.md` - System design
- `copywriter.md` - UI text

## Build Checklist

Before marking build complete:
- [ ] Used agency-app-starter template
- [ ] Firebase project created and configured
- [ ] Firebase Auth enabled via `enable_email_auth.py` script
- [ ] Landing page exists and is public
- [ ] **NO MOCK DATA** — all lists query Firestore
- [ ] **Empty states** for empty collections
- [ ] **Data persists** after refresh and re-login
- [ ] All PRD features implemented with real CRUD
- [ ] `npm run build` succeeds
- [ ] QA Agent passed E2E tests INCLUDING data persistence
- [ ] Security Agent passed OWASP checks
- [ ] GitHub repo created and pushed
- [ ] Deployed to Caddy with healthcheck

## DESIGN PIPELINE (2-Stage Process)

### Stage 1: Preview Generation
- Generate: color palette, logo, fonts, 3 sample screens
- **ALWAYS include `generated_at` timestamps** for all items in design.yaml
- Update workflow.json to `preview_ready` status at 60%
- **STOP and wait for human approval**

### Stage 2: Full Generation (After Approval Only)
- Only trigger when human clicks "Approve Design" button
- Generate: brand-guidelines, design-brief, all screens from user stories
- Never auto-proceed to Stage 2

### Design.yaml Sample Screen Format
```yaml
sample_screens:
  - name: "Dashboard"
    description: "Main overview screen"
    path: "assets/screen-dashboard.png"
    status: "ready"
    generated_at: "2026-02-05T10:15:00Z"  # REQUIRED!
```

Without `generated_at`, the UI won't display the image.

## Firebase Auth Initialization (Discovered 2026-02-05)

**⚠️ ALWAYS DO THIS DURING BUILD - NO EXCEPTIONS**

**Problem:** `CONFIGURATION_NOT_FOUND` error when trying to enable Firebase Auth programmatically.

**Root Cause:** Identity Platform requires:
1. An **OPEN** billing account linked to the project
2. Explicit initialization via `initializeAuth` API endpoint

**Solution - Full automation flow (run EVERY build):**
```bash
# After creating Firebase project, ALWAYS run:
source /tmp/firebase-auth-env/bin/activate
python skills/firebase/scripts/enable_email_auth.py symph-{slug}
```

The script handles:
1. Finding an open billing account (defaults to Abaca)
2. Linking billing to project
3. Initializing Identity Platform
4. Enabling Email/Password auth

**Key insight:** The billing account must be `"open": true` — closed/suspended accounts won't work even if linked.

**Default billing account:** `billingAccounts/00446F-8B8C0A-26B94E` (Abaca - confirmed open)

## FIRESTORE RULES MUST MATCH ACTUAL CODE (CRITICAL - Added 2026-02-06)

**⚠️ Rules MUST match the EXACT collection names used in your app code!**

**Problem 1:** Rules define `match /users/{userId}` but app queries `dev_users`. 
**Problem 2:** Rules define `dev_users` but app code uses `dev_profiles`.
**Result:** "Missing or insufficient permissions"

**MANDATORY: Check actual collection names in code BEFORE writing rules:**
```bash
# Find what collections the app actually uses
grep -r "COLLECTIONS" src/lib/firebase/config.ts
```

**Then match rules EXACTLY:**
```javascript
// If app uses COLLECTIONS.profiles = 'dev_profiles'
match /dev_profiles/{userId} { ... }  // ✅ Matches code

// NOT this:
match /dev_users/{userId} { ... }     // ❌ Wrong name
```

**Checklist for EVERY build:**
- [ ] Check `.env.local` for `NEXT_PUBLIC_FIRESTORE_NAMESPACE`
- [ ] **READ the app's config.ts to see actual collection names**
- [ ] Update `firestore.rules` with EXACT collection names from code
- [ ] Include subcollections if used (e.g., `users/{uid}/completions`)
- [ ] Deploy rules: `firebase deploy --only firestore:rules --project {project-id}`
- [ ] Test login + data access works

---

## AUTO-CREATE PROFILES ON LOGIN (Added 2026-02-06)

**⚠️ Always handle missing user profiles gracefully!**

**Problem:** User registers → profile creation fails silently → user logs in → blank screen (no profile data)

**Solution:** In the `signIn` function, check if profile exists. If not, create it:

```typescript
const signIn = async (email: string, password: string) => {
  const result = await signInWithEmailAndPassword(auth, email, password);
  let profile = await fetchProfile(result.user.uid);
  
  // Auto-create profile if missing
  if (!profile) {
    console.log('No profile found, creating one...');
    await setDoc(doc(db, COLLECTIONS.profiles, result.user.uid), {
      email,
      schemaVersion: 1,
      createdAt: serverTimestamp(),
      updatedAt: serverTimestamp(),
      // ... other default fields
    });
    profile = await fetchProfile(result.user.uid);
  }
  
  setProfile(profile);
};
```

**Also remember:**
- Include `schemaVersion` in all Firestore documents
- Include `createdAt` and `updatedAt` timestamps
- Wrap Firestore writes in try/catch to avoid silent failures
- Log errors to console for debugging

---

## ENABLE REQUIRED FIREBASE APIS (CRITICAL - Added 2026-02-06)

**⚠️ Firebase APIs must be explicitly enabled BEFORE the app can use them!**

**Common error:** "Failed to get document because the client is offline" — means Firestore API isn't enabled

**APIs to enable for EVERY Firebase project:**
```bash
# Initialize Firestore (enables API + creates database)
firebase init firestore --project symph-{slug}

# Deploy rules
firebase deploy --only firestore:rules --project symph-{slug}
```

**Full Firebase setup checklist:**
1. [ ] Create project: `firebase projects:create symph-{slug}`
2. [ ] Enable Auth: `python enable_email_auth.py symph-{slug}`
3. [ ] Add authorized domain: `{slug}.dev.apps.symph.co`
4. [ ] **Initialize Firestore: `firebase init firestore --project symph-{slug}`**
5. [ ] Deploy rules with correct collection names
6. [ ] Enable Storage if needed: `python enable_storage.py symph-{slug}`

---

## VERIFY PROJECT ID MATCHES (CRITICAL - Added 2026-02-06)

**⚠️ ALWAYS verify the Firebase project ID in `.env.local` matches what you're configuring!**

**Problem that keeps recurring:** Auth is enabled on project A, but the app's `.env.local` points to project B. Result: `auth/configuration-not-found` error even though auth IS enabled.

**MANDATORY CHECK before every Firebase operation:**
```bash
# 1. Check what project the app is configured to use:
cat /home/aria/projects/{slug}/.env.local | grep FIREBASE_PROJECT_ID

# 2. Enable auth on THAT project (not what you think it should be):
python enable_email_auth.py <project-from-step-1>
```

**Common mistake:** 
- App built with project `symph-complimentary-app`
- But we enable auth on `symph-complimentary`
- These are TWO DIFFERENT projects!

**Rule:** Read the .env.local FIRST, use THAT project ID for all Firebase operations.

## Dev URLs Must Be Branded (2026-02-06)

**Rule:** Each product gets ONE dev URL, and it should be branded based on the product name, not the slug.

**Example:**
- ✅ `gala-grid.dev.apps.symph.co` (branded)
- ❌ `event-creation-event-suppliers.dev.apps.symph.co` (generic slug)

**Why:** Cleaner, more memorable, and avoids confusion with multiple URLs pointing to the same app.

**Naming convention:** Use the product name (from PRD/brief), kebab-cased. E.g., "Gala Grid" → `gala-grid`.

### Firestore Datastore Mode Bug (CRITICAL - 2026-02-09)
**Problem:** `firebase init firestore` sometimes creates the database in **Datastore Mode** instead of **Firestore Native Mode**. The Firestore client SDK silently hangs on all operations — app loads but auth/writes never complete (infinite spinner).
**Detection:** App loads login page fine but hangs after auth. Logs show `FAILED_PRECONDITION` or operations timeout.
**Fix:** Delete the Datastore Mode database via REST API, wait ~5 min, recreate in FIRESTORE_NATIVE mode:
```bash
# Delete
curl -X DELETE "https://firestore.googleapis.com/v1/projects/PROJECT/databases/(default)" -H "Authorization: Bearer $(gcloud auth print-access-token)"
# Recreate in Native mode
curl -X POST "https://firestore.googleapis.com/v1/projects/PROJECT/databases" \
  -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "Content-Type: application/json" \
  -d '{"type":"FIRESTORE_NATIVE","locationId":"nam5","databaseId":"(default)"}'
# Redeploy rules
firebase deploy --only firestore:rules --project PROJECT
```
**Affected builds:** Gala Grid, MetCabinet (both hit this on 2026-02-09)
**Prevention:** After `firebase init firestore`, verify mode before proceeding.

### NEVER Build Inside /home/aria/projects/symph-agency/ (CRITICAL - 2026-02-09)
**Rule:** No subagent or build process may EVER run `npm run build`, `rm -rf`, or modify files inside `/home/aria/projects/symph-agency/`. This is the PM2-managed Symph Agency UI.
**Deploy ONLY via:** `/home/aria/projects/symph-agency/deploy.sh` (uses staging dir, atomic swap)
**What happened:** A build agent likely corrupted/deleted `.next` causing 48 PM2 crash restarts and a 502 outage.
**Enforcement:** All build tasks must use `/home/aria/projects/{slug}/` (never `symph-agency`). Add this check to every build subagent prompt.

### 2026-02-10 | Next.js Standalone Mode Startup

**Issue:** Empty screen / CSS 500 errors on Personal Accounting dashboard

**Root Cause:** App configured with `output: "standalone"` in next.config, but started with `npm run start` instead of standalone server. This serves stale pre-rendered HTML with incorrect CSS references.

**Correct Approach for Standalone Apps:**
```bash
# After npm run build
cd /path/to/app
cp -r public .next/standalone/
cp -r .next/static .next/standalone/.next/
PORT=3015 node .next/standalone/server.js
```

**Wrong Approach:**
```bash
npm run start -- -p 3015  # Does NOT work properly with standalone
```

**Detection:** If CSS returns 500 but .css files exist in `.next/static/chunks/`, check if standalone mode is enabled.

**Tags:** #nextjs #standalone #css #build #deployment

### 2026-02-10 | Mobile-First Modal Standards (from Nicole)

**Feedback:** "please be cautious of padding and spacing of modals. make sure things are not covered. And everything should be mobile responsive by default"

**Standard for all Symph Agency products:**

1. **Dialog/Modal defaults:**
   - `max-h-[calc(100vh-2rem)]` with `overflow-y-auto`
   - Safe-area padding: `pb-[max(1rem,env(safe-area-inset-bottom))]`
   - Responsive padding: `p-4` mobile, `p-6` desktop
   - Close button should be touch-friendly (min 44x44 tap target)

2. **Category grids:**
   - `grid-cols-3` on mobile, `grid-cols-4` on desktop
   - Minimum button height for tap targets
   - Smaller fonts on mobile to prevent overflow

3. **Bottom navigation:**
   - Must include safe-area support: `paddingBottom: "max(0.5rem, env(safe-area-inset-bottom))"`
   - FAB position must respect safe areas

4. **Global CSS requirements:**
   - Input minimum 16px font to prevent iOS zoom
   - Safe-area-inset support in html/body
   - `-webkit-overflow-scrolling: touch` for iOS

**Tags:** #mobile #responsive #modals #ui #standards
