Files
cleveragents-core/pyproject.toml
T
drew 36b133ec5e feat(controller): Phase 1b — DB schema + dequeue helper + payload guard
Five SQLAlchemy 2.0 declarative models implementing plan v6/v9's
unified workflow schema. Cross-dialect (SQLite for tests + local dev,
Postgres for multi-machine production). Lock columns on
workflow_attempts implement the multi-machine-safe dequeue protocol
(plan v5).

Modules:

- tools/controller/db/models.py:
  - Workflow (kind discriminator pr/issue, unique on owner+repo+kind+
    entity_number, parent_workflow_id FK for issue→PR linkage)
  - WorkflowAttempt (status/locked_by_instance/locked_at/
    lock_heartbeat_at/lock_ttl_seconds/pickup_count + CHECK
    constraints on status enum and pickup_count≥0; partial indexes
    on the pending/in_progress/complete hot paths)
  - ControllerEvent (Forgejo-write replay support kept in-schema even
    though v9 simplified to Forgejo-first protocol; allows v3-style
    upgrade later without migration)
  - FlakeHistory (composite PK; supports the v6 flake-learning
    heuristic)
  - CIObservation (raw CI state history; 90-day retention to be
    enforced by a sweep task)
  - AutoincrementPk variant (Integer on SQLite where it autoincrements
    via rowid; BigInteger on Postgres for BIGSERIAL); JsonColumn
    variant (JSON on SQLite, JSONB on Postgres)

- tools/controller/db/session.py: build_engine (per-dialect tuning —
  SQLite WAL + foreign_keys + busy_timeout; Postgres pool_pre_ping);
  create_all (idempotent); session_scope (transactional context).

- tools/controller/db/dequeue.py: dequeue_one (one row atomically;
  Postgres path uses SELECT FOR UPDATE SKIP LOCKED, SQLite path uses
  UPDATE-WHERE-id-IN-SELECT-LIMIT-1 with RETURNING). Bumps pickup_count
  on dequeue; respects max_pickups guard (default 3 per v6 blocker fix).
  Returns DequeueResult dataclass with role/tier/pickup_count and
  reason on miss.

- tools/controller/db/payload_guard.py: enforce_input_payload_size
  with 4MB cap and 5-step truncation priority (older_summary → oldest
  verbatim → comments → full_diff → CI failure excerpts). Raises
  PayloadTooLargeError after all steps exhausted; master maps to
  workflow STUCK with reason='input-too-large'.

- pyproject.toml: new optional extras `controller-db` pinning
  sqlalchemy + psycopg2-binary (latter installed only for prod
  multi-machine deploy; tests use stdlib sqlite3 via SQLAlchemy's
  SQLite dialect which is already pulled in transitively via alembic).

37 new tests across test_db_schema/dequeue/payload_guard; 141
controller tests total; full auto_agents suite 2503 pass (no
regressions).
2026-05-18 13:01:55 -04:00

268 lines
7.9 KiB
TOML

