Deterministic quality gate CLI for Python projects with bounded auto-repair and structured escalation evidence.
PyGate is not a lint aggregator. It is an AI repair interface for Python CI — normalizing output from ruff, pyright, and pytest into a unified schema, running bounded deterministic repair loops, and producing agent-friendly briefs when human (or model) intervention is needed.
- Quick Start
- What It Does
- Commands
- Artifacts
- Repair Loop
- Configuration
- GitHub Action
- Limitations
- Roadmap
- Contributing
- License
# Prerequisites: ruff, pyright, and pytest must be available in your environment
pip install pygate-ci
# Run quality gates on changed files
echo "src/app.py" > changed.txt
pygate run --mode canary --changed-files changed.txt
# Generate agent brief from failures
pygate summarize --input .pygate/failures.json
# Attempt bounded repair
pygate repair --input .pygate/failures.json --max-attempts 3Note: The PyPI package is
pygate-cibut the CLI command ispygate.
PyGate runs deterministic quality gates on your Python project and produces structured, machine-readable artifacts designed for both humans and AI agents.
| Gate | Tool | Canary | Full |
|---|---|---|---|
| lint | ruff | yes | yes |
| typecheck | pyright | yes | yes |
| test | pytest | configurable | yes |
Changed Files ──> Run Gates ──> Findings? ──No──> Pass
|
Yes
|
v
Repair Loop ──> Improved? ──Fixed──> Pass
|
No
|
v
Escalate with Evidence
- You tell PyGate which files changed (from your CI diff, PR, etc.)
- It runs lint, typecheck, and optionally tests
- Findings are normalized into a unified schema with severity, rule codes, and evidence
- The repair loop applies safe deterministic fixes (ruff --fix + format)
- If it can't fix everything, it escalates with structured evidence explaining why
pygate run --mode canary|full --changed-files <path>
pygate summarize --input .pygate/failures.json
pygate repair --input .pygate/failures.json [--max-attempts N]
Exit codes: 0 = pass, 1 = fail (run), 2 = escalated (repair)
All artifacts are written to .pygate/:
| File | Description |
|---|---|
failures.json |
Structured findings with severity, rule codes, and evidence |
run-metadata.json |
Gate execution traces (commands, stdout, stderr, durations) |
agent-brief.json |
Priority actions and retry policy for AI agents |
agent-brief.md |
Human-readable summary |
repair-report.json |
Repair attempt history (on success) |
escalation.json |
Escalation reason and evidence (on failure) |
JSON Schema files for all artifact types are available in schemas/ for downstream validation and code generation. See demo/artifacts/ for sample output.
The repair command runs a bounded deterministic repair loop:
- Backup workspace
- Fix via
ruff check --fix+ruff formaton scoped files - Re-run gates to measure improvement
- Decide: pass (done), worsened (rollback), no improvement (escalate)
| Parameter | Default |
|---|---|
| Max attempts | 3 |
| Max patch lines | 150 |
| No-improvement abort | 2 consecutive |
| Time cap | 20 minutes |
| Code | Meaning |
|---|---|
NO_IMPROVEMENT |
2+ consecutive attempts with no finding reduction |
PATCH_BUDGET_EXCEEDED |
Edit exceeded line budget |
UNKNOWN_BLOCKER |
Max attempts exhausted |
UNRESOLVED_DETERMINISTIC_FAILURES |
Deterministic failures remain after repair |
ARCHITECTURAL_CHANGE_REQUIRED |
Structural issues beyond repair scope (reserved) |
FLAKY_EVALUATOR |
Gate produces inconsistent results (reserved) |
ENVIRONMENT_DRIFT |
Python version or dependency mismatch (reserved) |
TEST_FIXTURE_OR_EXTERNAL_DEP |
Tests depend on network, DB, or time (reserved) |
Configure via pygate.toml (standalone) or [tool.pygate] in pyproject.toml:
pygate.toml:
[policy]
max_attempts = 3
max_patch_lines = 150
abort_on_no_improvement = 2
time_cap_seconds = 1200
[commands]
lint = "ruff check --output-format json ."
typecheck = "pyright --outputjson ."
test = "pytest --json-report --json-report-file=.pygate/pytest-report.json -q"
[gates]
test_in_canary = falseOr in pyproject.toml:
[tool.pygate.policy]
max_attempts = 3
[tool.pygate.commands]
lint = "ruff check --output-format json ."
[tool.pygate.gates]
test_in_canary = falsePyGate ships with a composite GitHub Action for CI integration:
- uses: actions/checkout@v4
- uses: roli-lpci/pygate/.github/actions/pygate@main
with:
mode: canary # or "full"
repair: "true" # attempt auto-repair on failures
max-attempts: 3
python-version: "3.12"The action detects changed files from the PR, runs gates, optionally repairs, and uploads .pygate/ artifacts. The post-comment feature requires pull-requests: write permission in your workflow.
- Deterministic repair only (v1): The repair loop uses
ruff --fixandruff format. It cannot fix type errors, failing tests, or issues requiring semantic understanding. - No incremental analysis: All specified gates run on every invocation. There is no caching or incremental mode.
- Tool availability: PyGate requires ruff, pyright, and pytest to be installed in the target environment. It does not install them.
- Single-repo scope: Designed for single Python projects, not monorepos with multiple packages.
- Model-assisted repair (LLM-powered fixes for type errors and test failures)
- Coverage gate (fail on coverage drops)
- Security gate (bandit / safety integration)
- Incremental mode (only re-run gates on changed files)
- PyPI trusted publishing via GitHub Actions
- Plugin system for custom gates
See CONTRIBUTING.md for development setup and guidelines.