ワンクリックで
data-validation
>-
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
>-
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
| id | data-validation |
| name | data-validation |
| type | skill |
| version | 1.0.0 |
| created | 20/03/2026 |
| modified | 20/03/2026 |
| status | active |
| metadata | {"author":"NodeJS-Starter-V1","version":"1.0.0","locale":"en-AU"} |
| description | >- |
| context | fork |
Validation patterns ensuring all data entering the system is validated at boundaries: user input via Zod (frontend), API requests via Pydantic (backend). No unvalidated data crosses a trust boundary.
Defines Zod and Pydantic validation patterns for all data entering the system at trust boundaries. Covers form validation, API request schemas, type-safe contracts, Australian-specific validators (ABN, phone, postcode), and schema composition strategies.
error-taxonomy instead)[User Input] --Zod--> [Frontend] --fetch--> [API] --Pydantic--> [Service Layer]
^ ^
| |
VALIDATE HERE VALIDATE HERE
| Layer | Convention | Example |
|---|---|---|
| Frontend Zod schema | camelCase + Schema suffix | loginFormSchema |
| Frontend inferred type | PascalCase via z.infer | type LoginForm = z.infer<typeof loginFormSchema> |
| Backend Pydantic model | PascalCase + purpose suffix | DocumentCreateRequest |
| Shared contract | Same field names both sides | email, password, title |
The project already uses this pattern in apps/web/components/auth/login-form.tsx:
import * as z from 'zod';
import { zodResolver } from '@hookform/resolvers/zod';
import { useForm } from 'react-hook-form';
// 1. Define schema
const loginFormSchema = z.object({
email: z.string().email('Please enter a valid email address'),
password: z.string().min(6, 'Password must be at least 6 characters'),
});
// 2. Infer type (never define manually)
type LoginForm = z.infer<typeof loginFormSchema>;
// 3. Use with react-hook-form
const form = useForm<LoginForm>({
resolver: zodResolver(loginFormSchema),
defaultValues: { email: '', password: '' },
});
Build complex schemas from reusable parts:
// Base schemas (reusable)
const emailSchema = z.string().email('Please enter a valid email address');
const passwordSchema = z.string().min(6, 'Password must be at least 6 characters');
const abnSchema = z.string().regex(/^\d{11}$/, 'ABN must be 11 digits');
// Composed schemas
const registerFormSchema = z
.object({
email: emailSchema,
password: passwordSchema,
confirmPassword: z.string(),
})
.refine((data) => data.password === data.confirmPassword, {
message: 'Passwords do not match',
path: ['confirmPassword'],
});
Validate data before sending to the backend:
const documentCreateSchema = z.object({
title: z.string().min(1, 'Title is required').max(255),
content: z.string().min(1, 'Content is required'),
metadata: z.record(z.unknown()).optional(),
});
async function createDocument(input: unknown): Promise<Document> {
// Validate before sending — fail fast on the client
const validated = documentCreateSchema.parse(input);
return apiClient.post('/api/documents', validated);
}
// Australian Business Number (ABN) with checksum
const abnSchema = z.string().refine(
(val) => {
if (!/^\d{11}$/.test(val)) return false;
const weights = [10, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19];
const digits = val.split('').map(Number);
digits[0] -= 1;
const sum = digits.reduce((acc, d, i) => acc + d * weights[i], 0);
return sum % 89 === 0;
},
{ message: 'Invalid ABN' }
);
// Australian phone number
const auPhoneSchema = z.string().regex(
/^(\+61|0)[2-478]\d{8}$/,
'Please enter a valid Australian phone number'
);
// Australian postcode
const postcodeSchema = z.string().regex(/^\d{4}$/, 'Postcode must be 4 digits');
// Date in DD/MM/YYYY format
const auDateSchema = z.string().regex(
/^\d{2}\/\d{2}\/\d{4}$/,
'Date must be in DD/MM/YYYY format'
);
The project defines request models in route files or apps/backend/src/api/schemas/:
from pydantic import BaseModel, Field, field_validator
from typing import Optional
class DocumentCreateRequest(BaseModel):
"""Validated input for document creation."""
title: str = Field(
...,
min_length=1,
max_length=255,
description="Document title"
)
content: str = Field(
...,
min_length=1,
description="Document content"
)
metadata: Optional[dict] = Field(
None,
description="Additional metadata"
)
@field_validator("title")
@classmethod
def strip_title(cls, v: str) -> str:
return v.strip()
Use Pydantic's built-in constraints instead of custom validators where possible:
from pydantic import BaseModel, Field, EmailStr
from typing import Annotated
# Constrained types (prefer over custom validators)
PositiveInt = Annotated[int, Field(gt=0)]
PageSize = Annotated[int, Field(ge=1, le=100)]
ShortString = Annotated[str, Field(min_length=1, max_length=255)]
class PaginationParams(BaseModel):
"""Reusable pagination parameters."""
page: PositiveInt = 1
page_size: PageSize = 20
class SearchRequest(BaseModel):
"""Validated search input."""
query: ShortString
pagination: PaginationParams = PaginationParams()
from fastapi import APIRouter
router = APIRouter(prefix="/api/documents", tags=["documents"])
@router.post("/", status_code=201)
async def create_document(
request: DocumentCreateRequest, # Pydantic validates automatically
user: User = Depends(get_current_user),
) -> DocumentResponse:
"""Create a new document. Input is validated by Pydantic."""
# request is already validated — trust it here
return await document_service.create(request, user)
| Schema Type | Location | When to Use |
|---|---|---|
| Route-specific | Inline in route file | Simple schemas used by one endpoint |
| Shared across routes | apps/backend/src/api/schemas/ | Schemas used by multiple endpoints |
| Domain models | apps/backend/src/models/ | Core business models (not request schemas) |
When adding a new feature, verify:
zodResolverz.infer, not manually defined| Pattern | Problem | Correct Approach |
|---|---|---|
| Validation logic inside route handlers | Mixes concerns, hard to reuse or test | Define Zod/Pydantic schemas separately and reference them |
| No error messages on validation failure | Users see raw Pydantic/Zod errors | Provide human-readable messages in en-AU on every field |
Type coercion without validation (Number(input)) | NaN and unexpected values slip through | Use Zod .coerce.number() or Pydantic field_validator |
| Optional fields without defaults | Downstream code must null-check everywhere | Set sensible defaults via Field(default=...) or .default() |
| Manually defining types instead of inferring | Types drift out of sync with schemas | Use z.infer<typeof schema> and Pydantic model exports |
[AGENT_ACTIVATED]: Data Validation
[PHASE]: {Schema Design | Implementation | Review}
[STATUS]: {in_progress | complete}
{validation analysis or schema output}
[NEXT_ACTION]: {what to do next}
Validation failures should use DATA_VALIDATION_* error codes from the error-taxonomy skill:
# Backend: structured validation error
raise HTTPException(
status_code=422,
detail={
"detail": "Email format is invalid",
"error_code": "DATA_VALIDATION_INVALID_FORMAT",
"field": "email",
},
)
min_length, gt) over custom validatorsWhen api-contract skill is installed, Zod and Pydantic schemas should mirror each other to form a typed contract between frontend and backend.
+61 or 0X XXXX XXXXRoute complex requests to the right specialist agent or chain of agents. This skill acts as the central brain of an agent swarm — it analyses what the user needs, determines which specialist domain(s) are required, and coordinates parallel or sequential agent execution. Use this skill when a request spans multiple domains (e.g., "research competitors and create a pitch deck"), when you need to decide which specialist should handle an ambiguous request, or when a task requires a multi-step pipeline across different skills. Triggers on: multi-step requests, cross-domain tasks, "coordinate", "plan this out", "I need help with multiple things", or any complex request that touches more than one specialist area. Also triggers when the user seems unsure which tool or approach to use.
>-
Hybrid DAG execution primitive combining deterministic and agentic nodes with hard iteration caps
">"
Act as a brand ambassador — create authentic, platform-specific social media content that embodies a brand's identity and connects with audiences. Use this skill whenever the user asks to "create social media content for a brand", "act as a brand ambassador", "write ambassador posts", "promote [brand] on social media", "create influencer-style content", "write authentic brand content", "social media ambassador", or any request involving representing a brand through social content. Also triggers on "ambassador voice", "brand promotion posts", "influencer content", "authentic brand posts", "UGC-style content", or when someone wants social media content that sounds like a real person recommending a brand rather than corporate marketing copy. Even if the user just says "help me promote [brand]" or "I need content for [brand]'s social channels" — use this skill.
>-