# Pitfalls Checklist: Business Operations Suite v2.0

Quick reference for preventing the 9 major pitfalls during Phase 7–9 implementation.

---

## Critical Pitfalls (Before Code)

### 1. Catalog Consolidation Breaks Quotes
- [ ] Migration strategy documented (Expand-Contract, not drop)
- [ ] Mapping layer between old/new tables designed
- [ ] Backfill script tested on production backup
- [ ] Referential integrity test query written
- [ ] Dual-write period defined (2-3 weeks)
- [ ] Rollback plan documented (drop new table, revert code)

### 2. Offer Template Mutation
- [ ] Offer/project hierarchy mapping documented (1 page)
- [ ] Copy function uses `db.transaction()` (all-or-nothing)
- [ ] Uses `structuredClone()` for deep copy, not `{...spread}`
- [ ] Idempotency key added to CRM leads table
- [ ] Integration test: copy offer, verify structure consistent across instances
- [ ] Test: edit project phase, verify offer template unchanged

### 3. Public Quote Token Leakage
- [ ] Token length ≥ 32 chars (nanoid(32) or nanoid(64))
- [ ] Expiration field added (default 7 days)
- [ ] Email validation on public page (token + email required)
- [ ] Rate limiting implemented (max 3 views/token/minute)
- [ ] Code audit: quote_items NOT in public API response
- [ ] Test: brute-force 1000 guesses, verify rate limit activates
- [ ] Test: enumerate tokens with different emails, verify 401

---

## Moderate Pitfalls (During Implementation)

### 4. Drag-and-Drop Race Condition
- [ ] `version` field added to offer_phases
- [ ] Update mutation checks version (returns 409 if mismatch)
- [ ] Server recomputes all sort_orders atomically
- [ ] Frontend handles 409 Conflict (refresh UI)
- [ ] Test: concurrent drag in 2 tabs, verify final state correct

### 5. CRM "Win" Double-Click
- [ ] Idempotency key added to crm_leads (unique)
- [ ] "Win" mutation checks if key already processed
- [ ] All sub-steps in single `db.transaction()`
- [ ] Button disabled until success
- [ ] Idempotency key stored in localStorage
- [ ] Test: double-click "Win", verify only 1 client created

### 6. Offer Copy Missing Tasks/Deliverables
- [ ] Mapping documented: offer_micro → project_phase → project_task → deliverable
- [ ] Copy function recursive (phases → tasks → deliverables)
- [ ] Validation test: assert counts match expected
- [ ] Integration test exists (copy, verify structure)
- [ ] Query test: no orphaned deliverables (task_id IS NULL)

---

## Minor Pitfalls (Polish)

### 7. Offer State Not Synced Between Tabs
- [ ] `version` field on offer_micros
- [ ] Update returns 409 on version mismatch
- [ ] UI shows "Offer changed, reload?" dialog

### 8. CRM Schema Backward Incompatibility
- [ ] New columns added as NULLABLE
- [ ] Code handles NULL defensively (e.g., `lead.budget ?? 0`)
- [ ] Backfill script written (sets defaults for existing rows)
- [ ] NOT NULL constraint added only after backfill + validation
- [ ] Test: production data copied to test db, code runs without errors

### 9. CRM Scope Creep
- [ ] Must-have features defined for v2.0 (lead pipeline, quote, auto-onboard)
- [ ] Should-have features listed for v2.1+ (email, calls, source)
- [ ] Won't-have features documented (team, multi-user, integrations)
- [ ] Scope document signed off by product
- [ ] Success metric defined: "Can go from lead to won project in <30 min"

---

## Phase 7 Checklist (Catalog & Offers)

**Before code:**
- [ ] Catalog consolidation migration plan reviewed
- [ ] Offer/project hierarchy documented
- [ ] Drag-drop version/locking strategy designed

**During implementation:**
- [ ] Migration backfill tested on production backup
- [ ] Drag-drop version field added
- [ ] Offer phase copy function atomic (transaction)
- [ ] Idempotency key on CRM leads
- [ ] Integration tests for copy consistency

**Before go-live:**
- [ ] Dry-run migration on production DB
- [ ] Verify referential integrity (no orphaned quote_items)
- [ ] Concurrent drag-drop test passes
- [ ] Offer copy count validation passes