[build-system]
requires = ["hatchling>=1.21.0"]
build-backend = "hatchling.build"
[project]
name = "cleveragents"
version = "1.0.0"
description = "CleverAgents CLI and runtime toolkit"
readme = "README.md"
requires-python = ">=3.13"
license = {text = "MIT"}
authors = [
{name = "CleverThis Engineering", email = "engineering@cleverthis.com"},
]
keywords = ["cleveragents", "ai", "cli", "automation"]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.13",
]
dependencies = [
# CLI Framework (ADR-009)
"typer>=0.9.0",
"uvicorn>=0.30.1",
"watchdog>=4.0.0",
"faiss-cpu>=1.7.4", # Vector store backend
"rx>=3.2.0", # Reactive streams for routing
"dependency-injector>=4.41.0", # DI container
"pydantic>=2.7.0",
"pydantic-settings>=2.11.0",
"structlog>=24.4.0",
"langchain>=0.2.14",
"langchain-anthropic>=0.2.0",
"langchain-community>=0.2.14",
"langchain-openai>=0.2.0",
"langchain-google-genai>=0.2.0",
"jinja2>=3.1.0",
"alembic>=1.13.1",
"numpy>=2.1.0",
"python-ulid>=2.7.0", # ULID generation for plan/action IDs
"RestrictedPython>=7.0", # Secure sandbox for user-supplied code
"jsonschema>=4.20.0", # JSON Schema validation for tool inputs/outputs
"tomlkit>=0.13.0", # TOML writing with comment preservation for config CLI
"tenacity>=8.2.0", # Retry framework for service layer resilience
"aiohttp>=3.13.4", # CVE-2026-34515 mitigation: open redirect vulnerability
"a2a-sdk>=0.3.0,<1.0.0", # A2A Python SDK — required transport for local (stdio) and server (HTTP) modes (ADR-047); pinned <1.0.0 (removed legacy A2AClient)
]
[project.optional-dependencies]
tui = [
"textual>=1.0.0,<2.0.0",
]
dev = [
# Code formatting and linting
"ruff>=0.15.0,<0.16.0",
# Type checking
"pyright>=1.1.350",
"types-pyyaml>=6.0.0",
"types-aiofiles>=23.0.0",
# Testing
"behave==1.3.3",
"pytest>=8.0.0",
"pytest-asyncio>=0.23.0",
"pytest-cov>=4.1.0",
"pytest-httpserver>=1.1.0", # Used by tests/auto_agents/test_opencode_worker.py to fake the OpenCode HTTP API.
# Pre-commit hooks
"pre-commit>=3.6.0",
# Security scanning
"bandit[toml]>=1.7.5",
"semgrep>=1.60.0",
# Dead code detection
"vulture>=2.10",
# Complexity metrics
"radon>=6.0.1",
]
tests = [
"behave==1.3.3",
"slipcover>=1.0.17",
"asv>=0.6.5",
"robotframework>=7.3.2",
"robotframework-pabot>=4.0.0",
"faker>=20.0.0", # Dynamic test data generation
]
docs = [
"mkdocs>=1.6.1",
"mkdocs-material>=9.6.0",
"mkdocstrings[python]>=0.24.0",
"mkdocs-kroki-plugin>=1.2.0",
"mkdocs-gen-files>=0.5.0",
"mkdocs-literate-nav>=0.6.0",
"griffe-pydantic>=1.0.0",
"mkdocs-click>=0.8.0",
"ruff>=0.4.0",
]
mcp-servers = [
# Runtime for tools/mcp_*_server.py (graphify, ci, forgejo, git) —
# spawned by OpenCode via `.opencode/opencode.json`'s `mcp` block.
# Optional because only operators running the OpenCode worker
# pipeline need them; library users / tests do not.
"mcp>=1.0.0",
]
controller-db = [
# Postgres driver for the controller's production DB layer
# (tools/controller/db/). Optional because tests + local dev use
# SQLite (stdlib sqlite3 + the SQLAlchemy abstraction already
# pulled in transitively via alembic). Operators deploying the
# controller on multiple machines install this extras.
"sqlalchemy>=2.0",
"psycopg2-binary>=2.9",
]
[project.urls]
Homepage = "https://cleverthis.com/cleveragents"
Documentation = "https://docs.cleverthis.com/cleveragents"
Repository = "https://git.cleverthis.com/cleveragents/core"
Issues = "https://git.cleverthis.com/cleveragents/core/issues"
[project.scripts]
cleveragents = "cleveragents.cli:main"
agents = "cleveragents.cli:main"
[tool.hatch.build.targets.wheel]
packages = ["src/cleveragents"]
include = [
"src/cleveragents/py.typed",
"src/cleveragents/infrastructure/database/migrations/alembic.ini",
"src/cleveragents/infrastructure/database/migrations/script.py.mako",
"src/cleveragents/infrastructure/database/migrations/README",
]
[tool.ruff]
line-length = 88
target-version = "py313" # Target Python 3.13
src = ["src", "tests", "benchmarks"]
extend-exclude = ["docs/reference/contracts/stubs/*.py", "scripts/*.sh", "**/*.feature"]
[tool.ruff.lint]
select = ["E", "F", "W", "B", "UP", "I", "SIM", "RUF"]
ignore = []
[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["F401"]
# Behave step files: F811 = redefined step_impl (Behave pattern), E501 = long step decorator strings
# B010 = setattr with constant attribute name is intentional in immutability tests (exercises frozen model enforcement)
# I001 = import sorting (Behave step files have specific import patterns)
"features/steps/*.py" = ["F811", "E501", "B010", "I001"]
"features/mocks/*.py" = ["E501"]
"features/environment.py" = ["E501"]
# retry_patterns.py re-exports symbols from retry_service_patterns at module bottom
"src/cleveragents/core/retry_patterns.py" = ["E402"]
[tool.ruff.format]
# Use double quotes for strings
quote-style = "double"
# Indent with 4 spaces
indent-style = "space"
# Unix line endings
line-ending = "auto"
[tool.pyright]
include = ["src"]
exclude = ["**/.nox", "**/__pycache__", "**/site-packages", "src/cleveragents/discovery"]
pythonVersion = "3.13" # Target Python 3.13
typeCheckingMode = "strict"
strictListInference = true
strictDictionaryInference = true
strictSetInference = true
reportMissingImports = true
reportMissingTypeStubs = false
reportMissingTypeArgument = true
reportIncompatibleMethodOverride = true
reportIncompatibleVariableOverride = true
reportUnusedImport = true
reportUnusedClass = true
reportUnusedFunction = true
reportUnusedVariable = true
reportDuplicateImport = true
reportOptionalMemberAccess = true
reportOptionalCall = true
reportOptionalIterable = true
reportOptionalContextManager = true
reportOptionalOperand = true
reportTypedDictNotRequiredAccess = true
reportUnnecessaryIsInstance = true
reportUnnecessaryCast = true
reportUnnecessaryComparison = true
reportImplicitStringConcatenation = false
reportUnusedCallResult = false
reportUnknownMemberType = false
reportUnknownVariableType = false
reportUnknownParameterType = false
[tool.coverage]
# Coverage configuration
[tool.coverage.run]
source = ["src", "scripts"]
branch = true
parallel = false
omit = [
"*/tests/*",
"*/test_*",
"features/*",
"*/features/*",
"*/__pycache__/*",
"*/site-packages/*",
"*/dependency_injector/*",
"*/venv/*",
"*/.venv/*",
"*/.nox/*",
"src/cleveragents/discovery/*",
]
data_file = "build/.coverage"
[tool.coverage.html]
directory = "build/htmlcov"
[tool.coverage.xml]
output = "build/coverage.xml"
[tool.bandit]
targets = ["src/cleveragents"]
exclude_dirs = [
"tests",
"features",
".nox",
"build",
"dist",
"docs",
"src/cleveragents/discovery",
]
# Severity level: LOW, MEDIUM, HIGH
severity = "MEDIUM"
# Confidence level: LOW, MEDIUM, HIGH
confidence = "MEDIUM"
# Skip specific tests if needed
skips = []
[tool.vulture]
min_confidence = 80
paths = ["src/cleveragents"]
exclude = ["src/cleveragents/discovery"]
[tool.hatch]
# Hatch build configuration
[tool.pytest.ini_options]
# Markers registered here so pytest doesn't emit a
# PytestUnknownMarkWarning for the custom ``slow`` mark used by
# the auto-agents subprocess smoke tests. Operators who want a
# fast suite for the iterative loop can run
# ``pytest -m 'not slow'`` to skip them.
markers = [
"slow: tests that spawn subprocesses or otherwise add notable wall-clock time",
]
[tool.commitizen]
name = "cz_conventional_commits"
tag_format = "$version"
version_scheme = "pep440"
version_provider = "pep621"
update_changelog_on_bump = true
major_version_zero = true