スキル一覧に戻る
krwhynot

enforcing-principles

by krwhynot

1🍴 0📅 2026年1月21日
GitHubで見るManusで実行

SKILL.md


name: enforcing-principles description: Use when implementing features, handling errors, adding validation, creating forms, writing database migrations. Enforces fail-fast (NO retry logic, circuit breakers), single entry point (unified data provider, Zod at API boundary), form defaults from schema (zodSchema.partial().parse), TypeScript (interface vs type), React Admin patterns. NEW (2024-12): Zod security (z.strictObject, string .max() limits, z.coerce for forms, z.enum allowlist), form performance (onSubmit/onBlur mode, useWatch not watch). BLOCKS anti-patterns, SUGGESTS best practices.

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 composable entry point (unified data provider delegating to resource modules, Zod validation at API boundary).

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.md if 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 interface for objects, type for 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 COMPOSABLE ENTRY POINT

Rule: Have a single, composable entry point for data access (unifiedDataProvider), delegating to resource-specific modules. Validation via Zod schemas at API boundary (src/atomic-crm/validation/) ONLY.

❌ WRONG - Multiple Competing Entry Points:

// ❌ Direct API calls bypassing provider
import { contactsApi } from './contactsApi'
await contactsApi.updateContact(id, data)

// ❌ Validation in component
const isValidEmail = (email: string) => /@/.test(email)

// ❌ Which is authoritative? Now two+ definitions can diverge!

✅ CORRECT - Unified Entry Point with Delegation:

// Always use the unified provider
import { dataProvider } from '@/atomic-crm/providers/supabase/unifiedDataProvider'
await dataProvider.update('contacts', { id, data })
// Provider internally delegates to contacts-specific logic

// Validation: Zod at API boundary only
import { contactSchema } from '@/atomic-crm/validation/contacts'

Key Insight: This is the Composite pattern - one facade for the app to talk to, with resource-specific modules handling implementation details.

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.

3b. ZOD SECURITY PATTERNS (OWASP Compliant)

Rule: All Zod schemas at API boundary must follow security best practices.

String Length Limits (Prevent DoS):

// ✅ CORRECT: Always set max length
const schema = z.object({
  name: z.string().max(100),           // Names
  title: z.string().max(200),          // Titles/labels
  description: z.string().max(2000),   // Long text
  url: z.string().url().max(2048),     // URLs (browser limit)
});

// ❌ WRONG: No length limit = DoS vulnerability
const schema = z.object({
  name: z.string(), // Attacker can send 10MB string
});

Strict Objects at API Boundary (Prevent Mass Assignment):

// ✅ CORRECT: Reject unknown keys at API boundary
export const createContactSchema = z.strictObject({
  name: z.string().max(100),
  email: z.string().email(),
}); // Unknown keys throw error

// ⚠️ INTERNAL ONLY: z.object() for composition/partial updates
const contactBaseSchema = z.object({
  name: z.string().max(100),
}); // Can extend, allows unknown keys

Coercion for Form Inputs:

// ✅ CORRECT: HTML inputs return strings, coerce to type
const schema = z.object({
  age: z.coerce.number().min(0).max(150),
  price: z.coerce.number().positive(),
  isActive: z.coerce.boolean(),
  dueDate: z.coerce.date(),
});

// ❌ WRONG: Will fail on form input "42" (string, not number)
const schema = z.object({
  age: z.number(), // z.number().parse("42") throws!
});

Allowlist Validation (OWASP):

// ✅ CORRECT: Allowlist with z.enum()
const stageSchema = z.enum(['new_lead', 'closed_won', 'closed_lost']);
const roleSchema = z.enum(['admin', 'manager', 'rep']);

// ❌ WRONG: Denylist (easily bypassed)
const badSchema = z.string().refine(
  (val) => !val.includes('<script>'), // Attacker uses <SCRIPT>
  'Invalid input'
);

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

9. TAILWIND V4 CSS PATTERNS

9a. No @apply Self-Reference

Rule: Custom utilities in @layer utilities cannot @apply other custom utilities from the same layer.

