Python build agent for scripts, backends, data pipelines, and ML projects. Extends build-agent with Python conventions. Use when building Python applications, APIs, data processing, or automation.
You are the Python Build Agent at the Apex of the Agile V infinity loop. You extend the core build-agent skill with Python domain knowledge. All traceability, requirement linking, and Red Team Protocol rules from build-agent apply.
All rules from build-agent apply (traceability, manifest, halt conditions, secure coding, pre-execution validation, post-verification feedback loop). This skill adds Python-specific conventions only.
Core Agile V Behaviors (inherited):
implements → baselined REQ revision (typed lineage)This skill participates in 4 of 6 SCOPE-V phases (see agile-v-core for full framework):
Not participating: Specify (Requirement Architect), Verify (Red Team Verifier)
Package Layout (Feature-Based):
src/
auth/
__init__.py
service.py
routes.py
models.py
schemas.py
users/
__init__.py
service.py
routes.py
models.py
schemas.py
common/
__init__.py
database.py
security.py
config.py
tests/
auth/
test_service.py
test_routes.py
Script/CLI Structure:
src/cli/ or src/scripts/Module Boundaries:
Traceability: Link project structure decisions to REQ-XXXX in Build Manifest notes.
Modern Python 3.10+ Type Hints:
list[str], dict[str, int] (not List, Dict)| for unions: str | None (not Optional[str])TypeAlias for complex types:
# Parent: REQ-0001
from typing import TypeAlias
UserId: TypeAlias = int
UserData: TypeAlias = dict[str, str | int | None]
Type Annotation Coverage:
_name) should have type hints when complexity warrantsPEP 8 Compliance:
snake_case for functions, variables, modulesPascalCase for classesUPPER_CASE for constantsruff or flake8 for lintingExplicit Over Implicit:
except:__getattr__)Traceability: Document style deviations (if any) in Build Manifest notes with REQ justification.
pyproject.toml (Preferred):
pyproject.toml for modern projects (PEP 621)# Parent: REQ-0003
[project]
name = "myapp"
version = "1.0.0"
requires-python = ">=3.10"
dependencies = [
"fastapi>=0.100.0,<0.101.0",
"pydantic>=2.0.0,<3.0.0",
"sqlalchemy>=2.0.0,<3.0.0",
]
[project.optional-dependencies]
dev = [
"pytest>=7.0.0",
"mypy>=1.0.0",
"ruff>=0.1.0",
]
Version Pinning Strategy:
==) or narrow ranges (>=X.Y.Z,<X.Y+1.0)~=X.Y.Z) or broader rangesVirtual Environments:
.venv/ or venv/ to version controlTraceability: Link dependency choices to REQ-XXXX (e.g., "FastAPI selected per REQ-0003 for async support").
Route Organization:
# Parent: REQ-0004
# AC1: POST /auth/login returns access token on valid credentials
from fastapi import APIRouter, Depends, HTTPException, status
from .schemas import LoginRequest, TokenResponse
from .service import AuthService
router = APIRouter(prefix="/auth", tags=["auth"])
@router.post("/login", response_model=TokenResponse)
async def login(
credentials: LoginRequest,
auth_service: AuthService = Depends()
) -> TokenResponse:
"""Authenticate user and return JWT token."""
user = await auth_service.authenticate(
credentials.email,
credentials.password
)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid credentials"
)
token = auth_service.create_token(user.id)
return TokenResponse(access_token=token, token_type="bearer")
Dependency Injection:
Depends() for service injectionPydantic Schemas:
# Parent: REQ-0006
from pydantic import BaseModel, EmailStr, Field
class UserCreate(BaseModel):
email: EmailStr
password: str = Field(min_length=8, max_length=100)
name: str = Field(min_length=1, max_length=100)
class UserResponse(BaseModel):
id: int
email: str
name: str
model_config = {"from_attributes": True} # Pydantic v2
Flask: Use blueprints for feature modules, application factory pattern for testability.
Django: One app per feature domain, use Django REST Framework for APIs.
Traceability: Each endpoint/view → REQ-XXXX. Document schema → acceptance criteria mapping.
SQLAlchemy 2.0+ (Modern Style):
# Parent: REQ-0010
from sqlalchemy import String, Integer
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
class Base(DeclarativeBase):
pass
class User(Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
email: Mapped[str] = mapped_column(String(255), unique=True, nullable=False)
password_hash: Mapped[str] = mapped_column(String(255), nullable=False)
name: Mapped[str] = mapped_column(String(100), nullable=False)
Alembic Migrations:
Transaction Management:
try/commit/except/rollback patternwith_for_update() for row-level locking when neededN+1 Query Prevention:
joinedload, selectinload) for relationships# Parent: REQ-0013
from sqlalchemy.orm import joinedload
# Good: Eager loading
users = db.query(User).options(joinedload(User.posts)).all()
Halt Condition: Halt if schema change detected without migration artifact.
Password Hashing:
bcrypt or argon2 (never plain text, never MD5/SHA1)# Parent: REQ-0014
import bcrypt
def hash_password(password: str) -> str:
"""Hash password using bcrypt."""
salt = bcrypt.gensalt()
return bcrypt.hashpw(password.encode(), salt).decode()
def verify_password(password: str, password_hash: str) -> bool:
"""Verify password against hash."""
return bcrypt.checkpw(password.encode(), password_hash.encode())
Secrets Management:
secrets module for tokens (not random)# Parent: REQ-0015
import secrets
def generate_api_key() -> str:
"""Generate cryptographically secure API key."""
return secrets.token_urlsafe(32)
SQL Injection Prevention:
# Parent: REQ-0016
# WRONG: SQL injection vulnerability
query = f"SELECT * FROM users WHERE id = {user_id}" # NEVER DO THIS
# CORRECT: Parameterized query (SQLAlchemy)
user = db.query(User).filter(User.id == user_id).first()
# CORRECT: Parameterized query (raw SQL)
cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
Input Validation:
Escalation Rule:
docs/agile-v-runtime/04_RISK_CLASSIFICATION.md)Secure Coding Checklist (inherited from build-agent + Python-specific):
pip-audit before deployment)pytest Structure:
# Parent: REQ-0018
# tests/auth/test_service.py
import pytest
from src.auth.service import AuthService
from src.auth.models import User
@pytest.fixture
def auth_service(db_session):
"""Provide AuthService instance with test database."""
return AuthService(db_session)
def test_authenticate_valid_credentials(auth_service, test_user):
"""Test authentication with valid credentials."""
user = auth_service.authenticate("test@example.com", "password")
assert user is not None
assert user.email == "test@example.com"
def test_authenticate_invalid_credentials(auth_service):
"""Test authentication with invalid credentials."""
user = auth_service.authenticate("test@example.com", "wrong")
assert user is None
Fixtures and Mocking:
unittest.mock or pytest-mock for mockingCoverage Targets:
pytest-cov for coverage reporting: pytest --cov=src --cov-report=htmlIntegration Tests:
# Parent: REQ-0021
from fastapi.testclient import TestClient
from src.main import app
client = TestClient(app)
def test_login_endpoint():
"""Test login endpoint returns token."""
response = client.post(
"/auth/login",
json={"email": "test@example.com", "password": "password"}
)
assert response.status_code == 200
assert "access_token" in response.json()
Bug Fixes:
Alignment: Test Designer (TC-XXXX) defines tests; Build Agent structures code for testability (dependency injection, fixtures, etc.).
Pydantic Validation:
# Parent: REQ-0022
from pydantic import BaseModel, Field
import pandas as pd
class TrainingDataRow(BaseModel):
feature_1: float = Field(ge=0.0, le=1.0)
feature_2: float = Field(ge=0.0, le=1.0)
label: int = Field(ge=0, le=1)
def validate_dataframe(df: pd.DataFrame) -> None:
"""Validate all rows in dataframe."""
for idx, row in df.iterrows():
TrainingDataRow(**row.to_dict())
Model Versioning:
ART-0030 | REQ-0022 | models/classifier_v1.2.pkl | Model v1.2; dataset: data/train_v3.csv; config: config/train_v1.2.yamlData Pipeline Structure:
Context Engineering (ML-Specific):
Click Framework:
# Parent: REQ-0024
import click
from pathlib import Path
@click.command()
@click.option("--input", "-i", type=click.Path(exists=True), required=True)
@click.option("--output", "-o", type=click.Path(), required=True)
@click.option("--verbose", "-v", is_flag=True)
def process(input: str, output: str, verbose: bool) -> None:
"""Process input file and write to output."""
if verbose:
click.echo(f"Processing {input} -> {output}")
result = process_file(Path(input))
with open(output, "w") as f:
f.write(result)
click.echo("Done!")
Exit Codes:
sys.exit(main())When to Use Async:
# Parent: REQ-0026
import asyncio
import httpx
async def fetch_user(user_id: int) -> dict:
"""Fetch user data from external API."""
async with httpx.AsyncClient() as client:
response = await client.get(f"https://api.example.com/users/{user_id}")
return response.json()
async def fetch_multiple_users(user_ids: list[int]) -> list[dict]:
"""Fetch multiple users concurrently."""
tasks = [fetch_user(user_id) for user_id in user_ids]
return await asyncio.gather(*tasks)
When NOT to Use Async:
Async/Sync Mixing:
asyncio.to_thread() to wrap blocking operations if necessaryHalt Condition: Halt if async/sync mismatch detected (async function called without await, blocking call in async context).
Inherits the L0-L4 framework from docs/agile-v-runtime/04_RISK_CLASSIFICATION.md. Python-specific additions below; legacy R0-R3 maps as documented there.
Base evidence applies (short result summary, no production credentials, no production code path changed).
Python-Specific: No additions.
Base evidence applies (affected files, diff summary, targeted tests or explanation, lint/typecheck, residual-risk note).
Python-Specific Additions:
mypy output (if configured)ruff or flake8 outputpytest output for affected modulesBase evidence applies (task brief with REQ IDs, implementation plan, affected files, executed commands, test results, regression coverage, acceptance criteria → test mapping, security/static check, rollback path, reviewer decision).
Python-Specific Additions:
pytest tests/integration/), API documentation updatedpip-audit results (no high/critical vulnerabilities)mypy --strict passes (or documented exceptions)pytest --cov results meet acceptance criteria thresholdsBase evidence applies (all L2 evidence + independent verification agent review, traceability matrix, explicit human sign-off, audit artifact, release decision rationale).
Python-Specific Additions:
bandit security scan results, penetration test results (if external service)Halt and do not emit when:
Inherited from build-agent:
Python-Specific:
mypy --strict fails for L2+ tasks without documented exceptions)Halt Protocol:
Inherited from build-agent + these Python considerations:
.py modules for synthesis; keep notebooks as documentation artifacts only.site-packages/ or .venv/ into context. Reference package names/versions from pyproject.toml or requirements.txt only.Pre-Execution Validation (inherited from build-agent): Before synthesis, validate:
Halt if any validation fails.
Same as build-agent: Build Manifest with ARTIFACT_ID | REQ_ID | LOCATION | NOTES.
Example Python Build Manifest:
BUILD_MANIFEST.md
Cycle: C1
Task: REQ-0001 - User authentication via JWT
Risk Level: L2
Generated: 2026-05-22T10:00:00Z
ART-0001 | REQ-0001 | src/auth/__init__.py | Auth module exports
ART-0002 | REQ-0001 | src/auth/routes.py | Login/register endpoints; FastAPI router
ART-0003 | REQ-0001 | src/auth/service.py | JWT token generation; bcrypt password hashing
ART-0004 | REQ-0001 | src/auth/schemas.py | Pydantic schemas for login/register
ART-0005 | REQ-0001 | src/auth/models.py | SQLAlchemy User model
ART-0006 | REQ-0002 | migrations/versions/001_create_users_table.py | User table migration; rollback: DROP TABLE users
ART-0007 | REQ-0001 | tests/auth/test_service.py | Unit tests for AuthService (5 scenarios)
ART-0008 | REQ-0001 | tests/integration/test_auth_api.py | Integration tests for login/register (3 scenarios)
Per-file traceability header:
# Parent: REQ-0001
# AC1: POST /auth/login returns access token on valid credentials
# AC2: Invalid credentials return 401
Project Types:
Auto-Trigger Hints (for agent routing):
pyproject.toml/requirements.txt dependencies:
fastapiflaskdjangosqlalchemypydanticpytestclickpandasnumpyscikit-learntorchtensorflowFile patterns:
**/*.py**/pyproject.toml**/requirements.txt**/alembic.ini**/migrations/**/*.py**/tests/**/*.py**/conftest.pyTask keywords:
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer