| name | atomic-crm-constitution |
| description | Use when implementing features in Atomic CRM - enforces Engineering Constitution principles including fail-fast error handling, single source of truth for validation, form state from Zod schemas, and pre-launch velocity over resilience |
Atomic CRM Engineering Constitution
Overview
Enforce Atomic CRM's Engineering Constitution principles to prevent over-engineering and maintain codebase velocity. Most critical: fail fast (no retry logic/circuit breakers) and single source of truth (Zod validation only).
Core principle: Pre-launch phase prioritizes velocity over resilience. Simple solutions over clever ones.
When to Use
Use this skill when:
- Handling errors or adding resilience
- Adding validation or data transforms
- Creating forms with default values
- Defining TypeScript types
- Editing existing files
- Creating database migrations
Do NOT use for:
- Reading documentation
- Analyzing existing code
- Non-implementation tasks
Pre-Implementation Checklist
Before writing code, verify:
- Read
.claude/engineering-constitution.mdif unsure about any principle - Error handling: Am I adding retry logic? (NO - fail fast)
- Validation: Am I validating outside Zod schemas? (NO - API boundary only)
- Form defaults: Am I hardcoding in component? (NO - use
zodSchema.partial().parse({})) - TypeScript: Using
interfacefor objects,typefor unions? - Editing file: Am I fixing nearby issues? (YES - Boy Scout Rule)
Critical Rules
1. NO OVER-ENGINEERING (Most Violated)
Rule: No circuit breakers, retry logic, or graceful fallbacks. Fail fast.
Context: Pre-launch phase = velocity over resilience. We want loud failures, not silent degradation.
❌ FORBIDDEN PATTERNS:
// ❌ Circuit breaker
class CircuitBreaker {
state: 'OPEN' | 'CLOSED' | 'HALF-OPEN'
}
// ❌ Retry logic with exponential backoff
for (let i = 0; i < MAX_RETRIES; i++) {
try {
return await operation()
} catch (error) {
await sleep(Math.pow(2, i) * 100)
}
}
// ❌ Graceful fallbacks
try {
return await fetchData()
} catch {
return cachedData // Silent degradation
}
// ❌ Health monitoring
if (failureCount > threshold) {
activateCircuitBreaker()
}
✅ CORRECT PATTERN:
// ✅ Let it throw - operator sees error immediately
const data = await supabase.from('contacts').select()
// If 429 error occurs, it throws
// Operator investigates and fixes at source
Why Fail Fast:
- Complex error handling = maintenance burden
- No users yet = no one benefits from resilience
- Loud failures = immediate investigation
- Silent degradation = hidden problems
Common Rationalizations to REJECT:
- "This is for production" → We're pre-launch, velocity matters more
- "It needs to be resilient" → Resilience = fail loud, not graceful degradation
- "Users will see errors" → No users yet, operators need to see errors
- "Industry best practice" → Context matters, pre-launch has different needs
2. SINGLE SOURCE OF TRUTH
Rule: Zod schemas at API boundary (src/atomic-crm/validation/) ONLY. No validation elsewhere.
❌ WRONG - Multiple Validation Sources:
// ❌ Validation in component
const isValidEmail = (email: string) => /@/.test(email)
// ❌ Validation in utils
function validateContact(data) {
if (!data.email?.includes('@')) throw new Error()
}
// Now two+ definitions can diverge!
✅ CORRECT - Zod at API Boundary:
// src/atomic-crm/validation/contacts.ts
export const contactSchema = z.object({
email: z.string().email(), // Single source of truth
})
// Import everywhere:
import { contactSchema } from '@/atomic-crm/validation/contacts'
3. FORM STATE DERIVED FROM TRUTH
Rule: React Hook Form defaultValues MUST use zodSchema.partial().parse({}).
❌ WRONG - Hardcoded Defaults:
// ❌ Hardcoded in component
const form = useForm({
defaultValues: {
stage: 'new_lead', // Out of sync with schema!
priority: 'medium',
}
})
// ❌ Using defaultValue prop
<SelectInput source="stage" defaultValue="new_lead" />
✅ CORRECT - Schema-Derived Defaults:
// 1. Define defaults in Zod schema
export const opportunitySchema = z.object({
stage: z.string().default('new_lead'),
priority: z.string().default('medium'),
})
// 2. Extract defaults in component
const schemaDefaults = opportunitySchema.partial().parse({})
const form = useForm({
defaultValues: {
...schemaDefaults,
owner_id: identity.id, // Runtime values merged
}
})
// 3. NO defaultValue props on inputs
<SelectInput source="stage" /> // Uses form default
Why: Prevents drift between validation and UI.
4. BOY SCOUT RULE
Rule: Fix inconsistencies when editing files. Leave code better than you found it.
Examples:
- See unused import? Delete it
- See inconsistent spacing? Fix it
- See missing type? Add it
- See hardcoded value? Extract to constant
Scope: Only fix issues in files you're editing. Don't go on refactoring sprees.
5. TYPESCRIPT CONVENTIONS
Rule: interface for objects/classes, type for unions/intersections.
// ✅ Interfaces for object shapes
interface Contact {
id: string
first_name: string
}
// ✅ Types for unions
type Status = 'active' | 'inactive'
// ✅ Types for intersections
type ContactWithMeta = Contact & { created_at: string }
6. FORMS - USE REACT ADMIN COMPONENTS
Rule: Always use admin layer (src/components/admin/) for forms.
// ✅ React Admin components
import { TextInput, SelectInput } from 'react-admin'
// ❌ Raw HTML
<input type="text" />
7. COLORS - SEMANTIC VARIABLES ONLY
Rule: Use semantic CSS variables, never hex codes or direct OKLCH.
/* ✅ Semantic tokens */
color: var(--primary);
background: var(--brand-700);
/* ❌ Hex codes */
color: #7CB342;
/* ❌ Direct OKLCH */
color: oklch(65% 0.15 125);
Note: See atomic-crm-ui-design skill for complete color system guidance.
8. MIGRATIONS - TIMESTAMP FORMAT
Rule: Use Supabase CLI to generate correctly timestamped migrations.
# ✅ Correct
npx supabase migration new add_contact_tags
# Generates: 20250126143000_add_contact_tags.sql
# ❌ Don't manually create
# 001_add_contact_tags.sql
Common Mistakes
| Mistake | Fix |
|---|---|
| "Add retry logic for production" | NO. Fail fast. Pre-launch = velocity over resilience. |
| "Circuit breaker for resilience" | NO. Let errors throw. Investigate and fix at source. |
| Validation in component/utils | Move to Zod schema in src/atomic-crm/validation/ |
| Hardcoded form defaults | Use zodSchema.partial().parse({}) |
type for object shapes |
Use interface for objects |
Raw <input> elements |
Use React Admin's <TextInput> |
| Leaving unused imports | Fix when editing file (Boy Scout Rule) |
Implementation Workflow
1. Check Context
- Pre-launch = velocity over resilience
- Fail fast over graceful degradation
- Simple over clever
2. Verify Patterns
- Error handling: Let it throw
- Validation: Zod at API boundary only
- Forms: Defaults from schema
- Types:
interfacefor objects
3. Boy Scout Rule
- Fix issues in files you edit
- Don't go on tangential refactors
4. Commit
- Verify no retry/circuit breaker code
- Verify no validation outside Zod
- Verify form defaults from schema
Red Flags - STOP and Review
If you find yourself:
- Writing retry logic → Delete it, let errors throw
- Adding circuit breaker → Delete it, fail fast
- Creating "resilient" error handling → Pre-launch doesn't need it
- Validating outside Zod schemas → Move to API boundary
- Hardcoding form defaults → Use schema.partial().parse({})
- Using
<input>directly → Use React Admin components - Ignoring nearby issues → Fix them (Boy Scout Rule)
All of these mean: Review Engineering Constitution before proceeding.
Real-World Impact
Following Constitution:
- Fast feature velocity (no over-engineering)
- Loud failures = quick fixes
- Consistent validation (single source)
- No drift between UI and validation
- Clean codebase (Boy Scout Rule)
Violating Constitution:
- 3,000+ lines of retry/circuit breaker code
- Hidden failures (silent degradation)
- Validation drift (multiple sources)
- Form defaults out of sync
- Technical debt accumulation
Constitution Principles Summary
- NO OVER-ENGINEERING - Fail fast, no retry/circuit breakers
- SINGLE SOURCE OF TRUTH - Zod validation at API boundary only
- BOY SCOUT RULE - Fix issues in files you edit
- VALIDATION - API boundary only (
src/atomic-crm/validation/) - FORM STATE - Derived from Zod schema (
.partial().parse({})) - TYPESCRIPT -
interfacefor objects,typefor unions - FORMS - React Admin components only
- COLORS - Semantic CSS variables only
- MIGRATIONS - Timestamp format via Supabase CLI
Full details: docs/claude/engineering-constitution.md