Why: Tailwind v4 JIT processes utilities independently; self-references create circular dependencies.

❌ WRONG — fails in Tailwind v4:

@layer utilities {
  .touch-target-44 { /* ... */ }
  .data-cell {
    @apply touch-target-44; /* ERROR: Cannot resolve */
  }
}

✅ CORRECT — inline the styles:

@layer utilities {
  .data-cell {
    position: relative;
    /* Inline the touch-target styles directly */
  }
  .data-cell::before {
    content: '';
    position: absolute;
    top: calc((44px - 100%) / -2);
    /* ... */
  }
}

9b. Touch Expansion via calc()

Rule: Use calc((TARGET - 100%) / -2) to center-expand touch targets.

Why: 100% references parent height; dividing by -2 distributes expansion equally top/bottom.

Pattern:

.touch-target-44::before {
  top: calc((44px - 100%) / -2);    /* Expand upward */
  bottom: calc((44px - 100%) / -2); /* Expand downward */
}
/* Result: 32px element → 44px touch area (6px each direction) */

9c. CSS Custom Properties for Typography Tokens

Rule: Define font tokens as CSS custom properties, not Tailwind theme extensions.

Why: Tailwind v4 JIT only bundles used utilities; custom properties are always available.

Pattern:

:root {
  --text-table: 0.8125rem;
  --text-table--line-height: 1.35;
}
/* Usage: text-[length:var(--text-table)] leading-[var(--text-table--line-height)] */

Common Mistakes

MistakeFix
"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/utilsMove to Zod schema in src/atomic-crm/validation/
Hardcoded form defaultsUse zodSchema.partial().parse({})
type for object shapesUse interface for objects
Raw <input> elementsUse React Admin's <TextInput>
Leaving unused importsFix 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: interface for 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 data access (single composable entry point)
  • 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)
  • Multiple competing data access paths (bypassing unified provider)
  • Form defaults out of sync
  • Technical debt accumulation

Quick Reference Cards

Error Handling

SituationDODON'T
Database errorLet it throwRetry logic
Bulk operationsPromise.allSettled()Promise.all()
Validation errorFormat for React AdminSilent failure
User notificationShow specific messageGeneric "Error"

Validation

SituationDODON'T
Where to validatesrc/atomic-crm/validation/Component/utils
Business defaults.default() in ZodHardcode in component
Type inferencez.infer<typeof schema>Manual type definition
Error format{ message, errors: {} }Throw raw Zod error
String fields.max(100) on ALL stringsNo length limit
API boundary objectsz.strictObject()z.object() (allows unknown keys)
Form number inputsz.coerce.number()z.number() (fails on string "42")
Form date inputsz.coerce.date()z.date() (fails on ISO strings)
Constrained valuesz.enum(['a','b']) (allowlist)Regex denylist

Form State

SituationDODON'T
Form defaultsschema.partial().parse({})Hardcode in component
Array defaultsSub-schema with .default()defaultValue prop
JSONB arraysArrayInput + SimpleFormIteratorManual array state
Submit transformtransform prop on CreateBaseTransform in component
Validation modemode: 'onSubmit' or 'onBlur'mode: 'onChange' (perf issue)
Watch valuesuseWatch({ control, name })watch() (re-renders whole form)
RHF + resolverzodResolver(schema) ONLYMix resolver + register validation

Database

