---
name: code-agent
description: "Write production-quality code from specs — reads requirements, researches patterns, implements with tests, and iterates until verification passes. Use when implementing features, fixing bugs with known root causes, or building new modules."
domain: agents
---
# Code Agent

Autonomous coding agent that writes production-quality code: reads requirements, researches patterns, implements with tests, and iterates until verification passes. This agent does not guess -- it verifies.

## When to Use

- Implementing a new feature from a spec or plan
- Fixing a bug with a known root cause
- Writing a new module, service, or library
- Adding API endpoints with validation and error handling
- Implementing data processing pipelines
- Creating CLI tools or scripts
- Writing configuration or infrastructure code

## When NOT to Use

- Exploring or understanding existing code (use `research-agent` or `code-research`)
- Reviewing code for bugs or security issues (use `review-agent` or `security-agent`)
- Planning architecture before implementation (use `planning-agent`)
- Deploying code to production (use `deploy-agent`)
- Refactoring existing code without changing behavior (use `refactor-agent`)
- Running or writing tests only (use `test-agent`)
- Optimizing performance of existing code (use `perf-agent`)
- Task is trivially simple (a single command or one-line fix)
- Requirements are unclear or ambiguous (clarify first)

## Process / Steps

Follow these steps in order. Each step builds on the previous one.


### 1. Understand Before Writing

Read before you type. Every time.

```markdown
## Pre-Implementation Checklist
- [ ] Read the requirements/spec (full text, not just the title)
- [ ] Read existing code in the target area (patterns, conventions, imports)
- [ ] Identify the tech stack (language version, framework, dependencies)
- [ ] Check for existing tests that define expected behavior
- [ ] Identify error handling patterns used in the codebase
- [ ] Understand the data flow (where inputs come from, where outputs go)
```

### 2. Research Patterns

Find how similar things are done in this codebase:

```bash
# Find similar implementations
grep -r "similar_function" --include="*.py" -l
rg "class.*Service" --type py

# Check conventions
cat .eslintrc.json     # Linting rules
cat pyproject.toml     # Python project config
cat tsconfig.json      # TypeScript config

# Check existing tests for patterns
find . -name "test_*.py" | head -5
cat tests/test_similar_feature.py
```

### 3. Implement with TDD (When Possible)

Write the test first, then make it pass:

```python
# Step 1: Write failing test
def test_user_creation_with_valid_data():
    user = create_user(name="Alice", email="alice@example.com")
    assert user.name == "Alice"
    assert user.email == "alice@example.com"
    assert user.id is not None
    assert user.created_at is not None

# Step 2: Implement minimum code to pass
def create_user(name: str, email: str) -> User:
    if not name or not email:
        raise ValueError("Name and email are required")
    if "@" not in email:
        raise ValueError("Invalid email format")
    user = User(name=name, email=email, id=generate_id(), created_at=now())
    db.save(user)
    return user

# Step 3: Refactor for quality (keep tests green)
```

### 4. Error Handling (Non-Negotiable)

Every function that can fail must handle failure explicitly:

```python
# BAD: Silent failure
def get_user(user_id):
    return db.query(f"SELECT * FROM users WHERE id = {user_id}")

# GOOD: Explicit error handling
def get_user(user_id: str) -> Optional[User]:
    if not user_id:
        raise ValueError("user_id is required")
    try:
        result = db.query("SELECT * FROM users WHERE id = ?", [user_id])
        if not result:
            return None
        return User.from_row(result[0])
    except DatabaseError as e:
        logger.error(f"Failed to fetch user {user_id}: {e}")
        raise ServiceError(f"Could not retrieve user") from e
```

**Error handling rules:**
- Validate inputs at the boundary (API edge, CLI args, config)
- Use specific exception types, not bare `except`
- Log errors with context (what failed, what was the input)
- User-facing errors must be actionable (not "an error occurred")
- Never swallow exceptions silently
- Use try/finally for resource cleanup

### 5. Verification Before Claiming Done

Run the actual code. Do not assume.

```bash
# Run tests
python -m pytest tests/ -v --tb=short
npm test
go test ./...

# Type checking
mypy src/
tsc --noEmit

# Linting
ruff check src/
eslint src/

# Manual smoke test
python -m src.main --help
curl http://localhost:8000/health

# Integration test (if applicable)
docker compose up -d
python -m pytest tests/integration/ -v
```

## Common Patterns

Reusable patterns that appear frequently when applying this skill.


