debug-backend
Systematic 5-step backend debugging flow for AI-coded FastAPI apps. Load this skill when a bug is reported, a test fails, or unexpected behavior appears in any…
Checklist for adding a new AI provider (image, video, text, audio) that satisfies all 5 integration principles — ACL, bulkhead, idempotency, observability, and contract test. Load when integrating any new external AI API. Prevents the most common mistake of pasting httpx calls
$ npx -y skills add yerdaulet-damir/vibe-coding-rules --skill add-provider --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/add-providerContext preview
The summary Claude sees to decide when to auto-load this skill.
Checklist for adding a new AI provider (image, video, text, audio) that satisfies all 5 integration principles — ACL, bulkhead, idempotency, observability, and contract test. Load when integrating any new external AI API. Prevents the most common mistake of pasting httpx calls
name: add-provider description: Checklist for adding a new AI provider (image, video, text, audio) that satisfies all 5 integration principles — ACL, bulkhead, idempotency, observability, and contract test. Load when integrating any new external AI API. Prevents the most common mistake of pasting httpx calls directly into a service.
A provider added wrong leaks its API shape into your business logic. One API change from the vendor → rewrite half your service. Follow this checklist.
---
| Provider supports | Structure | |------------------|-----------| | One modality (text only, image only) | `app/providers/<name>.py` | | Multiple modalities (image + video) | `app/providers/<name>/` with `image.py`, `video.py`, `__init__.py` |
Principle A4: one file per format when a provider handles multiple formats.
---
# app/providers/<name>.py (or app/providers/<name>/image.py)
from __future__ import annotations
import logging
from decimal import Decimal
import httpx
from app.core.bulkhead import get_provider_client # Principle B5
from app.core.context import provider_ctx # Principle B7
from app.providers.base import AIProvider, JobRequest, JobResult
from app.providers.exceptions import (
ProviderError,
ProviderInvalidResponseError,
ProviderRateLimitError,
ProviderTimeoutError,
)
logger = logging.getLogger(__name__)
class <Name>Provider:
provider_name = "<name>" # used in logs + bulkhead key
async def generate(self, request: JobRequest) -> JobResult:
provider_ctx.set(self.provider_name) # Principle B7: set before any I/O
client = get_provider_client(self.provider_name) # Principle B5: isolated client
try:
response = await client.post(
"/v1/generate",
json=self._build_payload(request),
headers={"X-Idempotency-Key": request.idempotency_key}, # Principle B6
timeout=30.0,
)
response.raise_for_status()
except httpx.TimeoutException as e:
raise ProviderTimeoutError(
message=str(e), provider=self.provider_name, retryable=True
) from e
except httpx.HTTPStatusError as e:
self._map_http_error(e)
return self._parse_response(response.json(), request) # Principle B3: ACL here
def _build_payload(self, request: JobRequest) -> dict:
return {"prompt": request.prompt, "model": request.model_id, **request.params}
def _parse_response(self, data: dict, request: JobRequest) -> JobResult:
# ACL: validate and map to our domain type. Never return raw data.
try:
url = data["output"]["url"] # adjust to actual provider shape
cost = Decimal(str(data.get("cost", "0")))
except (KeyError, TypeError) as e:
raise ProviderInvalidResponseError(
message=f"Unexpected response shape: {e}",
provider=self.provider_name,
retryable=False,
raw_response=data,
) from e
return JobResult(
url=url,
cost_usd=cost,
provider=self.provider_name,
model_id=request.model_id,
)
def _map_http_error(self, e: httpx.HTTPStatusError) -> None:
if e.response.status_code == 429:
retry_after = int(e.response.headers.get("Retry-After", 60))
raise ProviderRateLimitError(
message="Rate limited",
provider=self.provider_name,
retryable=True,
retry_after=retry_after,
) from e
raise ProviderError(
message=f"HTTP {e.response.status_code}",
provider=self.provider_name,
retryable=e.response.status_code >= 500,
) from e---
`get_provider_client("<name>")` auto-creates an isolated `httpx.AsyncClient` on first call with the limits from `settings.HTTP_MAX_CONNECTIONS`. No extra step needed unless the provider needs custom limits:
# app/core/bulkhead.py — only if non-default limits needed
_PROVIDER_LIMITS = {
"<name>": httpx.Limits(max_connections=5, max_keepalive_connections=2),
}---
Create a fixture with a real (or realistic) provider response:
touch reference/tests/fixtures/<name>_response.json
{
"output": { "url": "https://cdn.example.com/result.png" },
"cost": "0.004",
"id": "job_abc123"
}Add a parser test in `tests/integration/test_provider_contracts.py`:
def test_<name>_response_parses_to_job_result(load_fixture):
raw = load_fixture("<name>_response.json")
provider = <Name>Provider()
request = JobRequest(model_id="m", prompt="p", modality="image",
idempotency_key="k")
result = provider._parse_response(raw, request)
assert isinstance(result, JobResult)
assert result.url.startswith("https://")
assert result.cost_usd >= Decimal("0")This test breaks in CI when the provider changes their response shape. That's the point.
---
# app/providers/__init__.py from app.providers.<name> import <Name>Provider __all__ = [..., "<Name>Provider"]
---
| Principle | Check | |-----------|-------| | B3 — ACL: returns JobResult not dict | `_parse_response` returns `JobResult` | | B5 — Bulkhead: isolated client | uses `get_provider_client(self.provider_name)` | | B6 — Idempotency: key forwarded | `headers={"X-Idempotency-Key": request.idempotency_key}` | | B7 — Observability: context set | `provider_ctx.set(self.provider_name)` before I/O | | B9 — Contract test exists | fixture + parser test in
54 production architecture principles your AI coding agent (Claude Code, Cursor) follows automatically. Drop-in CLAUDE.md, .cursor/rules/, and .claude/skills/ for FastAPI, Next.js 15, and Go 1.22+. MIT.
Repo: yerdaulet-damir/vibe-coding-rules
Systematic 5-step backend debugging flow for AI-coded FastAPI apps. Load this skill when a bug is reported, a test fails, or unexpected behavior appears in any…
Systematic 5-step debugging flow for Next.js 15 + React 19 + TypeScript apps. Load when a UI bug is reported, hydration error appears, Server Action returns…
Systematic 5-step debugging flow for Go 1.22+ services. Load when a test fails, a goroutine leaks, a downstream provider hangs, errors lose context, or…
Pre-flight checklist for adding a new feature to a Go 1.22+ service. Load when creating a new endpoint, internal package, external integration, or background…
Pre-flight checklist for adding a new feature to a Next.js 15 + TypeScript app. Load when creating a new page, route, server action, or feature module. Forces…
Pre-flight checklist that must complete before writing the first line of code for any new feature. Load this skill when starting a new endpoint, service…