diff --git a/.agents/skills/backend-agent/SKILL.md b/.agents/skills/backend-agent/SKILL.md new file mode 100644 index 0000000..75c4c2f --- /dev/null +++ b/.agents/skills/backend-agent/SKILL.md @@ -0,0 +1,113 @@ +--- +name: backend-agent +description: Backend specialist for APIs, databases, authentication using FastAPI with clean architecture (Repository/Service/Router pattern). Use for API, endpoint, REST, database, server, migration, and auth work. +--- + +# Backend Agent - API & Server Specialist + +## When to use +- Building REST APIs or GraphQL endpoints +- Database design and migrations +- Authentication and authorization +- Server-side business logic +- Background jobs and queues + +## When NOT to use +- Frontend UI -> use Frontend Agent +- Mobile-specific code -> use Mobile Agent + +## Core Rules + +1. **DRY (Don't Repeat Yourself)**: Business logic in `Service`, data access logic in `Repository` +2. **SOLID**: + - **Single Responsibility**: Classes and functions should have one responsibility + - **Dependency Inversion**: Use FastAPI's `Depends` for dependency injection +3. **KISS**: Keep it simple and clear + +## Architecture Pattern + +``` +Router (HTTP) → Service (Business Logic) → Repository (Data Access) → Models +``` + +### Repository Layer +- **File**: `src/[domain]/repository.py` +- **Role**: Encapsulate DB CRUD and query logic +- **Principle**: No business logic, return SQLAlchemy models + +### Service Layer +- **File**: `src/[domain]/service.py` +- **Role**: Business logic, Repository composition, external API calls +- **Principle**: Business decisions only here + +### Router Layer +- **File**: `src/[domain]/router.py` +- **Role**: Receive HTTP requests, input validation, call Service, return response +- **Principle**: No business logic, inject Service via DI + +## Core Rules + +1. **Clean architecture**: router → service → repository → models +2. **No business logic in route handlers** +3. **All inputs validated with Pydantic** +4. **Parameterized queries only** (never string interpolation) +5. **JWT + bcrypt for auth**; rate limit auth endpoints +6. **Async/await consistently**; type hints on all signatures +7. **Custom exceptions** via `src/lib/exceptions.py` (not raw HTTPException) +8. **Explicit ORM loading strategy**: do not rely on default relation loading when query shape matters +9. **Explicit transaction boundaries**: group one business operation into one request/service-scoped unit of work +10. **Safe ORM lifecycle**: do not share mutable ORM session/entity manager/client objects across concurrent work unless the ORM explicitly supports it + +## Dependency Injection + +```python +# src/recipes/routers/dependencies.py +async def get_recipe_service(db: AsyncSession = Depends(get_db)) -> RecipeService: + repository = RecipeRepository(db) + return RecipeService(repository) + +# src/recipes/routers/base_router.py +@router.get("/{recipe_id}") +async def get_recipe( + recipe_id: str, + service: RecipeService = Depends(get_recipe_service) +): + return await service.get_recipe(recipe_id) +``` + +## Code Quality + +- **Python 3.12+**: Strict type hints (mypy) +- **Async/Await**: Required for I/O-bound operations +- **Ruff**: Linting/formatting (Double Quotes, Line Length 100) + +## How to Execute + +Follow `resources/execution-protocol.md` step by step. +See `resources/examples.md` for input/output examples. +Use `resources/orm-reference.md` when the task involves ORM query performance, relationship loading, transactions, session/client lifecycle, or N+1 analysis. +Before submitting, run `resources/checklist.md`. + +## Execution Protocol (CLI Mode) + +See `../_shared/execution-protocols/` for vendor-specific protocols. +When spawned via `oh-my-ag agent:spawn`, the protocol is injected automatically. + +## References + +- Execution steps: `resources/execution-protocol.md` +- Code examples: `resources/examples.md` +- Code snippets: `resources/snippets.md` +- Checklist: `resources/checklist.md` +- ORM reference: `resources/orm-reference.md` +- Error recovery: `resources/error-playbook.md` +- Tech stack: `resources/tech-stack.md` +- API template: `resources/api-template.py` +- Context loading: `../_shared/context-loading.md` +- Reasoning templates: `../_shared/reasoning-templates.md` +- Clarification: `../_shared/clarification-protocol.md` +- Context budget: `../_shared/context-budget.md` +- Lessons learned: `../_shared/lessons-learned.md` + +> [!IMPORTANT] +> When adding new modules, always include `__init__.py` to maintain package structure diff --git a/.agents/skills/backend-agent/resources/api-template.py b/.agents/skills/backend-agent/resources/api-template.py new file mode 100644 index 0000000..47847ea --- /dev/null +++ b/.agents/skills/backend-agent/resources/api-template.py @@ -0,0 +1,326 @@ +""" +API Endpoint Template for Backend Agent + +This template demonstrates best practices for FastAPI endpoints. +""" + +from fastapi import APIRouter, Depends, HTTPException, status, Query +from sqlalchemy.orm import Session +from typing import Annotated, List +from uuid import UUID + +from app.database import get_db +from app.auth import get_current_user +from app.models import User, Resource +from app.schemas import ResourceCreate, ResourceUpdate, ResourceResponse +from app.services import ResourceService + +# Type aliases for cleaner code +DatabaseDep = Annotated[Session, Depends(get_db)] +UserDep = Annotated[User, Depends(get_current_user)] + +# Router setup +router = APIRouter( + prefix="/api/resources", + tags=["resources"], + responses={404: {"description": "Not found"}}, +) + + +# List endpoint with pagination and filtering +@router.get( + "/", + response_model=List[ResourceResponse], + summary="List resources", + description="Retrieve a paginated list of resources with optional filtering" +) +async def list_resources( + db: DatabaseDep, + current_user: UserDep, + skip: int = Query(0, ge=0, description="Number of records to skip"), + limit: int = Query(100, ge=1, le=1000, description="Max records to return"), + search: str | None = Query(None, description="Search query"), + status: str | None = Query(None, description="Filter by status"), +): + """ + List resources with pagination. + + - **skip**: Offset for pagination + - **limit**: Maximum number of records + - **search**: Optional search term + - **status**: Optional status filter + """ + service = ResourceService(db) + resources = service.list_resources( + user_id=current_user.id, + skip=skip, + limit=limit, + search=search, + status=status, + ) + return resources + + +# Get single resource +@router.get( + "/{resource_id}", + response_model=ResourceResponse, + summary="Get resource", + responses={ + 200: {"description": "Resource found"}, + 404: {"description": "Resource not found"}, + 403: {"description": "Access denied"} + } +) +async def get_resource( + resource_id: UUID, + db: DatabaseDep, + current_user: UserDep, +): + """ + Get a specific resource by ID. + + Raises: + 404: Resource not found + 403: User doesn't own this resource + """ + service = ResourceService(db) + resource = service.get_resource(resource_id) + + if not resource: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=f"Resource {resource_id} not found" + ) + + # Authorization check + if resource.user_id != current_user.id: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="Access denied" + ) + + return resource + + +# Create resource +@router.post( + "/", + response_model=ResourceResponse, + status_code=status.HTTP_201_CREATED, + summary="Create resource", +) +async def create_resource( + resource_data: ResourceCreate, + db: DatabaseDep, + current_user: UserDep, +): + """ + Create a new resource. + + - **name**: Resource name (required) + - **description**: Optional description + - **status**: Initial status (default: active) + """ + service = ResourceService(db) + + try: + resource = service.create_resource( + user_id=current_user.id, + data=resource_data + ) + return resource + except ValueError as e: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=str(e) + ) + + +# Update resource +@router.patch( + "/{resource_id}", + response_model=ResourceResponse, + summary="Update resource", +) +async def update_resource( + resource_id: UUID, + resource_data: ResourceUpdate, + db: DatabaseDep, + current_user: UserDep, +): + """ + Update an existing resource (partial update). + + Only provided fields will be updated. + """ + service = ResourceService(db) + resource = service.get_resource(resource_id) + + if not resource: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=f"Resource {resource_id} not found" + ) + + if resource.user_id != current_user.id: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="Access denied" + ) + + try: + updated_resource = service.update_resource(resource, resource_data) + return updated_resource + except ValueError as e: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=str(e) + ) + + +# Delete resource +@router.delete( + "/{resource_id}", + status_code=status.HTTP_204_NO_CONTENT, + summary="Delete resource", +) +async def delete_resource( + resource_id: UUID, + db: DatabaseDep, + current_user: UserDep, + hard: bool = Query(False, description="Perform hard delete instead of soft delete"), +): + """ + Delete a resource. + + - **hard=false**: Soft delete (default) - sets deleted_at timestamp + - **hard=true**: Hard delete - permanently removes from database + """ + service = ResourceService(db) + resource = service.get_resource(resource_id) + + if not resource: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=f"Resource {resource_id} not found" + ) + + if resource.user_id != current_user.id: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="Access denied" + ) + + service.delete_resource(resource, hard=hard) + # 204 No Content - no response body + + +# Bulk operations example +@router.post( + "/bulk", + response_model=List[ResourceResponse], + status_code=status.HTTP_201_CREATED, + summary="Bulk create resources", +) +async def bulk_create_resources( + resources_data: List[ResourceCreate], + db: DatabaseDep, + current_user: UserDep, +): + """ + Create multiple resources in one request. + + Useful for batch imports. + """ + if len(resources_data) > 100: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="Maximum 100 resources per bulk operation" + ) + + service = ResourceService(db) + created_resources = [] + + try: + for data in resources_data: + resource = service.create_resource( + user_id=current_user.id, + data=data + ) + created_resources.append(resource) + + return created_resources + except ValueError as e: + db.rollback() # Rollback on error + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=str(e) + ) + + +# Service class template (separate file: app/services/resource_service.py) +""" +from sqlalchemy.orm import Session +from app.models import Resource +from app.schemas import ResourceCreate, ResourceUpdate +from datetime import datetime +from uuid import UUID + +class ResourceService: + def __init__(self, db: Session): + self.db = db + + def list_resources( + self, + user_id: UUID, + skip: int = 0, + limit: int = 100, + search: str | None = None, + status: str | None = None, + ): + query = self.db.query(Resource).filter( + Resource.user_id == user_id, + Resource.deleted_at.is_(None) + ) + + if search: + query = query.filter(Resource.name.ilike(f"%{search}%")) + + if status: + query = query.filter(Resource.status == status) + + return query.offset(skip).limit(limit).all() + + def get_resource(self, resource_id: UUID) -> Resource | None: + return self.db.query(Resource).filter( + Resource.id == resource_id, + Resource.deleted_at.is_(None) + ).first() + + def create_resource(self, user_id: UUID, data: ResourceCreate) -> Resource: + resource = Resource( + **data.model_dump(), + user_id=user_id + ) + self.db.add(resource) + self.db.commit() + self.db.refresh(resource) + return resource + + def update_resource(self, resource: Resource, data: ResourceUpdate) -> Resource: + for field, value in data.model_dump(exclude_unset=True).items(): + setattr(resource, field, value) + + self.db.commit() + self.db.refresh(resource) + return resource + + def delete_resource(self, resource: Resource, hard: bool = False): + if hard: + self.db.delete(resource) + else: + resource.deleted_at = datetime.utcnow() + + self.db.commit() +""" diff --git a/.agents/skills/backend-agent/resources/checklist.md b/.agents/skills/backend-agent/resources/checklist.md new file mode 100644 index 0000000..b1b7575 --- /dev/null +++ b/.agents/skills/backend-agent/resources/checklist.md @@ -0,0 +1,39 @@ +# Backend Agent - Self-Verification Checklist + +Run through every item before submitting your work. + +## API Design +- [ ] RESTful conventions followed (proper HTTP methods, status codes) +- [ ] OpenAPI documentation complete (all endpoints documented) +- [ ] Request/response schemas defined with Pydantic +- [ ] Pagination for list endpoints returning > 20 items +- [ ] Consistent error response format + +## Database +- [ ] Migrations created (Alembic) and tested +- [ ] Indexes on foreign keys and frequently queried columns +- [ ] No N+1 queries; relation loading strategy chosen explicitly for the ORM in use +- [ ] No over-fetching; selected only required fields/columns/attributes +- [ ] Transactions used for multi-step operations with explicit unit-of-work boundaries +- [ ] ORM session/client/entity-manager lifecycle matches the framework's concurrency model +- [ ] Query risks reviewed: missing indexes, full scans, repeated identical queries, join row multiplication + +## Security +- [ ] JWT authentication on protected endpoints +- [ ] Password hashing with bcrypt (cost 10-12) +- [ ] Rate limiting on auth endpoints +- [ ] Input validation with Pydantic (no raw user input in queries) +- [ ] SQL injection protected (ORM or parameterized queries) +- [ ] No secrets in code or logs + +## Testing +- [ ] Unit tests for service layer logic +- [ ] Integration tests for all endpoints (happy + error paths) +- [ ] Auth scenarios tested (missing token, expired, wrong role) +- [ ] Test coverage > 80% + +## Code Quality +- [ ] Clean architecture layers: router -> service -> repository +- [ ] No business logic in route handlers +- [ ] Async/await used consistently +- [ ] Type hints on all function signatures diff --git a/.agents/skills/backend-agent/resources/error-playbook.md b/.agents/skills/backend-agent/resources/error-playbook.md new file mode 100644 index 0000000..6537ddc --- /dev/null +++ b/.agents/skills/backend-agent/resources/error-playbook.md @@ -0,0 +1,98 @@ +# Backend Agent - Error Recovery Playbook + +When you encounter a failure, find the matching scenario and follow the recovery steps. +Do NOT stop or ask for help until you have exhausted the playbook. + +--- + +## Import / Module Not Found + +**Symptoms**: `ModuleNotFoundError`, `ImportError`, `No module named X` + +1. Check the import path — typo? wrong package name? +2. Verify the dependency exists in `pyproject.toml` or `requirements.txt` +3. If missing: note it in your result as "requires `pip install X`" — do NOT install yourself +4. If it's a local module: check the directory structure with `get_symbols_overview` +5. If the path changed: use `search_for_pattern("class ClassName")` to find the new location + +--- + +## Test Failure + +**Symptoms**: `pytest` returns FAILED, assertion errors + +1. Read the full error output — which test, which assertion, expected vs actual +2. `find_symbol("test_function_name")` to read the test code +3. Determine: is the test wrong or is the implementation wrong? + - Test expects old behavior → update test + - Implementation has a bug → fix implementation +4. Re-run the specific test: `pytest path/to/test.py::test_name -v` +5. After fix, run full test suite to check for regressions +6. **After 3 failures**: Try a different approach. Record current attempt in progress and implement alternative + +--- + +## Database Migration Error + +**Symptoms**: `alembic upgrade head` fails, `IntegrityError`, duplicate column + +1. Read the error — is it a conflict with existing migration? +2. Check current DB state: `alembic current` +3. If migration conflicts: `alembic downgrade -1` then fix migration script +4. If schema mismatch: compare model with actual DB schema +5. **NEVER do this**: `alembic stamp head` (risk of data loss) + +--- + +## Authentication / JWT Error + +**Symptoms**: 401/403 responses, `InvalidTokenError`, `ExpiredSignatureError` + +1. Check: is the secret key consistent between encode and decode? +2. Check: is the algorithm specified (`HS256` vs `RS256`)? +3. Check: is the token being sent in the correct header format? (`Bearer {token}`) +4. Check: is token expiry set correctly? (access: 15min, refresh: 7day) +5. Test with a manually created token to isolate the issue + +--- + +## N+1 Query / Slow Response + +**Symptoms**: API response > 500ms, many similar SQL queries in logs + +1. Enable SQL logging: `echo=True` on engine +2. Count queries for a single request +3. If N+1: add `joinedload()` or `selectinload()` to the query +4. If slow single query: check indexes with `EXPLAIN ANALYZE` +5. If still slow: consider caching with Redis + +--- + +## Rate Limit / Quota Error (Gemini API) + +**Symptoms**: `429`, `RESOURCE_EXHAUSTED`, `rate limit exceeded` + +1. **Stop immediately** — do not make additional API calls +2. Save current work to `progress-{agent-id}.md` +3. Record Status: `quota_exceeded` in `result-{agent-id}.md` +4. Specify remaining tasks so orchestrator can retry later + +--- + +## Serena Memory Unavailable + +**Symptoms**: `write_memory` / `read_memory` failure, timeout + +1. Retry once (may be transient error) +2. If 2 consecutive failures: fall back to local files + - progress → write to `/tmp/progress-{agent-id}.md` + - result → write to `/tmp/result-{agent-id}.md` +3. Add `memory_fallback: true` flag to result + +--- + +## General Principles + +- **After 3 failures**: If same approach fails 3 times, must try a different method +- **Blocked**: If no progress after 5 turns, save current state and record `Status: blocked` in result +- **Out of scope**: If you find issues in another agent's domain, only record in result — do not modify directly diff --git a/.agents/skills/backend-agent/resources/examples.md b/.agents/skills/backend-agent/resources/examples.md new file mode 100644 index 0000000..61d6ca4 --- /dev/null +++ b/.agents/skills/backend-agent/resources/examples.md @@ -0,0 +1,85 @@ +# Backend Agent - Examples + +## Example 1: CRUD API Endpoint + +**Input**: "Create a TODO CRUD API with user ownership" + +**Output**: +``` +## Task: TODO CRUD API + +### Endpoints Implemented +- POST /api/todos (create) +- GET /api/todos (list, user-scoped) +- GET /api/todos/{id} (detail) +- PATCH /api/todos/{id} (update) +- DELETE /api/todos/{id} (delete) + +### Database Schema +- todos table: id (UUID), title (VARCHAR), completed (BOOL), user_id (FK), created_at +- Index on user_id + +### Security +- [x] All endpoints require JWT auth +- [x] User can only access own todos (user_id filter) +- [x] Input validated with Pydantic + +### Files Created +- app/models/todo.py +- app/schemas/todo.py +- app/services/todo_service.py +- app/api/todos.py +- tests/test_todos.py +- alembic/versions/xxx_add_todos.py +``` + +## Example 2: Authentication System + +**Input**: "Add JWT authentication with register and login" + +**Output**: +``` +## Task: JWT Authentication + +### Endpoints Implemented +- POST /api/auth/register (email + password) +- POST /api/auth/login (returns access + refresh tokens) +- POST /api/auth/refresh (refresh token rotation) + +### Security +- [x] Password hashed with bcrypt (cost 12) +- [x] Access token: 15min expiry +- [x] Refresh token: 7 day expiry, single-use rotation +- [x] Rate limiting: 5 attempts/min on login +- [x] Email validation with Pydantic EmailStr + +### Files Created +- app/models/user.py +- app/schemas/auth.py +- app/services/auth_service.py +- app/api/auth.py +- app/core/security.py (JWT helpers, password hashing) +- tests/test_auth.py +``` + +## Example 3: Fixing N+1 Query + +**Input**: "The /api/posts endpoint is slow, taking 2s for 50 posts" + +**Output**: +``` +## Task: Fix N+1 Query in Posts API + +### Root Cause +Each post triggers a separate query for its author (N+1 problem). + +### Fix +Added joinedload(Post.author) to the query. + +### Before: 51 queries (1 + 50) +### After: 1 query with JOIN + +### Files Modified +- app/api/posts.py (added eager loading) +- tests/test_posts.py (added performance assertion) +``` diff --git a/.agents/skills/backend-agent/resources/execution-protocol.md b/.agents/skills/backend-agent/resources/execution-protocol.md new file mode 100644 index 0000000..ea43bad --- /dev/null +++ b/.agents/skills/backend-agent/resources/execution-protocol.md @@ -0,0 +1,47 @@ +# Backend Agent - Execution Protocol + +## Step 0: Prepare +1. **Assess difficulty** — see `../_shared/difficulty-guide.md` + - **Simple**: Skip to Step 3 | **Medium**: All 4 steps | **Complex**: All steps + checkpoints +2. **Check lessons** — read your domain section in `../_shared/lessons-learned.md` +3. **Clarify requirements** — follow `../_shared/clarification-protocol.md` + - Check **Uncertainty Triggers**: business logic, security/auth, existing code conflicts? + - Determine level: LOW → proceed | MEDIUM → present options | HIGH → ask immediately +4. **Budget context** — follow `../_shared/context-budget.md` (read symbols, not whole files) + +**⚠️ Intelligent Escalation**: When uncertain, escalate early. Don't blindly proceed. + +Follow these steps in order (adjust depth by difficulty). + +## Step 1: Analyze +- Read the task requirements carefully +- Identify which endpoints, models, and services are needed +- Check existing code with Serena: `get_symbols_overview("app/api")`, `find_symbol("existing_function")` +- If the task is ORM-heavy, load `resources/orm-reference.md` before deciding on loading strategy, transaction scope, or client/session lifecycle +- List assumptions; ask if unclear + +## Step 2: Plan +- Decide on file structure: models, schemas, routes, services +- Define API contracts (method, path, request/response types) +- Plan database schema changes (tables, columns, indexes, migrations) +- Plan relation loading strategy, transaction boundary, and ORM lifecycle constraints explicitly +- Identify security requirements (auth, validation, rate limiting) + +## Step 3: Implement +- Create/modify files in this order: + 1. Database models + migrations + 2. Pydantic schemas (request/response) + 3. Service layer (business logic) + 4. API routes (thin, delegate to services) + 5. Tests (unit + integration) +- Use `resources/api-template.py` as reference +- Follow clean architecture: router -> service -> repository -> models + +## Step 4: Verify +- Run `resources/checklist.md` items +- Run `../_shared/common-checklist.md` items +- Ensure all tests pass +- Confirm OpenAPI docs are complete + +## On Error +See `resources/error-playbook.md` for recovery steps. diff --git a/.agents/skills/backend-agent/resources/orm-reference.md b/.agents/skills/backend-agent/resources/orm-reference.md new file mode 100644 index 0000000..58b8d03 --- /dev/null +++ b/.agents/skills/backend-agent/resources/orm-reference.md @@ -0,0 +1,78 @@ +# Backend Agent - ORM Reference + +Use this guide when the task involves ORM query design, relation loading, transaction scoping, or session/client lifecycle decisions. + +This document is intentionally synthesized from official ORM docs. The purpose is not to mirror each vendor page, but to extract the common operating rules that keep backend work correct and performant. + +## ORM Rules + +### 1. Relation loading must be chosen per use case + +Backend Agent should therefore: +- choose loading strategy per endpoint or service method +- compare join-based eager loading, batched/select-in loading, and separate follow-up queries +- treat accidental lazy loads in loops as a performance bug + +What this rule is derived from: +- Prisma documents N+1 mitigation through batched access patterns and join-based relation loading +- SQLAlchemy documents `joinedload`, `selectinload`, `lazyload`, and `raiseload` as explicit strategies +- TypeORM documents join-based loading and the lazy/eager trade-off +- Sequelize documents `include` as join-based eager loading and `separate: true` for `hasMany` +- Hibernate documents that select-based fetching is vulnerable to N+1 + +### 2. Transaction boundaries must follow business operations + +Backend Agent should therefore: +- open one explicit transaction for one business operation +- keep read-modify-write flows inside the same transaction boundary +- avoid per-statement auto-commit designs +- never hold a database transaction open across user think time + +What this rule is derived from: +- Hibernate explicitly calls session-per-operation and auto-commit-per-statement anti-patterns +- Sequelize promotes managed transactions around a callback-shaped unit of work +- SQLAlchemy models the session as transaction-oriented state + +### 3. ORM lifecycle objects are not generic shared singletons + +Backend Agent should therefore: +- reuse factory/client objects only when the ORM recommends reuse +- keep transaction-scoped mutable objects request-scoped or task-scoped +- never share stateful unit-of-work objects across concurrent tasks unless the vendor explicitly permits it + +Operational reading: +- Prisma: reuse one `PrismaClient` in long-running apps; do not create a new client per query; do not disconnect after every request +- SQLAlchemy: `Session` and `AsyncSession` are mutable, transaction-scoped, and the documented model is session-per-thread / async-session-per-task +- Hibernate: prefer session-per-request over session-per-operation + +### 4. Field projection is mandatory unless full entity hydration is required + +Backend Agent should therefore: +- fetch only required columns, attributes, or nested fields +- prefer projections, DTO shaping, or raw results when domain objects are not needed +- treat over-fetching as a query design flaw, not a minor optimization item + +What this rule is derived from: +- Prisma calls out over-fetching directly and supports nested `select` +- TypeORM recommends `select` and `getRawMany()` +- Sequelize documents `attributes` and `{ raw: true }` + +### 5. Mapping defaults must never be trusted blindly + +Lazy and eager defaults are convenience features, not query plans. Multiple vendors warn that both directions can fail differently: +- lazy defaults can explode into N+1 +- eager defaults can create oversized joins, duplicate parent rows, or unnecessary payloads + +Backend Agent should therefore: +- inspect relation defaults before changing repository or endpoint behavior +- override defaults at query time when access shape differs from mapping defaults +- prefer repository-level query shaping over relying on entity metadata alone + +### 6. Query review must include access-path and row-shape checks + +Backend Agent should therefore review: +- missing indexes on filters, joins, and foreign keys +- full table scans +- repeated identical queries that should be cached or batched +- row multiplication from `hasMany` / collection eager joins +- memory cost of hydrating full entities when raw results or projections are enough diff --git a/.agents/skills/backend-agent/resources/snippets.md b/.agents/skills/backend-agent/resources/snippets.md new file mode 100644 index 0000000..218bf2a --- /dev/null +++ b/.agents/skills/backend-agent/resources/snippets.md @@ -0,0 +1,197 @@ +# Backend Agent - Code Snippets + +Copy-paste ready patterns. Use these as starting points, adapt to the specific task. + +--- + +## FastAPI Route with Auth + +```python +from fastapi import APIRouter, Depends, HTTPException, status +from sqlalchemy.ext.asyncio import AsyncSession + +from app.core.deps import get_current_user, get_db +from app.models.user import User +from app.schemas.resource import ResourceCreate, ResourceResponse + +router = APIRouter(prefix="/api/resources", tags=["resources"]) + +@router.post("/", response_model=ResourceResponse, status_code=status.HTTP_201_CREATED) +async def create_resource( + data: ResourceCreate, + db: AsyncSession = Depends(get_db), + current_user: User = Depends(get_current_user), +): + resource = Resource(**data.model_dump(), user_id=current_user.id) + db.add(resource) + await db.commit() + await db.refresh(resource) + return resource +``` + +--- + +## Pydantic Schema Pair + +```python +from datetime import datetime +from uuid import UUID +from pydantic import BaseModel, Field + +class ResourceCreate(BaseModel): + title: str = Field(..., min_length=1, max_length=200) + description: str | None = None + +class ResourceResponse(BaseModel): + id: UUID + title: str + description: str | None + user_id: UUID + created_at: datetime + + model_config = {"from_attributes": True} +``` + +--- + +## SQLAlchemy Model + +```python +import uuid +from datetime import datetime +from sqlalchemy import ForeignKey, String, Text +from sqlalchemy.dialects.postgresql import UUID +from sqlalchemy.orm import Mapped, mapped_column, relationship + +from app.core.database import Base + +class Resource(Base): + __tablename__ = "resources" + + id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) + title: Mapped[str] = mapped_column(String(200), nullable=False) + description: Mapped[str | None] = mapped_column(Text, nullable=True) + user_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("users.id"), nullable=False, index=True) + created_at: Mapped[datetime] = mapped_column(default=datetime.utcnow) + + user: Mapped["User"] = relationship(back_populates="resources") +``` + +--- + +## JWT Auth Dependency + +```python +from fastapi import Depends, HTTPException, status +from fastapi.security import OAuth2PasswordBearer +from jose import JWTError, jwt + +from app.core.config import settings + +oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/auth/login") + +async def get_current_user( + token: str = Depends(oauth2_scheme), + db: AsyncSession = Depends(get_db), +) -> User: + try: + payload = jwt.decode(token, settings.SECRET_KEY, algorithms=["HS256"]) + user_id = payload.get("sub") + if user_id is None: + raise HTTPException(status_code=401, detail="Invalid token") + except JWTError: + raise HTTPException(status_code=401, detail="Invalid token") + + user = await db.get(User, user_id) + if user is None: + raise HTTPException(status_code=401, detail="User not found") + return user +``` + +--- + +## Password Hashing + +```python +from passlib.context import CryptContext + +pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") + +def hash_password(password: str) -> str: + return pwd_context.hash(password) + +def verify_password(plain: str, hashed: str) -> bool: + return pwd_context.verify(plain, hashed) +``` + +--- + +## Paginated Query + +```python +from sqlalchemy import select, func + +async def paginate( + db: AsyncSession, + query, + page: int = 1, + size: int = 20, +): + total = await db.scalar(select(func.count()).select_from(query.subquery())) + items = await db.scalars(query.offset((page - 1) * size).limit(size)) + return { + "items": list(items), + "total": total, + "page": page, + "size": size, + } +``` + +--- + +## Alembic Migration + +```python +"""add resources table + +Revision ID: xxxx +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects.postgresql import UUID + +def upgrade() -> None: + op.create_table( + "resources", + sa.Column("id", UUID(as_uuid=True), primary_key=True), + sa.Column("title", sa.String(200), nullable=False), + sa.Column("description", sa.Text, nullable=True), + sa.Column("user_id", UUID(as_uuid=True), sa.ForeignKey("users.id"), nullable=False), + sa.Column("created_at", sa.DateTime, server_default=sa.func.now()), + ) + op.create_index("ix_resources_user_id", "resources", ["user_id"]) + +def downgrade() -> None: + op.drop_table("resources") +``` + +--- + +## Test with httpx + +```python +import pytest +from httpx import AsyncClient + +@pytest.mark.asyncio +async def test_create_resource(client: AsyncClient, auth_headers: dict): + response = await client.post( + "/api/resources/", + json={"title": "Test", "description": "Test desc"}, + headers=auth_headers, + ) + assert response.status_code == 201 + data = response.json() + assert data["title"] == "Test" + assert "id" in data +``` diff --git a/.agents/skills/backend-agent/resources/tech-stack.md b/.agents/skills/backend-agent/resources/tech-stack.md new file mode 100644 index 0000000..448b53e --- /dev/null +++ b/.agents/skills/backend-agent/resources/tech-stack.md @@ -0,0 +1,43 @@ +# Backend Agent - Tech Stack Reference + +## Python (Preferred) +- **Framework**: FastAPI 0.110+ +- **ORM**: SQLAlchemy 2.0 (async) +- **Validation**: Pydantic v2 +- **Database**: PostgreSQL 16+, Redis 7+ +- **Auth**: python-jose (JWT), passlib (bcrypt) +- **Testing**: pytest, httpx (async test client) +- **Migrations**: Alembic + +## Node.js (Alternative) +- **Framework**: Express.js, NestJS, Hono +- **ORM**: Prisma, Drizzle +- **Validation**: Zod +- **Auth**: jsonwebtoken, bcrypt +- **Testing**: Jest, Supertest + +## Architecture + +``` +backend/ + domain/ # Business logic (pure Python, no framework deps) + application/ # Use cases, services + infrastructure/ # Database, cache, external APIs + presentation/ # API endpoints, middleware +``` + +## Security Requirements +- Password hashing: bcrypt (cost factor 10-12) +- JWT: 15min access tokens, 7 day refresh tokens +- Rate limiting on auth endpoints +- Input validation with Pydantic/Zod +- Parameterized queries (never string interpolation) + +## ORM Guidance +- Cross-ORM operational rules and official vendor references: `resources/orm-reference.md` +- Primary references covered there: Prisma, SQLAlchemy, TypeORM, Sequelize, Hibernate + +## Serena MCP Shortcuts +- `find_symbol("create_todo")`: Locate existing function +- `get_symbols_overview("app/api")`: List all endpoints +- `find_referencing_symbols("User")`: Find all usages of a model diff --git a/.agents/skills/backend-development/SKILL.md b/.agents/skills/backend-development/SKILL.md deleted file mode 100644 index 5d0216f..0000000 --- a/.agents/skills/backend-development/SKILL.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -name: backend-development -description: Comprehensive backend development skill for building scalable backend systems using Python (FastAPI), Postgres, Redis, and more. Includes API design, database optimization, security implementation, and performance tuning. ---- - -# Backend Development - -This skill provides expert guidance for building robust, scalable, and secure backend systems, primarily focusing on the Python/FastAPI ecosystem used in this project. - -## Core Capabilities - -### 1. API Design & Implementation -- **RESTful Design**: Resource-oriented URLs, proper HTTP methods, and status codes. -- **FastAPI Best Practices**: Validation with Pydantic, dependency injection, and async handlers. -- **Documentation**: Automatic OpenAPI generation, clear descriptions, and examples. - -### 2. Database Management -- **Schema Design**: Normalized relationships, indexing strategies, and migration management (Alembic). -- **ORM Usage**: SQLAlchemy async session management, repository pattern. -- **Optimization**: N+1 problem avoidance, query analysis, connection pooling. - -### 3. Security -- **Authentication**: JWT/OAuth2 implementation, password hashing (bcrypt/argon2). -- **Authorization**: Role-Based Access Control (RBAC), scopes. -- **Data Protection**: Input sanitization, SQL injection prevention (via ORM), secure headers. - -### 4. Performance Tuning -- **Caching**: Redis implementation for specific endpoints or data. -- **Async I/O**: Non-blocking database and API calls. -- **Background Tasks**: Offloading heavy processing (Celery/Cloud Tasks). - -## Design Patterns - -- **Repository Pattern**: Decouple business logic from data access. -- **Dependency Injection**: Manage dependencies (DB sessions, config) cleanly. -- **Service Layer**: Encapsulate complex business rules. - -## When to Use - -- Designing new API endpoints or microservices. -- Optimizing slow database queries. -- Implementing complex business logic. -- Reviewing backend code for security and performance. -- Setting up authentication and authorization flows. diff --git a/.agents/skills/brainstorm/SKILL.md b/.agents/skills/brainstorm/SKILL.md new file mode 100644 index 0000000..3db6e61 --- /dev/null +++ b/.agents/skills/brainstorm/SKILL.md @@ -0,0 +1,55 @@ +--- +name: brainstorm +description: Design-first ideation that explores user intent, constraints, and approaches before any planning or implementation. Use for brainstorming, ideation, exploring concepts, and evaluating approaches. +--- + +# Brainstorm - Design-First Ideation + +## When to use +- Exploring a new feature idea before planning +- Understanding user intent and constraints before committing to an approach +- Comparing multiple design approaches with trade-offs +- When the user says "I have an idea" or "let's design something" +- Before invoking `/plan` for complex or ambiguous requests + +## When NOT to use +- Requirements are already clear and well-defined -> use pm-agent directly +- Implementing actual code -> delegate to specialized agents +- Performing code reviews -> use QA Agent +- Debugging existing issues -> use debug-agent + +## Core Rules +1. **No implementation or planning before design approval** - brainstorm produces a design document, not code or task plans +2. **One question at a time** - ask clarifying questions sequentially, not in batches +3. **Always propose 2-3 approaches** - include a recommended option with trade-off analysis +4. **Section-by-section design** - present design incrementally with user confirmation at each step +5. **YAGNI** - do not over-engineer; design only what is needed for the stated goal +6. **Save design, then transition** - persist the approved design document before handing off to `/plan` + +## How to Execute +Follow the brainstorm workflow step by step: +1. **Phase 1 - Context**: Explore the existing codebase and understand the project landscape +2. **Phase 2 - Questions**: Ask clarifying questions one at a time to understand intent and constraints +3. **Phase 3 - Approaches**: Propose 2-3 approaches with a recommended option and trade-off matrix +4. **Phase 4 - Design**: Present the detailed design section by section, getting user approval at each step +5. **Phase 5 - Documentation**: Save the approved design to `docs/plans/` and project memory +6. **Phase 6 - Transition**: Hand off to `/plan` for task decomposition + +## Common Pitfalls +- **Jumping to solutions**: Asking "how" before fully understanding "what" and "why" +- **Too many questions at once**: Overwhelming the user with a wall of questions +- **Single approach bias**: Presenting only one option without alternatives +- **Over-engineering**: Designing for hypothetical future requirements instead of stated needs +- **Skipping confirmation**: Moving forward without explicit user approval on design decisions + +## Execution Protocol (CLI Mode) + +See `../_shared/execution-protocols/` for vendor-specific protocols. +When spawned via `oh-my-ag agent:spawn`, the protocol is injected automatically. + +## References +- Context loading: `../_shared/context-loading.md` +- Reasoning templates: `../_shared/reasoning-templates.md` +- Clarification protocol: `../_shared/clarification-protocol.md` +- Quality principles: `../_shared/quality-principles.md` +- Skill routing: `../_shared/skill-routing.md` diff --git a/.agents/skills/commit/SKILL.md b/.agents/skills/commit/SKILL.md new file mode 100644 index 0000000..88eb6a9 --- /dev/null +++ b/.agents/skills/commit/SKILL.md @@ -0,0 +1,117 @@ +--- +name: commit +description: Create git commits following Conventional Commits specification with project-specific branch naming rules. Use for commit message generation, changelog, and versioning. +--- + +# Commit Skill - Conventional Commits + +## When to use +- When user requests "commit this", "commit", "save changes" +- When `/commit` command is invoked + +## Configuration +Project-specific settings: `.agents/skills/commit/config/commit-config.yaml` + +## Commit Types +| Type | Description | Branch Prefix | +|------|-------------|---------------| +| feat | New feature | feature/ | +| fix | Bug fix | fix/ | +| refactor | Code improvement | refactor/ | +| docs | Documentation changes | docs/ | +| test | Test additions/modifications | test/ | +| chore | Build, configuration, etc. | chore/ | +| style | Code style changes | style/ | +| perf | Performance improvements | perf/ | + +## Commit Format +``` +(): + +[optional body] + +Co-Authored-By: First Fluke +``` + +## Workflow + +### Step 1: Analyze Changes +```bash +git status +git diff --staged +git log --oneline -5 +``` + +### Step 1.5: Split by Feature (if needed) +If changed files span multiple features/domains, **split commits by feature**. + +**Split criteria:** +- Different scopes (e.g., workflows vs skills vs docs) +- Different types (e.g., feat vs fix vs docs) +- Logically independent changes + +**Example:** +``` +# Changed files: +.agents/workflows/*.md (7 files) → fix(workflows): ... +.agents/skills/**/*.md (4 files) → fix(skills): ... +USAGE.md, USAGE-ko.md → docs: ... + +# Split into 3 commits +``` + +**Do NOT split when:** +- All changes belong to a single feature +- Few files changed (5 or fewer) +- User requested a single commit + +### Step 2: Determine Commit Type +Analyze changes → Select appropriate type: +- New files added → `feat` +- Bug fixed → `fix` +- Refactoring → `refactor` +- Documentation only → `docs` +- Tests added → `test` +- Build/config changes → `chore` + +### Step 3: Determine Scope +Use changed module/component as scope: +- `feat(auth)`: Authentication related +- `fix(api)`: API related +- `refactor(ui)`: UI related +- No scope is also valid: `chore: update dependencies` + +### Step 4: Write Description +- Under 72 characters +- Use imperative mood (add, fix, update, remove...) +- Lowercase first letter +- No trailing period + +### Step 5: Execute Commit +Show the commit message and proceed immediately without asking for confirmation: +``` +📝 Committing: + +feat(orchestrator): add multi-CLI agent mapping support + +- Add user-preferences.yaml for CLI configuration +- Update spawn-agent.sh to read agent-CLI mapping +- Update memory schema with CLI field + +Co-Authored-By: First Fluke +``` + +```bash +git add +git commit -m "" +``` + +## References +- Configuration: `config/commit-config.yaml` +- Guide: `resources/conventional-commits.md` + +## Important Notes +- **NEVER** use `git add -A` or `git add .` without explicit permission +- **NEVER** commit files that may contain secrets (.env, credentials, etc.) +- **ALWAYS** use specific file names when staging +- **ALWAYS** use HEREDOC for multi-line commit messages diff --git a/.agents/skills/commit/config/commit-config.yaml b/.agents/skills/commit/config/commit-config.yaml new file mode 100644 index 0000000..1434586 --- /dev/null +++ b/.agents/skills/commit/config/commit-config.yaml @@ -0,0 +1,55 @@ +# Commit Configuration + +# Enable Conventional Commits +conventional_commits: true + +# Allowed commit types +types: + - feat + - fix + - refactor + - docs + - test + - chore + - style + - perf + +# Branch prefix rules +branch_prefixes: + feat: "feature/" + fix: "fix/" + refactor: "refactor/" + docs: "docs/" + test: "test/" + chore: "chore/" + hotfix: "hotfix/" + style: "style/" + perf: "perf/" + +# Commit message rules +message: + max_subject_length: 72 + require_body: false + body_wrap_length: 100 + +# Scope rules +scope: + required: false + # Allowed scopes list (empty means all allowed) + allowed: [] + +# Co-Author settings +co_author: + enabled: true + name: "First Fluke" + email: "our.first.fluke@gmail.com" + +# Forbidden file patterns (commit warnings) +forbidden_patterns: + - "*.env" + - "*.env.*" + - "credentials.json" + - "secrets.yaml" + - "*.pem" + - "*.key" + - ".env.local" diff --git a/.agents/skills/commit/resources/conventional-commits.md b/.agents/skills/commit/resources/conventional-commits.md new file mode 100644 index 0000000..0644dd9 --- /dev/null +++ b/.agents/skills/commit/resources/conventional-commits.md @@ -0,0 +1,166 @@ +# Conventional Commits Guide + +## Overview + +Conventional Commits applies consistent rules to commit messages to enable: +- Automated CHANGELOG generation +- Semantic Versioning automation +- Improved commit history readability across teams + +## Commit Message Structure + +``` +[optional scope]: + +[optional body] + +[optional footer(s)] +``` + +## Types + +### Primary Types + +| Type | Description | SemVer | Example | +|------|-------------|--------|---------| +| `feat` | Add new feature | MINOR | `feat: add user authentication` | +| `fix` | Bug fix | PATCH | `fix: resolve login timeout issue` | + +### Secondary Types + +| Type | Description | SemVer | Example | +|------|-------------|--------|---------| +| `docs` | Documentation changes | - | `docs: update API documentation` | +| `style` | Code style changes (formatting, semicolons, etc.) | - | `style: fix indentation` | +| `refactor` | Code improvement without behavior change | - | `refactor: extract helper function` | +| `perf` | Performance improvements | PATCH | `perf: optimize database queries` | +| `test` | Add/modify tests | - | `test: add unit tests for auth` | +| `chore` | Build, config, packages | - | `chore: update dependencies` | + +## Scope + +Scope indicates the area of changed code: + +``` +feat(auth): add OAuth2 support +fix(api): handle null response +refactor(ui): simplify button component +``` + +### Common Scopes +- `auth` - Authentication/authorization +- `api` - API endpoints +- `ui` - User interface +- `db` - Database +- `config` - Configuration +- `deps` - Dependencies + +## Description + +- **Imperative mood**: "add", "fix", "update" (NOT "added", "fixed", "updates") +- **Lowercase first letter** +- **No trailing period** +- **72 characters or less** + +### Good Examples +``` +feat(auth): add JWT token refresh mechanism +fix(api): handle empty response from payment gateway +refactor(ui): extract common button styles +``` + +### Bad Examples +``` +feat(auth): Added JWT token refresh mechanism. # past tense, period +fix: fix bug # insufficient description +Update the authentication system to support OAuth2 tokens and refresh mechanism # too long +``` + +## Body + +Body is optional but useful for complex changes: + +``` +feat(auth): add multi-factor authentication + +Implement TOTP-based two-factor authentication: +- Add QR code generation for authenticator apps +- Store encrypted TOTP secrets in database +- Add backup codes for account recovery + +Closes #123 +``` + +## Breaking Changes + +Breaking changes marked with `!` or in footer: + +``` +feat(api)!: change response format for user endpoint + +BREAKING CHANGE: The user endpoint now returns a nested object +instead of a flat structure. Update client code accordingly. +``` + +## Footer + +### Issue References +``` +feat(auth): add password reset flow + +Closes #456 +Refs #123, #789 +``` + +### Co-Authors +``` +feat(ui): redesign dashboard + +Co-Authored-By: Jane Doe +Co-Authored-By: Claude Opus 4.5 +``` + +## Branch Naming Convention + +| Type | Branch Prefix | Example | +|------|---------------|---------| +| feat | `feature/` | `feature/user-auth` | +| fix | `fix/` | `fix/login-timeout` | +| refactor | `refactor/` | `refactor/api-cleanup` | +| docs | `docs/` | `docs/api-guide` | +| hotfix | `hotfix/` | `hotfix/security-patch` | + +## Commit Workflow + +1. **Stage specific files** (NOT `git add .`): + ```bash + git add src/auth/login.ts + git add tests/auth/login.test.ts + ``` + +2. **Write commit message**: + ```bash + git commit -m "$(cat <<'EOF' + feat(auth): add login rate limiting + + - Limit failed attempts to 5 per minute + - Add exponential backoff for repeated failures + - Log suspicious activity + + Closes #234 + + Co-Authored-By: Claude Opus 4.5 + EOF + )" + ``` + +3. **Verify**: + ```bash + git log -1 --format=full + ``` + +## Resources + +- [Conventional Commits Specification](https://www.conventionalcommits.org/) +- [Semantic Versioning](https://semver.org/) +- [Angular Commit Guidelines](https://github.com/angular/angular/blob/main/CONTRIBUTING.md#commit) diff --git a/.agents/skills/debug-agent/SKILL.md b/.agents/skills/debug-agent/SKILL.md new file mode 100644 index 0000000..cfad270 --- /dev/null +++ b/.agents/skills/debug-agent/SKILL.md @@ -0,0 +1,53 @@ +--- +name: debug-agent +description: Bug diagnosis and fixing specialist - analyzes errors, identifies root causes, provides fixes, and writes regression tests. Use for bug, debug, error, crash, traceback, exception, and regression work. +--- + +# Debug Agent - Bug Fixing Specialist + +## When to use +- User reports a bug with error messages +- Something is broken and needs fixing +- Performance issues or slowdowns +- Intermittent failures or race conditions +- Regression bugs + +## When NOT to use +- Building new features -> use Frontend/Backend/Mobile agents +- General code review -> use QA Agent + +## Core Rules +1. Reproduce first, then diagnose - never guess at fixes +2. Identify root cause, not just symptoms +3. Minimal fix: change only what's necessary +4. Every fix gets a regression test +5. Search for similar patterns elsewhere after fixing +6. Document in `.agents/brain/bugs/` + +## How to Execute +Follow `resources/execution-protocol.md` step by step. +See `resources/examples.md` for input/output examples. +Before submitting, run `resources/checklist.md`. + +## Serena MCP +- `find_symbol("functionName")`: Locate the function +- `find_referencing_symbols("Component")`: Find all usages +- `search_for_pattern("error pattern")`: Find similar issues + +## Execution Protocol (CLI Mode) + +See `../_shared/execution-protocols/` for vendor-specific protocols. +When spawned via `oh-my-ag agent:spawn`, the protocol is injected automatically. + +## References +- Execution steps: `resources/execution-protocol.md` +- Code examples: `resources/examples.md` +- Checklist: `resources/checklist.md` +- Error recovery: `resources/error-playbook.md` +- Bug report template: `resources/bug-report-template.md` +- Common patterns: `resources/common-patterns.md` +- Debugging checklist: `resources/debugging-checklist.md` +- Context loading: `../_shared/context-loading.md` +- Reasoning templates: `../_shared/reasoning-templates.md` +- Context budget: `../_shared/context-budget.md` +- Lessons learned: `../_shared/lessons-learned.md` diff --git a/.agents/skills/debug-agent/resources/bug-report-template.md b/.agents/skills/debug-agent/resources/bug-report-template.md new file mode 100644 index 0000000..ca3112d --- /dev/null +++ b/.agents/skills/debug-agent/resources/bug-report-template.md @@ -0,0 +1,332 @@ +# Bug Report Template + +Use this template when documenting bugs in the Knowledge Base. + +Save to: `.agents/brain/bugs/bug-YYYYMMDD-[short-description].md` + +--- + +# Bug: [Short Descriptive Title] + +**Date Reported**: YYYY-MM-DD +**Date Fixed**: YYYY-MM-DD (or "In Progress") +**Reporter**: [User name or issue number] +**Assignee**: [Agent that fixed it] +**Severity**: 🔴 CRITICAL | 🟠 HIGH | 🟡 MEDIUM | 🔵 LOW +**Status**: 🐛 OPEN | 🔧 IN PROGRESS | ✅ FIXED | ⏸️ ON HOLD | ❌ WON'T FIX + +--- + +## 📝 Problem Description + +**What happened?** +[Clear description of the bug from user's perspective] + +**What was expected?** +[What should have happened instead] + +**Impact**: +- Users affected: [All | Specific role | Percentage] +- Business impact: [Revenue loss | User frustration | Security risk] +- Workaround available: [Yes/No - describe if yes] + +--- + +## 🔄 Reproduction Steps + +1. Navigate to [page/route] +2. Click on [button/element] +3. Enter [data] in [field] +4. Observe [unexpected behavior] + +**Frequency**: +- [ ] Every time (100%) +- [ ] Intermittent (specify pattern: ___%) +- [ ] Specific conditions only (describe: ___) + +--- + +## 🖼️ Evidence + +**Error Messages**: +``` +[Exact error text from console/logs] +``` + +**Stack Trace**: +``` +[Full stack trace if available] +``` + +**Screenshots**: +- Before: [description or file path] +- After: [description or file path] +- Error state: [description or file path] + +**Browser Console**: +```javascript +// Relevant console errors +``` + +**Network Requests**: +``` +Request URL: [URL] +Status Code: [200/404/500/etc] +Response: [relevant response data] +``` + +--- + +## 🌍 Environment + +**Frontend**: +- Browser: [Chrome 120 | Firefox 121 | Safari 17] +- OS: [Windows 11 | macOS 14 | iOS 17 | Android 14] +- Screen size: [Desktop | Tablet | Mobile] +- Browser extensions: [Any relevant extensions] + +**Backend**: +- Environment: [Development | Staging | Production] +- Server version: [API version] +- Database: [PostgreSQL 16.1] + +**Mobile** (if applicable): +- Device: [iPhone 15 | Samsung Galaxy S24] +- OS Version: [iOS 17.2 | Android 14] +- App Version: [1.2.3] + +--- + +## 🔍 Investigation + +### Initial Analysis + +**Hypothesis**: +[What you think is causing the bug] + +**Investigation Steps Taken**: +1. [What was checked] +2. [What was ruled out] +3. [Key findings] + +### Root Cause + +**Technical Explanation**: +[Deep dive into why the bug occurs] + +**Code Location**: +- File: `path/to/file.tsx` +- Line: 145 +- Function: `handleSubmit()` + +**Specific Issue**: +```typescript +// Problem code +const user = data.user.profile.name; // Crashes if profile is undefined +``` + +**Why it happens**: +[Explain the conditions that trigger this] + +--- + +## 🔧 Solution + +### Fix Applied + +**Approach**: +[High-level description of the fix strategy] + +**Code Changes**: + +```typescript +// File: path/to/file.tsx (line 145) + +// ❌ BEFORE (buggy code) +const user = data.user.profile.name; + +// ✅ AFTER (fixed code) +const user = data?.user?.profile?.name ?? 'Unknown'; +``` + +**Why this works**: +[Explain the fix] + +### Files Modified + +- ✏️ `src/components/UserProfile.tsx` - Added null check for profile +- ✏️ `src/lib/api/users.ts` - Improved error handling +- ➕ `src/components/UserProfile.test.tsx` - Added regression test + +### Migration/Deployment Notes + +**Database Changes**: None | [Describe migrations needed] +**Configuration Changes**: None | [Describe config updates] +**Breaking Changes**: None | [Describe breaking changes] +**Rollback Plan**: [How to revert if needed] + +--- + +## ✅ Verification + +### Testing Performed + +- [x] **Regression test added** + - File: `src/components/UserProfile.test.tsx` + - Coverage: Null profile, undefined user, missing name + +- [x] **Manual testing** + - Tested original reproduction steps + - Tested edge cases (null, undefined, empty) + - Verified fix works in all browsers + +- [x] **Related areas checked** + - Found similar pattern in `TeamProfile.tsx` - also fixed + - Checked all `.profile.` usages - 3 more locations updated + +- [x] **Performance impact**: None | [Describe if any] + +### Test Results + +**Unit Tests**: ✅ 15/15 passing +**Integration Tests**: ✅ 8/8 passing +**E2E Tests**: ✅ 3/3 passing +**Manual QA**: ✅ Verified on Chrome, Firefox, Safari + +--- + +## 📚 Prevention + +### How to Avoid Similar Bugs + +1. **Always check for null/undefined** before accessing nested properties +2. **Use optional chaining (`?.`)** for safe property access +3. **Provide default values** with nullish coalescing (`??`) +4. **Add TypeScript strict null checks** to catch at compile time +5. **Write defensive code** - assume data might be missing + +### Code Patterns to Follow + +```typescript +// ✅ GOOD: Safe access with fallback +const name = user?.profile?.name ?? 'Anonymous'; + +// ✅ GOOD: Explicit null check +if (user?.profile) { + const name = user.profile.name; +} + +// ✅ GOOD: Early return +if (!user?.profile) { + return
No profile available
; +} + +// ❌ BAD: Unsafe nested access +const name = user.profile.name; // Crashes if profile undefined +``` + +### Recommended Tools + +- **TypeScript** with `strictNullChecks: true` +- **ESLint** rule: `no-unsafe-member-access` +- **Unit tests** for edge cases (null, undefined, empty) + +--- + +## 🔗 Related + +**Similar Bugs**: +- Bug #123: Similar null check issue in `CommentList` +- Bug #456: Related data loading pattern + +**Dependent Issues**: +- Issue #789: Need to improve error handling across all API calls + +**Documentation**: +- [Link to architecture decision record] +- [Link to API documentation] + +**External References**: +- [Stack Overflow link if used] +- [GitHub issue in library if relevant] + +--- + +## 📊 Metrics + +**Time to Fix**: [2 hours | 1 day | 1 week] +**Lines Changed**: [+15 -5] +**Files Affected**: [3 files] +**Tests Added**: [5 new tests] + +--- + +## 💬 Communication + +**Notified**: +- [x] Product Manager - Impact assessment +- [x] QA Team - Additional testing needed +- [x] Users affected - Via email/announcement +- [ ] Other teams - [Specify] + +**Changelog Entry**: +```markdown +### Fixed +- Fixed crash when user profile is missing ([#issue-number]) +``` + +--- + +## 🎓 Lessons Learned + +**What went well**: +- Quick identification of root cause +- Proactive fix of similar patterns +- Comprehensive test coverage + +**What could improve**: +- Should have caught this in code review +- TypeScript strict mode would have prevented this +- Need better null check linting rules + +**Action Items**: +- [ ] Enable TypeScript strictNullChecks across project +- [ ] Add ESLint rule for unsafe member access +- [ ] Update code review checklist to include null checks +- [ ] Create coding standards doc for defensive programming + +--- + +## 🏷️ Tags + +`frontend` `null-check` `crash` `typescript` `user-profile` `high-priority` + +--- + +**Sign-off**: [Your name/agent name] +**Reviewed by**: [PM Agent | QA Agent | Frontend Agent] +**Approved for deploy**: [Yes/No] + +--- + +## Template Notes + +**How to use this template**: + +1. **Copy this template** when documenting a new bug +2. **Fill in all sections** - don't skip parts +3. **Be specific** - future developers need details +4. **Include code** - show the before/after +5. **Link related issues** - help connect patterns +6. **Update status** as work progresses +7. **Save to Knowledge Base** for future reference + +**Sections you can skip**: +- Screenshots (if text description is clear) +- Stack trace (if no error thrown) +- Migration notes (if no DB/config changes) + +**Optional sections to add**: +- Timeline (for long-running bugs) +- Cost impact (for business-critical bugs) +- Vendor communication (if third-party bug) diff --git a/.agents/skills/debug-agent/resources/checklist.md b/.agents/skills/debug-agent/resources/checklist.md new file mode 100644 index 0000000..fd32b3a --- /dev/null +++ b/.agents/skills/debug-agent/resources/checklist.md @@ -0,0 +1,30 @@ +# Debug Agent - Self-Verification Checklist + +Run through every item before submitting your fix. + +## Fix Quality +- [ ] Root cause identified (not just symptom patched) +- [ ] Fix is minimal and focused (no unrelated changes) +- [ ] Edge cases handled (null, empty, boundary values) +- [ ] No new bugs introduced + +## Regression Test +- [ ] Test written that fails without the fix +- [ ] Test passes with the fix +- [ ] Test covers the specific triggering condition +- [ ] All existing tests still pass + +## Similar Patterns +- [ ] Searched for same pattern elsewhere in codebase +- [ ] Reported or fixed similar occurrences +- [ ] Noted if systemic issue needs architectural fix + +## Documentation +- [ ] Bug report saved to `.agents/brain/bugs/` +- [ ] Root cause clearly explained +- [ ] Prevention advice included +- [ ] Files modified listed + +## Security Check (if applicable) +- [ ] Fix doesn't introduce SQL injection, XSS, or auth bypass +- [ ] Sensitive data not exposed in error messages or logs diff --git a/.agents/skills/debug-agent/resources/common-patterns.md b/.agents/skills/debug-agent/resources/common-patterns.md new file mode 100644 index 0000000..540c8a0 --- /dev/null +++ b/.agents/skills/debug-agent/resources/common-patterns.md @@ -0,0 +1,734 @@ +# Common Bug Patterns & Solutions + +Quick reference guide for frequently encountered bugs and their fixes. + +--- + +## 🔴 Frontend Bugs + +### 1. Undefined/Null Errors + +**❌ Problem**: `Cannot read property 'X' of undefined` + +```typescript +// Crash when data not loaded yet +const name = user.profile.name; +``` + +**✅ Solutions**: + +```typescript +// Option 1: Optional chaining + nullish coalescing +const name = user?.profile?.name ?? 'Unknown'; + +// Option 2: Conditional rendering +{user?.profile &&
{user.profile.name}
} + +// Option 3: Early return +if (!user?.profile) return
Loading...
; +``` + +--- + +### 2. Stale Closures in useEffect + +**❌ Problem**: Event handlers/callbacks use old state values + +```typescript +function Counter() { + const [count, setCount] = useState(0); + + useEffect(() => { + const interval = setInterval(() => { + console.log(count); // Always logs 0! + }, 1000); + return () => clearInterval(interval); + }, []); // Missing dependency + + return ; +} +``` + +**✅ Solutions**: + +```typescript +// Option 1: Include dependency +useEffect(() => { + const interval = setInterval(() => { + console.log(count); // Now updates! + }, 1000); + return () => clearInterval(interval); +}, [count]); // Dependency added + +// Option 2: Use functional update +useEffect(() => { + const interval = setInterval(() => { + setCount(c => { + console.log(c); // Current value + return c; + }); + }, 1000); + return () => clearInterval(interval); +}, []); // Can stay empty + +// Option 3: Use ref for latest value +const countRef = useRef(count); +countRef.current = count; + +useEffect(() => { + const interval = setInterval(() => { + console.log(countRef.current); // Latest value + }, 1000); + return () => clearInterval(interval); +}, []); +``` + +--- + +### 3. Missing Cleanup in useEffect + +**❌ Problem**: Memory leaks from subscriptions/listeners + +```typescript +useEffect(() => { + const subscription = api.subscribe(data => setData(data)); + // Missing cleanup! +}, []); +``` + +**✅ Solution**: + +```typescript +useEffect(() => { + const subscription = api.subscribe(data => setData(data)); + + return () => { + subscription.unsubscribe(); // Cleanup + }; +}, []); +``` + +**Common things that need cleanup**: +- Event listeners (`addEventListener`) +- Intervals (`setInterval`) +- Timeouts (`setTimeout`) +- Subscriptions (WebSockets, observables) +- API requests (cancellation tokens) + +--- + +### 4. Race Conditions in Async Effects + +**❌ Problem**: Old requests overwrite new ones + +```typescript +useEffect(() => { + fetchUser(userId).then(setUser); + // If userId changes quickly, old responses arrive after new ones +}, [userId]); +``` + +**✅ Solution**: + +```typescript +useEffect(() => { + let cancelled = false; + + fetchUser(userId).then(user => { + if (!cancelled) { + setUser(user); + } + }); + + return () => { + cancelled = true; + }; +}, [userId]); +``` + +--- + +### 5. Infinite Re-render Loops + +**❌ Problem**: Component re-renders infinitely + +```typescript +function Component() { + const [data, setData] = useState([]); + + useEffect(() => { + setData([...data, 'new']); // Triggers effect again! + }, [data]); // Dependency causes loop + + return
{data.length}
; +} +``` + +**✅ Solutions**: + +```typescript +// Option 1: Remove problematic dependency +useEffect(() => { + setData(prevData => [...prevData, 'new']); +}, []); // Empty deps - runs once + +// Option 2: Use ref instead of state +const dataRef = useRef([]); + +useEffect(() => { + dataRef.current = [...dataRef.current, 'new']; +}, []); + +// Option 3: Add condition +useEffect(() => { + if (data.length === 0) { // Only run when empty + setData(['new']); + } +}, [data]); +``` + +--- + +### 6. Key Prop Issues in Lists + +**❌ Problem**: List items reordering incorrectly + +```typescript +// Using index as key +{todos.map((todo, index) => ( + +))} +``` + +**✅ Solution**: + +```typescript +// Use stable, unique ID +{todos.map(todo => ( + +))} + +// If no ID, generate stable key +{todos.map((todo, index) => ( + +))} +``` + +--- + +### 7. Form Input Controlled/Uncontrolled Switch + +**❌ Problem**: `Warning: A component is changing an uncontrolled input to be controlled` + +```typescript +const [value, setValue] = useState(); // undefined initially + + setValue(e.target.value)} /> +``` + +**✅ Solution**: + +```typescript +// Initialize with empty string +const [value, setValue] = useState(''); + + setValue(e.target.value)} /> + +// Or use defaultValue for uncontrolled + console.log(e.target.value)} /> +``` + +--- + +## 🔴 Backend Bugs + +### 1. SQL Injection + +**❌ Problem**: User input directly in SQL query + +```python +# DANGEROUS! +email = request.args.get('email') +query = f"SELECT * FROM users WHERE email = '{email}'" +db.execute(query) +# User can input: ' OR '1'='1 +``` + +**✅ Solution**: + +```python +# Use parameterized queries +from sqlalchemy import text + +email = request.args.get('email') +query = text("SELECT * FROM users WHERE email = :email") +result = db.execute(query, {"email": email}) + +# Or use ORM +user = db.query(User).filter(User.email == email).first() +``` + +--- + +### 2. N+1 Query Problem + +**❌ Problem**: One query per item in a loop + +```python +# 1 query to get todos +todos = db.query(Todo).all() + +# N queries (one per todo) +for todo in todos: + user = db.query(User).filter(User.id == todo.user_id).first() + print(f"{todo.title} by {user.name}") +``` + +**✅ Solution**: + +```python +# Use JOIN - single query +from sqlalchemy.orm import joinedload + +todos = db.query(Todo).options(joinedload(Todo.user)).all() + +for todo in todos: + print(f"{todo.title} by {todo.user.name}") # No extra query +``` + +--- + +### 3. Missing Authentication Check + +**❌ Problem**: Protected endpoint accessible without auth + +```python +@app.get("/api/admin/users") +async def get_all_users(db: DatabaseDep): + return db.query(User).all() # Anyone can access! +``` + +**✅ Solution**: + +```python +@app.get("/api/admin/users") +async def get_all_users( + db: DatabaseDep, + current_user: User = Depends(get_current_user) # Require auth +): + if current_user.role != "admin": # Check role + raise HTTPException(403, "Admin only") + return db.query(User).all() +``` + +--- + +### 4. Missing Input Validation + +**❌ Problem**: Invalid data causes errors + +```python +@app.post("/api/users") +async def create_user(email: str, age: int): + # No validation! + user = User(email=email, age=age) + db.add(user) + db.commit() +``` + +**✅ Solution**: + +```python +from pydantic import BaseModel, EmailStr, Field + +class UserCreate(BaseModel): + email: EmailStr # Validates email format + age: int = Field(ge=0, le=150) # Must be 0-150 + +@app.post("/api/users") +async def create_user(user: UserCreate): + # Pydantic validates automatically + db_user = User(**user.model_dump()) + db.add(db_user) + db.commit() +``` + +--- + +### 5. Unhandled Exceptions + +**❌ Problem**: Server crashes on error + +```python +@app.post("/api/todos") +async def create_todo(todo: TodoCreate, user: User = Depends(get_current_user)): + db_todo = Todo(**todo.model_dump(), user_id=user.id) + db.add(db_todo) + db.commit() # Could fail! + return db_todo +``` + +**✅ Solution**: + +```python +from fastapi import HTTPException + +@app.post("/api/todos") +async def create_todo(todo: TodoCreate, user: User = Depends(get_current_user)): + try: + db_todo = Todo(**todo.model_dump(), user_id=user.id) + db.add(db_todo) + db.commit() + db.refresh(db_todo) + return db_todo + except IntegrityError as e: + db.rollback() + raise HTTPException(409, "Duplicate entry") + except Exception as e: + db.rollback() + logger.error(f"Failed to create todo: {e}") + raise HTTPException(500, "Internal server error") +``` + +--- + +### 6. Missing CORS Configuration + +**❌ Problem**: Frontend can't call API + +``` +Access to fetch at 'http://localhost:8000/api/todos' from origin +'http://localhost:3000' has been blocked by CORS policy +``` + +**✅ Solution**: + +```python +from fastapi.middleware.cors import CORSMiddleware + +app.add_middleware( + CORSMiddleware, + allow_origins=["http://localhost:3000"], # Frontend URL + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +# For production, be specific: +# allow_origins=["https://yourdomain.com"] +``` + +--- + +### 7. Password Storage + +**❌ Problem**: Passwords stored in plain text + +```python +user = User(email=email, password=password) # NEVER DO THIS! +``` + +**✅ Solution**: + +```python +from passlib.context import CryptContext + +pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") + +# Hash before storing +password_hash = pwd_context.hash(password) +user = User(email=email, password_hash=password_hash) + +# Verify on login +if not pwd_context.verify(plain_password, user.password_hash): + raise HTTPException(401, "Invalid credentials") +``` + +--- + +## 🔴 Mobile Bugs + +### 1. Memory Leaks in Flutter + +**❌ Problem**: Controllers not disposed + +```dart +class MyWidget extends StatefulWidget { + @override + _MyWidgetState createState() => _MyWidgetState(); +} + +class _MyWidgetState extends State { + final controller = TextEditingController(); + + @override + Widget build(BuildContext context) { + return TextField(controller: controller); + } + // Missing dispose! +} +``` + +**✅ Solution**: + +```dart +class _MyWidgetState extends State { + final controller = TextEditingController(); + + @override + void dispose() { + controller.dispose(); // Clean up + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return TextField(controller: controller); + } +} +``` + +--- + +### 2. Platform-Specific Code Not Checked + +**❌ Problem**: iOS-specific code crashes on Android + +```dart +// Crashes on Android +import 'dart:io' show Platform; + +final deviceName = Platform.isIOS ? 'iPhone' : 'Unknown'; +``` + +**✅ Solution**: + +```dart +import 'dart:io' show Platform; + +final deviceName = Platform.isIOS + ? 'iPhone' + : Platform.isAndroid + ? 'Android' + : 'Unknown'; + +// Or use conditional imports +if (Platform.isIOS) { + // iOS-specific code +} else if (Platform.isAndroid) { + // Android-specific code +} +``` + +--- + +## 🔴 Performance Bugs + +### 1. Unnecessary Re-renders (React) + +**❌ Problem**: Component re-renders on every parent render + +```typescript +function Parent() { + const [count, setCount] = useState(0); + + return ( +
+ + {/* Re-renders every time! */} +
+ ); +} +``` + +**✅ Solution**: + +```typescript +// Memoize the expensive component +const ExpensiveChild = React.memo(function ExpensiveChild({ data }) { + // Only re-renders when data changes + return
{/* expensive computation */}
; +}); + +// Or memoize the props +function Parent() { + const [count, setCount] = useState(0); + const memoizedData = useMemo(() => computeData(), []); + + return ( +
+ + +
+ ); +} +``` + +--- + +### 2. Large Bundle Size + +**❌ Problem**: Importing entire library + +```typescript +// Imports all of lodash (~70KB) +import _ from 'lodash'; + +const unique = _.uniq(array); +``` + +**✅ Solution**: + +```typescript +// Import only what you need +import uniq from 'lodash/uniq'; // ~2KB + +const unique = uniq(array); + +// Or use native methods +const unique = [...new Set(array)]; +``` + +--- + +## 🔴 Security Bugs + +### 1. XSS (Cross-Site Scripting) + +**❌ Problem**: User input rendered as HTML + +```typescript +// Dangerous! +
+``` + +**✅ Solution**: + +```typescript +// React escapes by default +
{userComment}
+ +// If HTML needed, sanitize first +import DOMPurify from 'dompurify'; + +
+``` + +--- + +### 2. Missing Rate Limiting + +**❌ Problem**: API can be abused + +```python +@app.post("/api/auth/login") +async def login(credentials: LoginRequest): + # No rate limiting - brute force possible! + ... +``` + +**✅ Solution**: + +```python +from slowapi import Limiter +from slowapi.util import get_remote_address + +limiter = Limiter(key_func=get_remote_address) + +@app.post("/api/auth/login") +@limiter.limit("5/minute") # Max 5 attempts per minute +async def login(request: Request, credentials: LoginRequest): + ... +``` + +--- + +## 📊 Common Error Messages & Solutions + +| Error | Likely Cause | Solution | +|-------|--------------|----------| +| `Cannot read property 'X' of undefined` | Accessing property before data loads | Add null check or optional chaining | +| `Maximum update depth exceeded` | Infinite re-render loop | Check useEffect dependencies | +| `Warning: Each child should have unique key` | Missing/duplicate keys in list | Use unique, stable IDs as keys | +| `401 Unauthorized` | Missing/invalid auth token | Check token in request headers | +| `403 Forbidden` | Insufficient permissions | Verify user role/permissions | +| `404 Not Found` | Wrong URL or resource doesn't exist | Check endpoint path and resource ID | +| `500 Internal Server Error` | Backend exception | Check server logs for stack trace | +| `CORS Error` | Cross-origin request blocked | Configure CORS middleware | +| `ERR_CONNECTION_REFUSED` | Server not running | Start the backend server | + +--- + +## 🎯 Quick Debugging Commands + +### Frontend +```bash +# Check for errors +npm run lint + +# Run tests +npm test + +# Build and check bundle size +npm run build +npm run analyze + +# Check for unused dependencies +npx depcheck +``` + +### Backend +```bash +# Check for security issues +pip install safety +safety check + +# Run tests with coverage +pytest --cov=app + +# Check for SQL injection +bandit -r app/ +``` + +### Mobile +```bash +# Check for issues +flutter analyze + +# Run tests +flutter test + +# Check app size +flutter build apk --analyze-size +``` + +--- + +## 🔍 When to Use Each Agent + +| Bug Type | Best Agent | Reason | +|----------|-----------|---------| +| Frontend crash | debug-agent | Specializes in bug diagnosis | +| Backend API error | debug-agent | Can trace through stack | +| Complex multi-domain | workflow-guide | Coordinates multiple agents | +| Security vulnerability | qa-agent | Security expertise | +| Performance issue | qa-agent | Performance profiling tools | +| New feature needed | Specialist agent | Not a bug, it's a feature | + +--- + +## 💡 Prevention Tips + +1. **Write tests first** - Catch bugs before they ship +2. **Use TypeScript** - Catch type errors at compile time +3. **Enable strict mode** - More safety checks +4. **Code review** - Second pair of eyes +5. **Automated linting** - Enforce best practices +6. **Error monitoring** - Sentry, LogRocket, etc. +7. **User testing** - Real users find real bugs + +--- + +**Remember**: The best bug is the one that never happens. Write defensive code, test thoroughly, and document lessons learned! diff --git a/.agents/skills/debug-agent/resources/debugging-checklist.md b/.agents/skills/debug-agent/resources/debugging-checklist.md new file mode 100644 index 0000000..09fbf8e --- /dev/null +++ b/.agents/skills/debug-agent/resources/debugging-checklist.md @@ -0,0 +1,362 @@ +# Debugging Checklist + +Use this checklist when investigating bugs to ensure thorough analysis. + +## 📋 Initial Information Gathering + +- [ ] **Bug description** - What is the expected vs actual behavior? +- [ ] **Error messages** - Exact error text, stack trace, error codes +- [ ] **Reproduction steps** - Clear, numbered steps to trigger the bug +- [ ] **Frequency** - Every time, intermittent, specific conditions? +- [ ] **Environment** - Browser/OS/version, mobile device, server environment +- [ ] **User impact** - How many users affected? Severity? +- [ ] **Recent changes** - New deploy? Code changes? Configuration updates? +- [ ] **Screenshots/videos** - Visual evidence of the bug + +## 🔍 Frontend Debugging + +### JavaScript/TypeScript Errors + +- [ ] Check browser console (F12) for errors +- [ ] Review stack trace - which file and line? +- [ ] Identify error type (TypeError, ReferenceError, etc.) +- [ ] Check for null/undefined values +- [ ] Verify variable types (especially after API calls) +- [ ] Look for typos in property names + +### React Component Issues + +- [ ] **State problems** + - [ ] State not updating? (need setState/useState) + - [ ] Stale state in closures? (use refs or functional updates) + - [ ] State out of sync with props? + +- [ ] **Effect problems** + - [ ] Missing dependencies in useEffect? + - [ ] Effect running too many times? + - [ ] Cleanup function needed? + - [ ] Memory leak from uncancelled requests? + +- [ ] **Rendering issues** + - [ ] Conditional rendering logic correct? + - [ ] Keys on list items unique and stable? + - [ ] Unnecessary re-renders? (use React DevTools Profiler) + +### UI/UX Issues + +- [ ] CSS not applying? (check specificity, typos) +- [ ] Layout broken? (check responsive breakpoints) +- [ ] Accessibility issues? (keyboard nav, screen reader) +- [ ] Dark mode broken? +- [ ] Mobile-specific issues? + +### API Integration + +- [ ] **Network tab investigation** + - [ ] Request sent? (check Headers tab) + - [ ] Correct endpoint URL? + - [ ] Proper HTTP method (GET, POST, etc.)? + - [ ] Headers included (Authorization, Content-Type)? + - [ ] Request body formatted correctly? + - [ ] Response status code (200, 401, 404, 500)? + - [ ] Response data structure matches expected? + +- [ ] **Error handling** + - [ ] Try/catch around API calls? + - [ ] Error state displayed to user? + - [ ] Timeout handling? + - [ ] Network failure handling? + +- [ ] **Loading states** + - [ ] Loading spinner shown? + - [ ] Disabled buttons during load? + - [ ] Optimistic updates causing issues? + +## 🖥️ Backend Debugging + +### Python/FastAPI Errors + +- [ ] **Stack trace analysis** + - [ ] Read error message carefully + - [ ] Identify the failing file and line + - [ ] Understand the error type (ValueError, KeyError, etc.) + - [ ] Check for None values + - [ ] Verify data types + +- [ ] **Database issues** + - [ ] Query syntax correct? + - [ ] N+1 query problem? + - [ ] Missing JOIN? + - [ ] Indexes present on filtered columns? + - [ ] Connection pool exhausted? + - [ ] Transaction isolation issues? + +- [ ] **Authentication/Authorization** + - [ ] Token validation working? + - [ ] Token expired? + - [ ] Permissions checked correctly? + - [ ] User session valid? + - [ ] CORS configured properly? + +### API Endpoint Issues + +- [ ] **Request validation** + - [ ] Pydantic schema matches request? + - [ ] Required fields present? + - [ ] Field types correct? + - [ ] Value ranges validated? + +- [ ] **Response formatting** + - [ ] Correct status code? + - [ ] Response schema matches contract? + - [ ] Error responses formatted properly? + - [ ] Pagination working? + +- [ ] **Performance** + - [ ] Response time acceptable (<200ms)? + - [ ] Query optimization needed? + - [ ] Caching implemented? + - [ ] Rate limiting working? + +### Server Issues + +- [ ] **Logs examination** + - [ ] Check application logs + - [ ] Check error logs + - [ ] Check access logs + - [ ] Timestamps correlate with bug reports? + +- [ ] **Environment** + - [ ] Environment variables set? + - [ ] Configuration correct for environment (dev/prod)? + - [ ] External services reachable? + - [ ] Database migrations applied? + +## 📱 Mobile Debugging + +### Platform-Specific Issues + +- [ ] **iOS vs Android differences** + - [ ] Test on both platforms + - [ ] Check platform-specific code (Platform.OS) + - [ ] Review native module integration + - [ ] Verify permissions (camera, location, etc.) + +- [ ] **Device-specific** + - [ ] Test on actual device (not just emulator) + - [ ] Check different screen sizes + - [ ] Test different OS versions + - [ ] Memory constraints on older devices? + +### Flutter-Specific + +- [ ] **Widget tree issues** + - [ ] State management correct? (Riverpod/Bloc) + - [ ] Build method pure? + - [ ] Keys used properly? + - [ ] Dispose called for controllers? + +- [ ] **Platform channels** + - [ ] Native code errors? + - [ ] Method channel names correct? + - [ ] Data serialization working? + +### Mobile Performance + +- [ ] Memory leaks? (listeners not disposed) +- [ ] Excessive rebuilds? +- [ ] Large images not optimized? +- [ ] Too many simultaneous network requests? + +## 🔐 Security Bugs + +- [ ] **Authentication bypassed?** + - [ ] Token validation on all protected routes? + - [ ] Token expiry checked? + - [ ] Refresh token flow secure? + +- [ ] **Authorization broken?** + - [ ] User can access others' data? + - [ ] Admin functions accessible to regular users? + - [ ] Horizontal privilege escalation possible? + +- [ ] **Injection vulnerabilities** + - [ ] SQL injection? (use parameterized queries) + - [ ] XSS? (sanitize user input) + - [ ] Command injection? + - [ ] Path traversal? + +- [ ] **Data exposure** + - [ ] Sensitive data in logs? + - [ ] Passwords in plain text? + - [ ] API keys exposed? + - [ ] Error messages leaking info? + +## 🐌 Performance Bugs + +### Frontend Performance + +- [ ] **Lighthouse audit** + - [ ] Performance score >90? + - [ ] First Contentful Paint <1.5s? + - [ ] Largest Contentful Paint <2.5s? + - [ ] Cumulative Layout Shift <0.1? + +- [ ] **Bundle analysis** + - [ ] Main bundle <500KB? + - [ ] Code splitting implemented? + - [ ] Large dependencies tree-shaken? + - [ ] Lazy loading used? + +- [ ] **React performance** + - [ ] Unnecessary re-renders? (React DevTools Profiler) + - [ ] Memo/useMemo/useCallback used appropriately? + - [ ] Virtual scrolling for long lists? + +### Backend Performance + +- [ ] **API latency** + - [ ] p95 response time <200ms? + - [ ] Slow queries identified? + - [ ] Indexes added? + - [ ] Caching implemented? + +- [ ] **Database** + - [ ] Connection pooling configured? + - [ ] Query plan optimized? (EXPLAIN) + - [ ] N+1 queries eliminated? + +## 🔬 Root Cause Analysis + +- [ ] **Reproduce the bug** + - [ ] Follow exact reproduction steps + - [ ] Confirm bug exists + - [ ] Note any variations + +- [ ] **Isolate the problem** + - [ ] Which component/function fails? + - [ ] What input triggers it? + - [ ] What conditions must be met? + +- [ ] **Trace the data flow** + - [ ] Where does the bad data come from? + - [ ] How is it transformed? + - [ ] Where does it cause the failure? + +- [ ] **Identify the root cause** + - [ ] Not just the symptom + - [ ] Why does this happen? + - [ ] What assumption was wrong? + +## ✅ Fix Verification + +- [ ] **Fix applied** + - [ ] Code changed in correct file(s) + - [ ] Logic correct + - [ ] Edge cases handled + - [ ] No new bugs introduced + +- [ ] **Testing** + - [ ] Regression test written + - [ ] Test passes + - [ ] Manual verification done + - [ ] Edge cases tested + +- [ ] **Related areas checked** + - [ ] Similar patterns elsewhere? + - [ ] Other code depending on this? + - [ ] Breaking changes avoided? + +- [ ] **Documentation** + - [ ] Bug documented in Knowledge Base + - [ ] Fix explained + - [ ] Prevention notes added + +## 🚨 When to Escalate + +Escalate to other agents if: + +- [ ] Bug spans multiple domains → **workflow-guide** +- [ ] Requires architectural change → **PM Agent** +- [ ] Need comprehensive security audit → **QA Agent** +- [ ] Complex frontend refactoring needed → **Frontend Agent** +- [ ] Database schema changes needed → **Backend Agent** +- [ ] Platform-specific mobile issue → **Mobile Agent** + +## 📊 Priority Assessment + +**🔴 CRITICAL** - Fix immediately: +- [ ] App crashes on launch +- [ ] Data loss or corruption +- [ ] Security vulnerability +- [ ] Payment/auth completely broken +- [ ] Affects all users + +**🟠 HIGH** - Fix within 24 hours: +- [ ] Major feature broken +- [ ] Affects >50% of users +- [ ] No workaround available +- [ ] Significant revenue impact + +**🟡 MEDIUM** - Fix within sprint: +- [ ] Minor feature broken +- [ ] Affects <50% of users +- [ ] Workaround exists +- [ ] Moderate inconvenience + +**🔵 LOW** - Schedule for future: +- [ ] Edge case +- [ ] Cosmetic issue +- [ ] Rarely encountered +- [ ] No user impact + +## 📝 Documentation Template + +After fixing, document in `.agents/brain/bugs/`: + +```markdown +# Bug: [Title] +**Date**: YYYY-MM-DD +**Severity**: CRITICAL/HIGH/MEDIUM/LOW +**Status**: FIXED + +## Problem +[What was broken] + +## Root Cause +[Why it was broken] + +## Fix +[What was changed] + +## Files Modified +- path/to/file + +## Testing +- [x] Regression test added +- [x] Manual verification +- [x] Related areas checked + +## Prevention +[How to avoid in future] +``` + +--- + +## 💡 Pro Tips + +1. **Read the error message** - It usually tells you exactly what's wrong +2. **Reproduce first** - Don't waste time fixing unconfirmed bugs +3. **One change at a time** - Don't fix multiple things simultaneously +4. **Test thoroughly** - Verify the fix and check for regressions +5. **Document everything** - Future you will be grateful +6. **Look for patterns** - One bug often reveals more + +## 🛠️ Tools Reference + +- **Browser DevTools**: F12 (Console, Network, React DevTools) +- **Serena MCP**: find_symbol, search_for_pattern, find_referencing_symbols +- **Antigravity Browser**: Automated testing and reproduction +- **React Profiler**: Performance analysis +- **Lighthouse**: Performance audit +- **Git bisect**: Find when bug was introduced diff --git a/.agents/skills/debug-agent/resources/error-playbook.md b/.agents/skills/debug-agent/resources/error-playbook.md new file mode 100644 index 0000000..1174ecc --- /dev/null +++ b/.agents/skills/debug-agent/resources/error-playbook.md @@ -0,0 +1,94 @@ +# Debug Agent - Error Recovery Playbook + +When you encounter a failure during debugging, follow these recovery steps. +Do NOT stop or ask for help until you have exhausted the playbook. + +--- + +## Cannot Reproduce the Bug + +**Symptoms**: Bug described by user but you can't trigger it + +1. Re-read user's reproduction steps — are you following them exactly? +2. Check environment differences: browser, OS, node/python version +3. Check data-dependent: does it need specific DB state or test data? +4. Check timing: is it a race condition? Try adding delays or rapid repetition +5. **After 3 attempts**: Record `Status: cannot_reproduce` in result with condition list + - NEVER give up immediately saying "cannot reproduce" + +--- + +## Fix Introduces New Failure + +**Symptoms**: Original bug fixed but other tests break + +1. Read the failing tests — are they testing the old (buggy) behavior? +2. If yes: update tests to reflect correct behavior +3. If no: your fix has side effects — revert and try a more targeted approach +4. `find_referencing_symbols("fixedFunction")` to check all callers +5. Consider: is the function contract changing? If so, update all callers + +--- + +## Root Cause Unclear + +**Symptoms**: You see the failure but can't trace why + +1. Add logging at each step of the execution path +2. Binary search: is the bug before or after the midpoint? +3. `search_for_pattern("suspicious_pattern")` to find related code +4. Check git history: `git log --oneline -20 -- path/to/file` — when was it last changed? +5. Check: is it a dependency issue? Library version mismatch? +6. **No progress after 5 turns**: Record current analysis in progress, switch to different hypothesis + +--- + +## Bug Is in Another Agent's Domain + +**Symptoms**: Frontend bug caused by backend API, or vice versa + +1. Confirm: is the root cause really in the other domain? +2. Document the cross-domain issue clearly: + - Which endpoint/component is wrong + - What the correct behavior should be + - Evidence (request/response logs, stack trace) +3. Record in result: `cross_domain_issue: {agent: "backend", description: "..."}` +4. **Do NOT modify directly** — touching another agent's code causes conflicts + +--- + +## Performance Bug Hard to Measure + +**Symptoms**: "It's slow" but no clear metric + +1. Establish baseline: measure current response time / render time +2. Backend: enable SQL query logging, count queries, check `EXPLAIN ANALYZE` +3. Frontend: run Lighthouse, check React DevTools Profiler +4. Mobile: use Flutter DevTools performance tab +5. Profile before fixing — never optimize without data + +--- + +## Test Cannot Be Written + +**Symptoms**: Bug is real but hard to test (race condition, environment-specific) + +1. Try: mock the timing / environment condition +2. Try: integration test instead of unit test +3. If truly untestable: document the manual reproduction steps +4. Add a comment in code explaining why the fix is correct +5. Note in result: `test_limitation: "reason why automated test is not feasible"` + +--- + +## Rate Limit / Quota / Memory Fallback + +Same as backend-agent playbook: See relevant sections. + +--- + +## General Principles + +- **After 3 failures**: If same approach fails 3 times, must try a different method +- **Blocked**: If no progress after 5 turns, save current state, `Status: blocked` +- **Out of scope**: Other agent's domain — only record, do not modify directly diff --git a/.agents/skills/debug-agent/resources/examples.md b/.agents/skills/debug-agent/resources/examples.md new file mode 100644 index 0000000..63ed718 --- /dev/null +++ b/.agents/skills/debug-agent/resources/examples.md @@ -0,0 +1,87 @@ +# Debug Agent - Examples + +## Example 1: Null Reference Error + +**Input**: "TodoList crashes with 'Cannot read property map of undefined'" + +**Output**: +``` +## Bug Analysis + +**Root Cause**: TodoList renders before API data loads. `todos` is undefined +on first render, and `.map()` is called immediately. + +**Why**: useEffect fetches async, but component doesn't guard against +undefined initial state. + +## Fix +- Before: `todos.map(t => )` +- After: `todos?.map(t => ) ?? ` +- Also added loading spinner while fetching + +## Changes Made +1. Added `if (isLoading) return ` +2. Added optional chaining on `todos?.map()` +3. Added empty state component + +## Files Modified +- src/components/TodoList.tsx (guard + loading state) +- src/components/EmptyState.tsx (NEW) +- tests/TodoList.test.tsx (regression test) + +## Prevention +Use TanStack Query which provides `isLoading`/`data` pattern by default. +``` + +## Example 2: N+1 Query Performance Bug + +**Input**: "The posts page takes 3 seconds to load with only 50 posts" + +**Output**: +``` +## Bug Analysis + +**Root Cause**: N+1 query. For each post, a separate SQL query fetches +the author. 50 posts = 51 queries. + +**Why**: SQLAlchemy lazy-loads relationships by default. + +## Fix +- Before: `db.query(Post).all()` +- After: `db.query(Post).options(joinedload(Post.author)).all()` + +## Changes Made +1. Added eager loading with joinedload +2. Load time: 3000ms -> 45ms + +## Files Modified +- app/api/posts.py (eager loading) +- tests/test_posts.py (performance assertion: < 200ms) +``` + +## Example 3: Race Condition + +**Input**: "Search results show wrong data when typing fast" + +**Output**: +``` +## Bug Analysis + +**Root Cause**: Race condition. Fast typing triggers multiple API calls. +Slower early requests resolve after faster recent ones, overwriting +correct results. + +**Why**: No request cancellation; last-write-wins causes stale data. + +## Fix +Added AbortController to cancel previous request on new keystroke. + +## Changes Made +1. useEffect cleanup cancels pending request +2. Added debounce (300ms) to reduce API calls +3. Added `cancelled` flag to ignore stale responses + +## Files Modified +- src/hooks/useSearch.ts (abort + debounce) +- tests/useSearch.test.ts (race condition test) +``` diff --git a/.agents/skills/debug-agent/resources/execution-protocol.md b/.agents/skills/debug-agent/resources/execution-protocol.md new file mode 100644 index 0000000..91df681 --- /dev/null +++ b/.agents/skills/debug-agent/resources/execution-protocol.md @@ -0,0 +1,51 @@ +# Debug Agent - Execution Protocol + +## Step 0: Prepare +1. **Assess difficulty** — see `../_shared/difficulty-guide.md` + - **Simple**: Skip to Step 3 | **Medium**: All 4 steps | **Complex**: All steps + checkpoints +2. **Check lessons** — read your domain section in `../_shared/lessons-learned.md` +3. **Clarify requirements** — follow `../_shared/clarification-protocol.md` + - Check **Uncertainty Triggers**: security/auth related bugs, existing code conflict potential? + - Determine level: LOW → proceed | MEDIUM → present options | HIGH → ask immediately +4. **Use reasoning templates** — for Complex bugs, use `../_shared/reasoning-templates.md` (hypothesis loop, execution trace) +5. **Budget context** — follow `../_shared/context-budget.md` (use find_symbol, not read_file) + +**⚠️ Intelligent Escalation**: When uncertain, escalate early. Don't blindly proceed. + +Follow these steps in order (adjust depth by difficulty). + +## Step 1: Understand +- Gather: What happened? What was expected? Error messages? Steps to reproduce? +- Read relevant code using Serena: + - `find_symbol("functionName")`: Locate the failing function + - `find_referencing_symbols("Component")`: Find all callers + - `search_for_pattern("error pattern")`: Find similar issues +- Classify: logic bug, runtime error, performance issue, security flaw, or integration failure + +## Step 2: Reproduce & Diagnose +- Trace execution flow from entry point to failure +- Identify the exact line and condition that causes the bug +- Determine root cause (not just symptom): + - Null/undefined access? + - Race condition? + - Missing validation? + - Wrong assumption about data shape? +- Check `resources/common-patterns.md` for known patterns + +## Step 3: Fix & Test +- Apply minimal fix that addresses the root cause +- Write a regression test that: + - Fails without the fix + - Passes with the fix + - Covers the specific edge case +- Check for similar patterns elsewhere: `search_for_pattern("same_bug_pattern")` +- If found, fix proactively or report them + +## Step 4: Document & Verify +- Run `resources/checklist.md` items +- Save bug report to `.agents/brain/bugs/` using `resources/bug-report-template.md` +- Include: root cause, fix, prevention advice +- Verify no regressions in related functionality + +## On Error +See `resources/error-playbook.md` for recovery steps. diff --git a/.agents/skills/devops-iac-engineer/SKILL.md b/.agents/skills/devops-iac-engineer/SKILL.md deleted file mode 100644 index 600f66a..0000000 --- a/.agents/skills/devops-iac-engineer/SKILL.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -name: devops-iac-engineer -description: Expert guidance for designing, implementing, and maintaining cloud infrastructure using Experience in Infrastructure as Code (IaC) principles. Use this skill for architecting cloud solutions, setting up CI/CD pipelines, implementing observability, and following SRE best practices. ---- - -# DevOps IaC Engineer - -This skill provides expertise in designing and managing cloud infrastructure using Infrastructure as Code (IaC) and DevOps/SRE best practices. - -## When to Use - -- Designing cloud architecture (AWS, GCP, Azure) -- Implementing or refactoring CI/CD pipelines -- Setting up observability (logging, metrics, tracing) -- Creating Kubernetes clusters and container orchestration strategies -- Implementing security controls and compliance checks -- Improving system reliability (SLO/SLA, Disaster Recovery) - -## Infrastructure as Code (IaC) Principles - -- **Declarative Code**: Use Terraform/OpenTofu to define the desired state. -- **GitOps**: Code repository is the single source of truth. Changes are applied via PRs and automated pipelines. -- **Immutable Infrastructure**: Replace servers/containers rather than patching them in place. - -## Core Domains - -### 1. Terraform & IaC -- Use modules for reusability. -- Separate state by environment (dev, stage, prod) and region. -- Automate `plan` and `apply` in CI/CD. - -### 2. Kubernetes & Containers -- Build small, stateless containers. -- Use Helm or Kustomize for resource management. -- Implement resource limits and requests. -- Use namespaces for isolation. - -### 3. CI/CD Pipelines -- **CI**: Lint, test, build, and scan (security) on every commit. -- **CD**: Automated deployment to lower environments; manual approval for production. -- Use tools like GitHub Actions, Cloud Build, or ArgoCD. - -### 4. Observability -- **Logs**: Centralized logging (e.g., Cloud Logging, ELK). -- **Metrics**: Prometheus/Grafana or Cloud Monitoring. -- **Tracing**: OpenTelemetry for distributed tracing. - -### 5. Security (DevSecOps) -- Scan IaC for misconfigurations (e.g., Checkov, Trivy). -- Manage secrets utilizing Secret Manager or Vault (never in code). -- Least privilege IAM roles. - -## SRE Practices - -- **SLI/SLO**: Define Service Level Indicators and Objectives for critical user journeys. -- **Error Budgets**: Use error budgets to balance innovation and reliability. -- **Post-Mortems**: Conduct blameless post-mortems for incidents. diff --git a/.agents/skills/frontend-agent/SKILL.md b/.agents/skills/frontend-agent/SKILL.md new file mode 100644 index 0000000..4e2938d --- /dev/null +++ b/.agents/skills/frontend-agent/SKILL.md @@ -0,0 +1,152 @@ +--- +name: frontend-agent +description: Frontend specialist for React, Next.js, TypeScript with FSD-lite architecture, shadcn/ui, and design system alignment. Use for UI, component, page, layout, CSS, Tailwind, and shadcn work. +--- + +# Frontend Agent - UI/UX Specialist + +## When to use +- Building user interfaces and components +- Client-side logic and state management +- Styling and responsive design +- Form validation and user interactions +- Integrating with backend APIs + +## When NOT to use +- Backend API implementation -> use Backend Agent +- Native mobile development -> use Mobile Agent + +## Core Rules + +1. **Component Reuse**: Use `shadcn/ui` components first. Extend via `cva` variants or composition. Avoid custom CSS. +2. **Design Fidelity**: Code must map 1:1 to Design Tokens. Resolve discrepancies before implementation. +3. **Rendering Strategy**: Default to Server Components for performance. Use Client Components only for interactivity and API integration. +4. **Accessibility**: Semantic HTML, ARIA labels, keyboard navigation, and screen reader compatibility are mandatory. +5. **Tool First**: Check for existing solutions and tools before coding. + +## 1. Tooling & Performance + +- **Metrics**: Target First Contentful Paint (FCP) < 1s. +- **Optimization**: Use `next/dynamic` for heavy components, `next/image` for media, and parallel routes. +- **Responsive Breakpoints**: 320px, 768px, 1024px, 1440px +- **Shadcn Workflow**: + 1. Search: `shadcn_search_items_in_registries` + 2. Review: `shadcn_get_item_examples_from_registries` + 3. Install: `shadcn_get_add_command_for_items` + +## 2. Architecture (FSD-lite) + +- **Root (`src/`)**: Shared logic (components, lib, types). Hoist common code here. +- **Feature (`src/features/*/`)**: Feature-specific logic. **No cross-feature imports.** Unidirectional flow only. + +### Feature Directory Structure +``` +src/features/[feature]/ +├── components/ # Feature UI components +│ └── skeleton/ # Loading skeleton components +├── types/ # Feature-specific type definitions +└── utils/ # Feature-specific utilities & helpers +``` + +### Placement Rules +- `components/`: React components only. One component per file. +- `types/`: TypeScript interfaces and type definitions. +- `utils/`: All feature-specific logic (formatters, validators, helpers). **Requires >90% test coverage** for custom logic. + +> **Note**: Feature level does NOT have `lib/` folder. Use `utils/` for all utilities. `lib/` exists only at root `src/lib/` level. + +## 3. Libraries + +| Category | Library | +|----------|---------| +| Date | `luxon` | +| Styling | `TailwindCSS v4` + `shadcn/ui` | +| Hooks | `ahooks` (Pre-made hooks preferred) | +| Utils | `es-toolkit` (First choice) | +| State (URL) | `jotai-location` | +| State (Server) | `TanStack Query` | +| State (Client) | `Jotai` (Minimize use) | +| Forms | `@tanstack/react-form` + `zod` | + +## 4. Standards + +- **Utilities**: Check `es-toolkit` first. If implementing custom logic, **>90% Unit Test Coverage** is MANDATORY. +- **Design Tokens**: Source of Truth is `packages/design-tokens` (OKLCH). Never hardcode colors. +- **i18n**: Source of Truth is `packages/i18n`. Never hardcode strings. + +## 5. Component Strategy + +### Server vs Client Components +- **Server Components**: Layouts, Marketing pages, SEO metadata (`generateMetadata`, `sitemap`) +- **Client Components**: Interactive features and `useQuery` hooks + +### Structure +- **One Component Per File** + +### Naming Conventions +| Type | Convention | +|------|------------| +| Files | `kebab-case.tsx` (Name MUST indicate purpose) | +| Components/Types/Interfaces | `PascalCase` | +| Functions/Vars/Hooks | `camelCase` | +| Constants | `SCREAMING_SNAKE_CASE` | + +### Imports +- Order: Standard > 3rd Party > Local +- Absolute `@/` is MANDATORY (No relative paths like `../../`) +- **MUST use `import type`** for interfaces/types + +### Skeletons +- Must be placed in `src/features/[feature]/components/skeleton/` + +## 6. UI Implementation (Shadcn/UI) + +- **Usage**: Prefer strict shadcn primitives (`Card`, `Sheet`, `Typography`, `Table`) over `div` or generic classes. +- **Responsiveness**: Use `Drawer` (Mobile) vs `Dialog` (Desktop) via `useResponsive`. +- **Customization Rule**: Treat `components/ui/*` as read-only. Do not modify directly. + - **Correct**: Create a wrapper (e.g., `components/common/ProductButton.tsx`) or use `cva` composition. + - **Incorrect**: Editing `components/ui/button.tsx`. + +## 7. Designer Collaboration + +- **Sync**: Map code variables to Figma layer names. +- **UX**: Ensure key actions are visible "Above the Fold". + +## How to Execute + +Follow `resources/execution-protocol.md` step by step. +See `resources/examples.md` for input/output examples. +Before submitting, run `resources/checklist.md`. + +## Review Checklist + +- [ ] **A11y**: Interactive elements have `aria-label`. Semantic headings (`h1`-`h6`). +- [ ] **Mobile**: Functionality verified on mobile viewports. +- [ ] **Performance**: No CLS, fast load. +- [ ] **Resilience**: Error Boundaries and Loading Skeletons implemented. +- [ ] **Tests**: Logic covered by Vitest where complex. +- [ ] **Quality**: Typecheck and Lint pass. + +## Execution Protocol (CLI Mode) + +See `../_shared/execution-protocols/` for vendor-specific protocols. +When spawned via `oh-my-ag agent:spawn`, the protocol is injected automatically. + +## References + +- Execution steps: `resources/execution-protocol.md` +- Code examples: `resources/examples.md` +- Code snippets: `resources/snippets.md` +- Checklist: `resources/checklist.md` +- Error recovery: `resources/error-playbook.md` +- Tech stack: `resources/tech-stack.md` +- Component template: `resources/component-template.tsx` +- Tailwind rules: `resources/tailwind-rules.md` +- Context loading: `../_shared/context-loading.md` +- Reasoning templates: `../_shared/reasoning-templates.md` +- Clarification: `../_shared/clarification-protocol.md` +- Context budget: `../_shared/context-budget.md` +- Lessons learned: `../_shared/lessons-learned.md` + +> [!IMPORTANT] +> Treat `components/ui/*` as read-only. Create wrappers for customization. diff --git a/.agents/skills/frontend-agent/resources/checklist.md b/.agents/skills/frontend-agent/resources/checklist.md new file mode 100644 index 0000000..ae34d11 --- /dev/null +++ b/.agents/skills/frontend-agent/resources/checklist.md @@ -0,0 +1,38 @@ +# Frontend Agent - Self-Verification Checklist + +Run through every item before submitting your work. + +## TypeScript +- [ ] Strict mode, no `any` types +- [ ] Explicit interfaces for all component props +- [ ] No TypeScript errors (`npx tsc --noEmit`) + +## Styling +- [ ] Tailwind CSS only (no inline styles, no CSS modules) +- [ ] Responsive at 320px, 768px, 1024px, 1440px +- [ ] Dark mode supported (if project uses it) +- [ ] No hardcoded colors (use Tailwind theme tokens) + +## Accessibility (WCAG 2.1 AA) +- [ ] Semantic HTML elements (`