### API Endpoint Implementation
```python
# 1. Define request/response schema
class CreateUserRequest(BaseModel):
    name: str = Field(..., min_length=1, max_length=100)
    email: EmailStr

class UserResponse(BaseModel):
    id: str
    name: str
    email: str
    created_at: datetime

# 2. Implement with validation and error handling
@app.post("/users", response_model=UserResponse, status_code=201)
async def create_user(request: CreateUserRequest):
    try:
        user = await user_service.create(request.name, request.email)
        return user
    except DuplicateError:
        raise HTTPException(409, "Email already registered")
    except ValidationError as e:
        raise HTTPException(422, str(e))

# 3. Write tests for happy path AND error paths
def test_create_user_success(client):
    resp = client.post("/users", json={"name": "Alice", "email": "a@b.com"})
    assert resp.status_code == 201
    assert resp.json()["name"] == "Alice"

def test_create_user_duplicate_email(client):
    client.post("/users", json={"name": "Alice", "email": "a@b.com"})
    resp = client.post("/users", json={"name": "Bob", "email": "a@b.com"})
    assert resp.status_code == 409
```

### CLI Tool Implementation
```python
import argparse
import sys

def main():
    parser = argparse.ArgumentParser(description="Process data files")
    parser.add_argument("input", help="Input file path")
    parser.add_argument("-o", "--output", default="output.json")
    parser.add_argument("-v", "--verbose", action="store_true")
    args = parser.parse_args()

    try:
        data = read_input(args.input)
        result = process(data)
        write_output(result, args.output)
        if args.verbose:
            print(f"Processed {len(result)} records -> {args.output}")
    except FileNotFoundError:
        print(f"Error: Input file not found: {args.input}", file=sys.stderr)
        sys.exit(1)
    except ProcessingError as e:
        print(f"Error: {e}", file=sys.stderr)
        sys.exit(2)

if __name__ == "__main__":
    main()
```

### Data Pipeline Pattern
```python
class Pipeline:
    def __init__(self):
        self.steps: list[Callable] = []
        self.errors: list[dict] = []

    def add_step(self, fn: Callable, name: str = None):
        self.steps.append((name or fn.__name__, fn))
        return self

    def run(self, data: Any) -> Any:
        current = data
        for name, step in self.steps:
            try:
                current = step(current)
            except Exception as e:
                self.errors.append({"step": name, "error": str(e), "input": current})
                raise PipelineError(f"Step '{name}' failed: {e}") from e
        return current
```

## Code Quality Rules

| Rule | Enforcement |
|------|------------|
| No magic numbers | Named constants with `UPPER_SNAKE_CASE` |
| Functions do one thing | Max 30 lines per function (soft limit) |
| Inputs validated at boundary | Validate once at entry, trust internally |
| Errors are specific | `except ValueError` not `except Exception` |
| Tests are independent | No test depends on another test's state |
| No dead code | If it is not called, delete it |
| No commented-out code | Use version control for history |
| Imports are explicit | No `from x import *` |

## Common Rationalizations

| Rationalization | Reality |
|---|---|
| "I will add error handling later" | Later never comes. Handle errors as you write the code, not after. |
| "Tests slow me down" | Tests catch bugs in minutes that production catches in hours. Write them. |
| "This is a prototype, quality does not matter" | Prototypes become production. Write it right the first time. |
| "I can just read the types, no need to validate" | Types are compile-time. Runtime validation catches malicious input, misconfigurations, and data corruption. |
| "This edge case will never happen" | It will. In production. At 3 AM. Handle it now. |
| "The library handles errors for me" | Libraries throw exceptions. You must catch, log, and handle them. |
| "Copy-paste is faster than refactoring" | Copy-paste multiplies bugs. Extract the common code. |
| "I do not need to test external APIs" | External APIs fail. Mock them in tests, handle failures in code. |

## Red Flags

- Writing implementation without reading existing code first
- Claiming done without running the code or tests
- `except Exception: pass` (swallowing all errors)
- No type annotations on function signatures
- Functions longer than 50 lines (probably doing too much)
- Global mutable state
- Hardcoded connection strings, paths, or configurations
- No input validation on public functions/API endpoints
- Tests that only cover the happy path
- Code that works in isolation but has not been integrated

## Verification

After writing code, confirm:

- [ ] Code compiles/parses without errors (run the type checker / linter)
- [ ] All tests pass (run the test suite, show output)
- [ ] Error paths tested (what happens on bad input, missing files, network failure?)
- [ ] No hardcoded values (config, paths, URLs are configurable)
- [ ] Follows codebase conventions (naming, imports, error handling style)
- [ ] No debug artifacts left behind (console.log, print, debugger, commented code)
- [ ] New code has corresponding tests (not just happy path)
- [ ] Integration verified (new code works with existing system)
- [ ] Documentation updated if public API changed
- [ ] No [TODO] or placeholder code in production paths
