Clone your fork locally:
git clone https://github.com/YOUR_USERNAME/salesagent.git
cd salesagent
Install pre-commit hooks:
uv run pre-commit install
Create a feature branch from main:
git checkout -b feat/my-new-feature
All development work happens on feature branches created from main. Branch names must use one of the following prefixes:
| Prefix | Use Case | Example |
|---|---|---|
feat/ |
New features or capabilities | feat/audio-creative-support |
fix/ |
Bug fixes | fix/budget-validation-negative |
docs/ |
Documentation-only changes | docs/update-migration-guide |
refactor/ |
Code restructuring (no behavior change) | refactor/extract-pricing-service |
chore/ |
Tooling, CI, dependencies, maintenance | chore/upgrade-sqlalchemy-2.1 |
test/ |
Adding or improving tests | test/add-delivery-edge-cases |
main.main.main.All commit messages must follow the Conventional Commits specification. This enables automated changelog generation and version bumping through release-please.
<type>: <short description>
<optional body>
<optional footer>
| Type | Description | Version Bump |
|---|---|---|
feat |
A new feature | Minor (0.x.0) |
fix |
A bug fix | Patch (0.0.x) |
docs |
Documentation only | None |
refactor |
Code change that neither fixes a bug nor adds a feature | None |
chore |
Maintenance tasks (CI, deps, config) | None |
test |
Adding or correcting tests | None |
perf |
Performance improvement | Patch (0.0.x) |
For breaking changes, add ! after the type or include BREAKING CHANGE: in the footer:
feat!: rename format_ids to format_identifiers across all schemas
BREAKING CHANGE: The `format_ids` field in Product and Creative schemas
has been renamed to `format_identifiers`. Clients must update their
request payloads.
Breaking changes trigger a major version bump (x.0.0).
feat: add audio creative format support to sync_creatives
Adds validation and processing for audio creative formats (MP3, WAV, FLAC)
in the sync_creatives tool. Includes format-specific duration and bitrate
validation.
fix: prevent negative budget values in create_media_buy
The create_media_buy _impl function now raises AdCPValidationError when
the budget is zero or negative, instead of passing the invalid value
through to the adapter.
Closes #142
chore: upgrade ruff to 0.8.0 and fix new lint rules
test: add structural guard for adapter capabilities declaration
All code must target Python 3.12+. Use modern Python features including:
str | None union syntax (not Optional[str]).list[str] lowercase generics (not List[str]).match statements where appropriate.The project uses Ruff for both linting and formatting, replacing Black, Flake8, and isort. Configuration is in pyproject.toml.
# Check for lint errors
uv run ruff check src/ tests/
# Auto-fix fixable issues
uv run ruff check --fix src/ tests/
# Format code
uv run ruff format src/ tests/
# Check formatting without modifying files
uv run ruff format --check src/ tests/
Static type analysis is enforced using mypy. Configuration is in pyproject.toml.
uv run mypy src/
All new code must pass mypy without errors. Common patterns:
# Good: explicit types
async def get_products_impl(
identity: ResolvedIdentity,
brief: str | None = None,
channels: list[str] | None = None,
) -> list[Product]:
...
# Good: TypedDict for complex dictionaries
class DeliveryMetrics(TypedDict):
impressions: int
spend: float
ctr: float
pacing: float
The project uses pre-commit to run checks automatically before each commit. The configuration is in .pre-commit-config.yaml.
uv run pre-commit install
| Hook | What It Does |
|---|---|
| ruff (lint) | Checks for code quality issues and auto-fixes where possible |
| ruff (format) | Ensures consistent code formatting |
| mypy | Runs static type checking |
| trailing-whitespace | Removes trailing whitespace |
| end-of-file-fixer | Ensures files end with a newline |
| check-yaml | Validates YAML file syntax |
| check-json | Validates JSON file syntax |
| check-merge-conflict | Prevents committing merge conflict markers |
# Run all hooks on all files
uv run pre-commit run --all-files
# Run a specific hook
uv run pre-commit run ruff --all-files
If you must bypass pre-commit in an emergency:
git commit --no-verify -m "fix: emergency hotfix for production outage"
When contributing code, adhere to these architectural rules. Structural guard tests enforce these automatically.
All three protocols (MCP, A2A, REST) must call the same _impl business logic functions. Never put business logic in a transport-specific handler.
# CORRECT: business logic in _impl, transport just resolves identity
# src/core/tools/products.py (MCP wrapper)
@mcp.tool()
async def get_products(ctx: Context, brief: str = None):
identity = await resolve_identity(ctx)
return await get_products_impl(identity, brief=brief)
# WRONG: business logic in the MCP handler
@mcp.tool()
async def get_products(ctx: Context, brief: str = None):
identity = await resolve_identity(ctx)
# DON'T DO THIS -- this logic should be in _impl
async with get_db_session() as session:
products = await session.execute(select(Product))
...
All _impl functions must accept ResolvedIdentity as their identity parameter, never transport-specific types like FastMCP Context, Flask g, or FastAPI Request.
# CORRECT
async def create_media_buy_impl(identity: ResolvedIdentity, **kwargs):
...
# WRONG
async def create_media_buy_impl(ctx: Context, **kwargs):
...
The _impl layer must raise AdCPError subclasses (e.g., AdCPValidationError, AdCPNotFoundError). Never raise transport-specific errors like ToolError from _impl code.
# CORRECT
from src.core.errors import AdCPValidationError
async def create_media_buy_impl(identity, budget):
if budget <= 0:
raise AdCPValidationError("Budget must be positive", recovery="correctable")
# WRONG
from fastmcp.exceptions import ToolError
async def create_media_buy_impl(identity, budget):
if budget <= 0:
raise ToolError("Budget must be positive") # transport-specific!
When adding a new parameter to an _impl function, update all three transport wrappers (MCP tool, A2A handler, REST endpoint) to accept and forward the new parameter. The test_architecture_boundary_completeness.py guard test catches missing parameters.
Complete this checklist before opening a pull request:
uv run ruff check src/ tests/ with no errors.uv run ruff format --check src/ tests/ with no changes needed.uv run mypy src/ with no errors.uv run pytest tests/unit.feat/, fix/, etc.).Every pull request must include:
pr-title-check.yml).ipr-agreement.yml).main.The following GitHub Actions workflows run automatically:
| Workflow | Trigger | Purpose |
|---|---|---|
test.yml |
Every PR and push to main |
Runs linting, type checking, unit tests, and integration tests |
pr-title-check.yml |
Every PR | Validates PR title follows Conventional Commits format |
ipr-agreement.yml |
Every PR | Verifies the contributor has signed the IPR agreement |
release-please.yml |
Push to main |
Manages automated releases and changelog generation |
The test workflow runs these steps in order:
uv sync.ruff check.mypy.pytest tests/unit.pytest tests/integration (using a PostgreSQL service container).The project uses release-please for automated releases. When commits are merged to main, release-please:
| Commit Type | Version Bump | Example |
|---|---|---|
feat |
Minor (0.1.0) | New tool, new adapter, new feature |
fix |
Patch (0.0.1) | Bug fix, error correction |
feat! or BREAKING CHANGE |
Major (1.0.0) | Schema change, API change, removed feature |
docs, chore, refactor, test |
None | No release triggered |
In exceptional cases, maintainers can trigger a manual release by updating the version in pyproject.toml and creating a git tag:
git tag v1.2.3
git push origin v1.2.3
Looking for areas to contribute? Here are the main areas of the codebase:
| Area | Path | Description |
|---|---|---|
| MCP Tools | src/core/tools/ |
Add new tools or improve existing tool implementations |
| Ad Server Adapters | src/adapters/ |
Add support for new ad servers or improve existing adapters |
| Admin UI | src/admin/blueprints/ |
Improve the publisher administration interface |
| AI Agents | src/services/ai/ |
Improve AI ranking, review, naming, or policy agents |
| Schemas | src/core/schemas/ |
Extend or refine Pydantic request/response models |
| Documentation | This site | Improve developer docs, tutorials, and tool references |
| Tests | tests/ |
Increase coverage, add edge case tests, improve test utilities |
| Database | src/core/database/ |
Optimize queries, add indexes, improve session management |
good first issue or help wanted on the GitHub repository. These issues are curated for new contributors.