---

## Phase 8 Checklist (Public Quote Pages)

**Before code:**
- [ ] Token security requirements finalized
- [ ] API response schema designed (no quote_items)
- [ ] Rate limiting strategy defined

**During implementation:**
- [ ] Token generation (nanoid(32))
- [ ] Expiration + email validation
- [ ] Rate limiting middleware
- [ ] Code audit for quote_items exposure
- [ ] Brute-force test (>1000 guesses/sec)

**Before go-live:**
- [ ] Brute-force test activates rate limit
- [ ] Enumeration test returns 401 for wrong email
- [ ] Expiration test returns 401 after expiration
- [ ] Code review: quote_items not in response

---

## Phase 9 Checklist (CRM Won Automation)

**Before code:**
- [ ] Scope document (Must/Should/Could/Won't) reviewed
- [ ] Idempotency strategy designed
- [ ] Payment plan algorithm defined
- [ ] Offer snapshot structure (JSONB) designed

**During implementation:**
- [ ] Idempotency key on crm_leads
- [ ] "Win" mutation atomic (client + project + phases + payments)
- [ ] Offer snapshot stored on win
- [ ] Button disabled until success
- [ ] Payment consistency tests

**Before go-live:**
- [ ] Double-click test: only 1 client created
- [ ] Network timeout test: retry returns same project
- [ ] Partial failure test: rollback on error
- [ ] Payment consistency test: correct number created
- [ ] Idempotency test: 10x "Win" with same key = 1 client

---

## Key Questions Before Each Phase

**Phase 7:**
- ✓ Is catalog consolidation migration safe (Expand-Contract, dual-write)?
- ✓ Does offer/project hierarchy mapping make sense (1 micro = 1 phase)?
- ✓ Is copy function atomic (all-or-nothing)?
- ✓ Do we have integration tests for offer phase structure consistency?

**Phase 8:**
- ✓ Is token length ≥ 32 chars and properly random?
- ✓ Do we validate both token AND email on public page?
- ✓ Is rate limiting active (max 3 views/token/min)?
- ✓ Can we prove quote_items never appears in public response?

**Phase 9:**
- ✓ Is "Win" action atomic (transaction)?
- ✓ Does idempotency key prevent duplicates on retry?
- ✓ Is button disabled until success?
- ✓ Have we defined Must/Should/Won't scope with user?

---

## Testing Validation Matrix

| Pitfall | Test Case | Expected Outcome | Status |
|---------|-----------|------------------|--------|
| 1. Catalog consolidation | Backfill migration on prod backup | Zero orphaned quote_items | [ ] |
| 2. Offer template mutation | Edit project phase, check offer unchanged | Offer template immutable | [ ] |
| 3. Token leakage | Brute-force 1000 guesses/sec | Rate limit activates (429) | [ ] |
| 3. Token leakage | Enumerate with wrong email | Returns 401 Unauthorized | [ ] |
| 4. Drag-drop race | Concurrent drag in 2 tabs | Final sort_order is coherent | [ ] |
| 5. Double-click clients | Click "Win" twice | Only 1 client created | [ ] |
| 5. Double-click clients | Network timeout + retry | Same project returned | [ ] |
| 6. Missing deliverables | Copy offer to project | All tasks and deliverables copied | [ ] |
| 8. Schema backward compat | Copy production data to test DB | No crashes, NULL handled | [ ] |
| 9. Payment consistency | "Win" with partial failure | Rollback, no orphaned payments | [ ] |

---

## Red Flags During Development

🚨 **Stop and review if:**
- Offer/project hierarchy mapping is unclear (should be 1 page, explicit)
- Drag-drop sort_order updates without version check
- "Win" mutation has any non-transactional steps (client created outside txn)
- Copy function uses shallow spread (`{...obj}`) instead of `structuredClone()`
- Token length < 32 chars
- Public API response includes quote_items or per_service_price
- CRM leads table has no idempotency_key column
- Schema changes add NOT NULL without backfill test
- "Win" button not disabled until response received
- No rate limiting on public quote page

---

**Owner:** Development team  
**Review:** Before each phase kickoff  
**Update:** As testing results come in
