# 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