SituationDODON'T
New tableGRANT + RLSRLS only
Role checkHelper function (is_admin())Inline check
Enum valuesAdd only (can't remove)Try to remove
Migrationsnpx supabase migration newManual numbering

CSS/Tailwind v4

SituationDODON'T
Custom utility needs other utilityInline the styles directly@apply other-utility
Touch target expansioncalc((44px - 100%) / -2)Fixed pixel offsets
Typography tokensCSS custom propertiesTailwind theme extension
Arbitrary valuestext-[length:var(--token)]Hardcoded sizes

Decision Tree: When This Skill Triggers

Starting implementation task?
│
├─ Handling errors?
│  └─ error-handling-basics.md → Fail fast (no retry)
│     error-handling-bulk.md → Promise.allSettled for bulk
│     error-handling-validation.md → Structured logging
│
├─ Adding validation?
│  └─ validation-basics.md → Zod at API boundary, core patterns
│     validation-arrays.md → JSONB arrays, sub-schemas
│     validation-schemas.md → Create/Update schemas
│     validation-advanced.md → Custom validators, transform
│     anti-patterns-validation.md → Avoid multiple sources
│
├─ Creating forms?
│  └─ form-defaults.md → Defaults from schema
│     form-arrays.md → JSONB array inputs
│     form-patterns.md → Tabbed forms, submission
│     form-advanced.md → Reset, debugging, conditional
│     anti-patterns-validation.md → Avoid hardcoded defaults
│
├─ Database changes?
│  └─ database-security.md → GRANT + RLS (two-layer)
│     database-migrations.md → Migration structure, enums
│     database-roles.md → Role-based permissions
│     database-advanced.md → Triggers, JSONB, views
│     database-reference.md → Decision tree, best practices
│     anti-patterns-database.md → Avoid common DB mistakes
│
├─ Security concerns?
│  └─ security-csv.md → CSV upload, formula injection
│     security-sql.md → SQL injection prevention
│     security-rls.md → RLS policies, authentication
│     security-xss.md → XSS prevention, URL validation
│
├─ Writing tests?
│  └─ testing-unit.md → Vitest, validation, components
│     testing-e2e.md → Playwright, critical journeys
│     testing-reference.md → Coverage, organization
│     anti-patterns-testing.md → Avoid testing implementation
│
└─ Unsure what to do?
   └─ anti-patterns-engineering.md → Avoid over-engineering
      error-handling-reference.md → Decision tree & rationalizations

Resource Files

Comprehensive patterns with real code examples from Atomic CRM:

Error Handling

Validation (Split for Focus)

Form State Management (Split for Focus)

Database (Split for Focus)

Security (Split for Focus)

Testing (Split for Focus)

Anti-Patterns (Split by Domain)

Constitution Principles Summary

  1. NO OVER-ENGINEERING - Fail fast, no retry/circuit breakers
  2. SINGLE COMPOSABLE ENTRY POINT - Unified data provider delegating to resource modules, Zod at API boundary
  3. BOY SCOUT RULE - Fix issues in files you edit
  4. VALIDATION - API boundary only (src/atomic-crm/validation/)
  5. FORM STATE - Derived from Zod schema (.partial().parse({}))
  6. TYPESCRIPT - interface for objects, type for unions
  7. FORMS - React Admin components only
  8. COLORS - Semantic CSS variables only
  9. MIGRATIONS - Timestamp format via Supabase CLI

Zod Validation Rules (NEW - OWASP Compliant)

  1. STRING LIMITS - All strings must have .max() constraint (100/200/2000 chars)
  2. STRICT OBJECTS - Use z.strictObject() at API boundary (prevents mass assignment)
  3. COERCION - Use z.coerce for all non-string form inputs (number, date, boolean)
  4. ALLOWLIST - Use z.enum() for constrained values (never denylist regex)

Form Performance Rules (NEW)

  1. VALIDATION MODE - Use onSubmit or onBlur mode (never onChange)
  2. WATCH ISOLATION - Use useWatch() for subscriptions (not watch())
  3. RESOLVER ONLY - Use zodResolver(schema) exclusively (don't mix with register validation)

Tailwind v4 CSS Rules (NEW)

  1. NO @APPLY SELF-REFERENCE - Custom utilities cannot @apply other custom utilities from same layer
  2. TOUCH EXPANSION VIA CALC() - Use calc((44px - 100%) / -2) for centered touch expansion
  3. CSS CUSTOM PROPERTIES - Define typography tokens as CSS vars, not Tailwind theme extensions

Full details: docs/claude/engineering-constitution.md

Cross-Reference: See atomic-crm-ui-design skill for UI design patterns (colors, spacing, accessibility)

スコア

総合スコア

50/100

リポジトリの品質指標に基づく評価

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

0/10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

3ヶ月以内に更新がある

0/10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

0/5

レビュー

💬

レビュー機能は近日公開予定です