Refactors the Claude Code skills for a cleaner, more intuitive dev loop. ### Changes 🏗️ - **`/pr-review` (new)**: Actual code review skill — reads the PR diff, fetches existing comments to avoid duplicates, and posts inline GitHub comments with structured feedback (Blockers / Should Fix / Nice to Have / Nit) covering correctness, security, code quality, architecture, and testing. - **`/pr-address` (was `/babysit-pr`)**: Addresses review comments and monitors CI until green. Renamed from `/babysit-pr` to `/pr-address` to better reflect its purpose. Handles bot-specific feedback (autogpt-reviewer, sentry, coderabbitai) and loops until all comments are addressed and CI is green. - **`/backend-check` + `/frontend-check` → `/check`**: Unified into a single `/check` skill that auto-detects whether backend (Python) or frontend (TypeScript) code changed and runs the appropriate formatting, linting, type checking, and tests. Shared code quality rules applied to both. - **`/code-style` enhanced**: Now covers both Python and TypeScript/React. Added learnings from real PR work: lazy `%s` logging, TOCTOU awareness, SSE protocol rules (`data:` vs `: comment`), FastAPI `Security()` vs `Depends()`, Redis pipeline atomicity, error path sanitization, mock target rules after refactoring. - **`/worktree` fixed**: Normal `git worktree` is now the default (was branchlet-first). Branchlet moved to optional section. All paths derived from `git rev-parse --show-toplevel`. - **`/pr-create`, `/openapi-regen`, `/new-block` cleaned up**: Reference `/check` and `/code-style` instead of duplicating instructions. ### Checklist 📋 #### For code changes: - [x] I have clearly listed my changes in the PR description - [x] I have made a test plan - [x] I have tested my changes according to the test plan: - [x] Verified all skill files parse correctly (valid YAML frontmatter) - [x] Verified skill auto-detection triggers updated in descriptions - [x] Verified old backend-check and frontend-check directories removed - [x] Verified pr-review and pr-address directories created with correct content
7.7 KiB
CLAUDE.md - Backend
This file provides guidance to Claude Code when working with the backend.
Essential Commands
To run something with Python package dependencies you MUST use poetry run ....
# Install dependencies
poetry install
# Run database migrations
poetry run prisma migrate dev
# Start all services (database, redis, rabbitmq, clamav)
docker compose up -d
# Run the backend as a whole
poetry run app
# Run tests
poetry run test
# Run specific test
poetry run pytest path/to/test_file.py::test_function_name
# Run block tests (tests that validate all blocks work correctly)
poetry run pytest backend/blocks/test/test_block.py -xvs
# Run tests for a specific block (e.g., GetCurrentTimeBlock)
poetry run pytest 'backend/blocks/test/test_block.py::test_available_blocks[GetCurrentTimeBlock]' -xvs
# Lint and format
# prefer format if you want to just "fix" it and only get the errors that can't be autofixed
poetry run format # Black + isort
poetry run lint # ruff
More details can be found in @TESTING.md
Creating/Updating Snapshots
When you first write a test or when the expected output changes:
poetry run pytest path/to/test.py --snapshot-update
⚠️ Important: Always review snapshot changes before committing! Use git diff to verify the changes are expected.
Architecture
- API Layer: FastAPI with REST and WebSocket endpoints
- Database: PostgreSQL with Prisma ORM, includes pgvector for embeddings
- Queue System: RabbitMQ for async task processing
- Execution Engine: Separate executor service processes agent workflows
- Authentication: JWT-based with Supabase integration
- Security: Cache protection middleware prevents sensitive data caching in browsers/proxies
Code Style
- Top-level imports only — no local/inner imports (lazy imports only for heavy optional deps like
openpyxl) - No duck typing — no
hasattr/getattr/isinstancefor type dispatch; use typed interfaces/unions/protocols - Pydantic models over dataclass/namedtuple/dict for structured data
- No linter suppressors — no
# type: ignore,# noqa,# pyright: ignore; fix the type/code - List comprehensions over manual loop-and-append
- Early return — guard clauses first, avoid deep nesting
- Lazy
%slogging —logger.info("Processing %s items", count)notlogger.info(f"Processing {count} items") - Sanitize error paths —
os.path.basename()in error messages to avoid leaking directory structure - TOCTOU awareness — avoid check-then-act patterns for file access and credit charging
Security()vsDepends()— useSecurity()for auth deps to get proper OpenAPI security spec- Redis pipelines —
transaction=Truefor atomicity on multi-step operations max(0, value)guards — for computed values that should never be negative- SSE protocol —
data:lines for frontend-parsed events (must match Zod schema),: commentlines for heartbeats/status - File length — keep files under ~300 lines; if a file grows beyond this, split by responsibility (e.g. extract helpers, models, or a sub-module into a new file). Never keep appending to a long file.
- Function length — keep functions under ~40 lines; extract named helpers when a function grows longer. Long functions are a sign of mixed concerns, not complexity.
Testing Approach
- Uses pytest with snapshot testing for API responses
- Test files are colocated with source files (
*_test.py) - Mock at boundaries — mock where the symbol is used, not where it's defined
- After refactoring, update mock targets to match new module paths
- Use
AsyncMockfor async functions (from unittest.mock import AsyncMock)
Database Schema
Key models (defined in schema.prisma):
User: Authentication and profile dataAgentGraph: Workflow definitions with version controlAgentGraphExecution: Execution history and resultsAgentNode: Individual nodes in a workflowStoreListing: Marketplace listings for sharing agents
Environment Configuration
- Backend:
.env.default(defaults) →.env(user overrides)
Common Development Tasks
Adding a new block
Follow the comprehensive Block SDK Guide which covers:
- Provider configuration with
ProviderBuilder - Block schema definition
- Authentication (API keys, OAuth, webhooks)
- Testing and validation
- File organization
Quick steps:
- Create new file in
backend/blocks/ - Configure provider using
ProviderBuilderin_config.py - Inherit from
Blockbase class - Define input/output schemas using
BlockSchema - Implement async
runmethod - Generate unique block ID using
uuid.uuid4() - Test with
poetry run pytest backend/blocks/test/test_block.py
Note: when making many new blocks analyze the interfaces for each of these blocks and picture if they would go well together in a graph-based editor or would they struggle to connect productively? ex: do the inputs and outputs tie well together?
If you get any pushback or hit complex block conditions check the new_blocks guide in the docs.
Handling files in blocks with store_media_file()
When blocks need to work with files (images, videos, documents), use store_media_file() from backend.util.file. The return_format parameter determines what you get back:
| Format | Use When | Returns |
|---|---|---|
"for_local_processing" |
Processing with local tools (ffmpeg, MoviePy, PIL) | Local file path (e.g., "image.png") |
"for_external_api" |
Sending content to external APIs (Replicate, OpenAI) | Data URI (e.g., "data:image/png;base64,...") |
"for_block_output" |
Returning output from your block | Smart: workspace:// in CoPilot, data URI in graphs |
Examples:
# INPUT: Need to process file locally with ffmpeg
local_path = await store_media_file(
file=input_data.video,
execution_context=execution_context,
return_format="for_local_processing",
)
# local_path = "video.mp4" - use with Path/ffmpeg/etc
# INPUT: Need to send to external API like Replicate
image_b64 = await store_media_file(
file=input_data.image,
execution_context=execution_context,
return_format="for_external_api",
)
# image_b64 = "data:image/png;base64,iVBORw0..." - send to API
# OUTPUT: Returning result from block
result_url = await store_media_file(
file=generated_image_url,
execution_context=execution_context,
return_format="for_block_output",
)
yield "image_url", result_url
# In CoPilot: result_url = "workspace://abc123"
# In graphs: result_url = "data:image/png;base64,..."
Key points:
for_block_outputis the ONLY format that auto-adapts to execution context- Always use
for_block_outputfor block outputs unless you have a specific reason not to - Never hardcode workspace checks - let
for_block_outputhandle it
Modifying the API
- Update route in
backend/api/features/ - Add/update Pydantic models in same directory
- Write tests alongside the route file
- Run
poetry run testto verify
Security Implementation
Cache Protection Middleware
- Located in
backend/api/middleware/security.py - Default behavior: Disables caching for ALL endpoints with
Cache-Control: no-store, no-cache, must-revalidate, private - Uses an allow list approach - only explicitly permitted paths can be cached
- Cacheable paths include: static assets (
static/*,_next/static/*), health checks, public store pages, documentation - Prevents sensitive data (auth tokens, API keys, user data) from being cached by browsers/proxies
- To allow caching for a new endpoint, add it to
CACHEABLE_PATHSin the middleware - Applied to both main API server and external API applications