Compare commits

...

1 Commits

Author SHA1 Message Date
Devin AI
10c63f7cac docs: add repo-local AGENTS.md with deterministic validation workflow guidance
Closes #4564

- Add AGENTS.md at repo root with contributor guidance for AI coding assistants
- Document repository layout, environment setup, and workspace structure
- Include deterministic validation workflow: ruff lint, ruff format, mypy, pytest
- Define safety boundaries (secrets, VCR cassettes, network blocking, tests)
- Document CI workflows, commit conventions, testing conventions, and code style
- Add 22 regression tests validating AGENTS.md content (sections, commands, safety rules)

Co-Authored-By: João <joao@crewai.com>
2026-02-23 14:02:09 +00:00
2 changed files with 362 additions and 0 deletions

233
AGENTS.md Normal file
View File

@@ -0,0 +1,233 @@
# AGENTS.md -- crewAI Repository Contributor Guide for AI Coding Assistants
> Repo-local instructions for AI coding assistants (Claude Code, Cursor,
> Windsurf, GitHub Copilot, Devin, Codex, etc.) contributing to the **crewAI
> monorepo**. Follow these deterministic validation steps before opening or
> updating any pull request.
---
## Repository Layout
```
crewAI/ # workspace root (uv workspace)
pyproject.toml # workspace-level config (ruff, mypy, pytest, uv)
conftest.py # shared pytest fixtures (VCR, event cleanup, env)
uv.lock
lib/
crewai/ # core framework package
src/crewai/ # source code
tests/ # unit & integration tests
pyproject.toml
crewai-tools/ # tool integrations package
src/crewai_tools/ # source code
tests/ # unit & integration tests
pyproject.toml
crewai-files/ # file-processing package
src/crewai_files/
tests/
pyproject.toml
devtools/ # internal developer tooling (lib/devtools)
src/
pyproject.toml
docs/ # documentation site content
.github/workflows/ # CI definitions (lint, tests, type-check)
```
Key conventions:
- **uv workspace** -- all packages are managed via `uv` with workspace members
declared in the root `pyproject.toml`.
- Source lives under `lib/<package>/src/`, tests under `lib/<package>/tests/`.
- Shared test configuration (fixtures, VCR cassette filtering) is in the
root `conftest.py`.
---
## Environment Setup
```bash
# 1. Install uv (if not already installed)
curl -LsSf https://astral.sh/uv/install.sh | sh
# 2. Install all workspace dependencies (including dev tools)
uv sync --all-groups --all-extras
# 3. Verify the virtual environment
uv run python -c "import crewai; print(crewai.__version__)"
```
Python version requirement: `>=3.10, <3.14` (see root `pyproject.toml`).
---
## Deterministic Validation Workflow
Run these checks **in order** before pushing commits. Every command must exit
with code 0.
### 1. Lint (ruff)
```bash
# Check for lint errors on changed files (mirrors CI)
uv run ruff check --config pyproject.toml <files>
# Or check all source files
uv run ruff check --config pyproject.toml lib/
```
The CI job runs ruff only on changed Python files, excluding
`lib/crewai/src/crewai/cli/templates/` and `*/tests/` directories.
Configuration: root `pyproject.toml` `[tool.ruff]` section.
### 2. Format (ruff)
```bash
# Check formatting (dry-run)
uv run ruff format --check --config pyproject.toml lib/
# Auto-fix formatting
uv run ruff format --config pyproject.toml <files>
```
### 3. Type Check (mypy)
```bash
# Run mypy on changed source files (excluding tests and templates)
uv run mypy --config-file pyproject.toml <files>
```
CI runs mypy only on changed files under `lib/*/src/` (not tests).
Configuration: root `pyproject.toml` `[tool.mypy]` section with
`strict = true`.
### 4. Tests (pytest)
```bash
# Run the full crewai test suite
cd lib/crewai && uv run pytest -vv --maxfail=3
# Run the crewai-tools test suite
cd lib/crewai-tools && uv run pytest -vv --maxfail=3
# Run the crewai-files test suite
cd lib/crewai-files && uv run pytest -vv --maxfail=3
# Run a single test file
cd lib/crewai && uv run pytest tests/path/to/test_file.py -vv
# Run a single test
cd lib/crewai && uv run pytest tests/path/to/test_file.py::test_function -vv
```
Test configuration (root `pyproject.toml` `[tool.pytest.ini_options]`):
- `asyncio_mode = "strict"` -- async tests require explicit
`@pytest.mark.asyncio`.
- `--block-network` -- network calls are blocked by default; use VCR
cassettes for HTTP interactions.
- `--timeout=60` -- per-test timeout of 60 seconds.
- `-n auto` -- parallel execution via pytest-xdist.
- `--dist=loadfile` -- tests from the same file run in the same worker.
### 5. Pre-commit (optional local check)
```bash
uv run pre-commit run --all-files
```
Runs ruff lint, ruff format, mypy, uv-lock consistency, and commitizen
checks.
---
## CI Workflows (GitHub Actions)
Pull requests trigger these workflows automatically:
| Workflow | File | What it checks |
|----------|------|----------------|
| **Lint** | `.github/workflows/linter.yml` | `ruff check` on changed `.py` files |
| **Tests** | `.github/workflows/tests.yml` | `pytest` across Python 3.10--3.13, split into 8 groups |
| **Type Check** | `.github/workflows/type-checker.yml` | `mypy` on changed source files across Python 3.10--3.13 |
All three must pass before merge.
---
## Safety Boundaries
- **Never commit secrets.** API keys, tokens, and credentials belong in
`.env` (git-ignored) or environment variables. The `.env.test` file
provides safe placeholder values for the test suite.
- **Never modify VCR cassettes by hand.** Cassettes are auto-generated YAML
recordings of HTTP interactions. To re-record, set
`PYTEST_VCR_RECORD_MODE=all` and run the relevant tests. Cassette paths
mirror the test directory structure under `tests/cassettes/`.
- **Never modify tests just to make them pass.** If a test fails, fix the
source code or report the issue. Do not weaken assertions, add
`pytest.skip`, or catch exceptions to hide failures.
- **Respect `--block-network`.** Tests run with network blocking enabled.
All external HTTP calls must use VCR cassettes. Do not disable this.
- **Do not add new dependencies without justification.** The workspace pins
dependencies tightly. Any new dependency needs explicit rationale and must
be added via `uv add`, not by editing `pyproject.toml` by hand.
- **Do not push directly to `main`.** All changes go through pull requests.
---
## Commit Convention
This repository uses [Commitizen](https://commitizen-tools.github.io/commitizen/)
with [Conventional Commits](https://www.conventionalcommits.org/):
```
<type>(<scope>): <subject>
# Examples:
feat(agent): add inject_date parameter
fix(llm): handle empty response from Bedrock
docs: update AGENTS.md validation commands
test(crew): add hierarchical process edge case
refactor(flow): simplify state persistence logic
```
Valid types: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`,
`build`, `ci`, `chore`, `revert`.
The `commitizen` pre-commit hook validates commit messages on push.
---
## Testing Conventions
- **Test file naming:** `test_*.py` in the appropriate `tests/` directory.
- **Test function naming:** `test_*` prefix.
- **Test class naming:** `Test*` prefix.
- **Fixtures:** Shared fixtures live in the root `conftest.py`. Package-
specific fixtures go in `lib/<package>/tests/conftest.py`.
- **VCR cassettes:** Use `@pytest.mark.vcr` for tests that make HTTP calls.
Cassettes are stored in `tests/cassettes/` mirroring the test directory
structure.
- **Async tests:** Must be decorated with `@pytest.mark.asyncio` (strict
mode).
- **Telemetry tests:** Mark with `@pytest.mark.telemetry` to opt out of
the default telemetry mocking.
---
## Code Style
- **Linter/formatter:** ruff (config in root `pyproject.toml`).
- **Type checker:** mypy with `strict = true`.
- **Imports:** Absolute imports only (`ban-relative-imports = "all"`).
Sorted by isort rules. No unused imports.
- **Line length:** No hard limit enforced (`E501` ignored), but keep lines
reasonable.
- **Print statements:** Disallowed in source code (ruff rule `T`). Use
proper logging.
- **Security:** Bandit rules enforced (ruff rule `S`). `assert` statements
are allowed in test files only.
- **Type annotations:** Required on all function signatures in source code
(mypy `disallow_untyped_defs = true`). Use `X | Y` union syntax (not
`Optional` or `Union`), `collections.abc` types, and built-in generics
(`list`, `dict` instead of `typing.List`, `typing.Dict`).

View File

@@ -0,0 +1,129 @@
"""Regression tests for the repo-local AGENTS.md file.
Validates that AGENTS.md exists at the repository root and contains the
required sections and validation commands so that contributors and
automation agents have deterministic, machine-readable guidance.
Addresses: https://github.com/crewAIInc/crewAI/issues/4564
"""
from pathlib import Path
import pytest
REPO_ROOT = Path(__file__).resolve().parents[3]
AGENTS_MD = REPO_ROOT / "AGENTS.md"
@pytest.fixture()
def agents_md_content() -> str:
"""Read and return the full text of the repo-root AGENTS.md."""
assert AGENTS_MD.is_file(), (
f"AGENTS.md not found at repository root ({REPO_ROOT}). "
"See https://github.com/crewAIInc/crewAI/issues/4564"
)
return AGENTS_MD.read_text(encoding="utf-8")
class TestAgentsMdExists:
"""AGENTS.md must be present at the repository root."""
def test_agents_md_file_exists(self) -> None:
assert AGENTS_MD.is_file(), (
f"Expected AGENTS.md at {REPO_ROOT}. "
"This file is required for contributor and automation-agent guidance."
)
def test_agents_md_is_not_empty(self, agents_md_content: str) -> None:
assert len(agents_md_content.strip()) > 0, "AGENTS.md must not be empty."
class TestAgentsMdRequiredSections:
"""AGENTS.md must contain sections covering validation, safety, and workflow."""
REQUIRED_HEADINGS = [
"Repository Layout",
"Environment Setup",
"Deterministic Validation Workflow",
"Safety Boundaries",
"Commit Convention",
"Testing Conventions",
"Code Style",
"CI Workflows",
]
@pytest.mark.parametrize("heading", REQUIRED_HEADINGS)
def test_required_section_present(
self,
agents_md_content: str,
heading: str,
) -> None:
assert heading in agents_md_content, (
f"AGENTS.md is missing required section: '{heading}'"
)
class TestAgentsMdValidationCommands:
"""AGENTS.md must document the exact validation commands used in CI."""
REQUIRED_COMMANDS = [
"uv run ruff check",
"uv run ruff format",
"uv run mypy",
"uv run pytest",
]
@pytest.mark.parametrize("command", REQUIRED_COMMANDS)
def test_validation_command_documented(
self,
agents_md_content: str,
command: str,
) -> None:
assert command in agents_md_content, (
f"AGENTS.md must document the validation command: '{command}'"
)
class TestAgentsMdSafetyRules:
"""AGENTS.md must include key safety rules."""
SAFETY_KEYWORDS = [
"Never commit secrets",
"Never modify VCR cassettes by hand",
"Never modify tests just to make them pass",
"--block-network",
]
@pytest.mark.parametrize("keyword", SAFETY_KEYWORDS)
def test_safety_rule_present(
self,
agents_md_content: str,
keyword: str,
) -> None:
assert keyword in agents_md_content, (
f"AGENTS.md is missing safety rule containing: '{keyword}'"
)
class TestAgentsMdWorkspaceStructure:
"""AGENTS.md must accurately describe the workspace layout."""
WORKSPACE_MEMBERS = [
"lib/crewai/",
"lib/crewai-tools/",
"lib/crewai-files/",
"lib/devtools/",
]
@pytest.mark.parametrize("member", WORKSPACE_MEMBERS)
def test_workspace_member_documented(
self,
agents_md_content: str,
member: str,
) -> None:
# Normalize: the file may reference paths with or without trailing /
member_stem = member.rstrip("/")
assert member_stem in agents_md_content, (
f"AGENTS.md must document workspace member: '{member_stem}'"
)