{
  "markdown": "<div align=\"center\">\n\n<h1>PyGate</h1>\n\n**PyGate turns Ruff, Pyright, and pytest results into one deterministic Python quality-gate result, with bounded lint repair and structured evidence when the gate cannot finish.**\n\nPyGate is an open-source reliability tool developed by [Hermes Labs](https://hermes-labs.ai), built for teams that need CI results to remain inspectable and actionable.\n\nHermes Labs is an agentic infrastructure company building the reliability layer for autonomous systems.\n\n[![CI](https://github.com/hermes-labs-ai/quick-gate-python/actions/workflows/ci.yml/badge.svg)](https://github.com/hermes-labs-ai/quick-gate-python/actions/workflows/ci.yml)\n[![PyPI version](https://img.shields.io/pypi/v/pygate-ci)](https://pypi.org/project/pygate-ci/)\n[![Apache 2.0 license](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)\n[![Made by Hermes Labs](https://img.shields.io/badge/made%20by-Hermes%20Labs-4F46E5)](https://hermes-labs.ai)\n\n</div>\n\nPython maintainers usually meet the same problem at the worst possible time: a pull request has several tool outputs, each with its own format and failure order, and someone must decide what to fix first. PyGate gives a CI job or follow-up agent one deterministic result, preserves the underlying command evidence, and stops deterministic repair within an explicit budget.\n\n## First success\n\nPyGate is published as `pygate-ci` and installs the `pygate` command. It supports Python 3.10 and newer. Install PyGate and the tools used by the default gates:\n\n~~~bash\npython -m pip install pygate-ci ruff pyright pytest pytest-json-report\n~~~\n\nVerify the installation:\n\n~~~bash\npygate --version\n# pygate 0.3.2\n~~~\n\nRun a whole-project canary gate:\n\n~~~bash\npygate run --mode canary\n~~~\n\nThe command prints a JSON `gate-result/v1` result. With no `--output-dir`, it does not create `.pygate` or other files. `canary` runs Ruff and Pyright; `full` also runs pytest. Pass `--changed-files PATH` when you want the result's snapshot metadata bound to a newline-delimited or JSON file list.\n\nFor a file-backed CI result, choose the output directory explicitly:\n\n~~~bash\npygate run \\\n  --mode full \\\n  --changed-files /tmp/pygate-changed-files.txt \\\n  --output-dir .pygate\n~~~\n\nThe most useful next command after a failed run is:\n\n~~~bash\npygate summarize --input .pygate/failures.json\n~~~\n\nThat writes a short agent brief to `.pygate/agent-brief.json` and `.pygate/agent-brief.md`.\n\nDon't take the pass/fail/escalated contract on faith. [`tests/test_fresh_repo_integration.py`](tests/test_fresh_repo_integration.py) builds real throwaway projects from [`tests/action-fixture/`](tests/action-fixture/) and runs the installed `pygate` binary against them for real — no mocked tool output. Clone the repo and run it yourself:\n\n~~~bash\npip install -e \".[dev]\"\npytest -m integration\n~~~\n\nIt proves three outcomes end to end: a clean project passes, a type error fails the gate and repair correctly escalates instead of guessing, and a Ruff-fixable lint issue is auto-repaired back to a passing gate. The same fixtures back the root action's CI smoke test ([`.github/workflows/action-smoke.yml`](.github/workflows/action-smoke.yml)), which runs the packaged GitHub Action itself against a separate, isolated consumer workspace on every push.\n\n## Agent skill\n\nThe repository root is also a portable [Agent Plugin](https://agent-plugins.org)\n(`plugin.json`, Agent Plugins 1.0.0) that ships one skill,\n[`skills/quick-gate-python/SKILL.md`](skills/quick-gate-python/SKILL.md). It\nteaches a coding agent to run `pygate` (or the pinned\n`uvx --from pygate-ci==0.3.2 pygate`) and report the result without\noverclaiming.\n\n| Host | Install | Read back |\n| --- | --- | --- |\n| Claude Code | `claude plugin marketplace add hermes-labs-ai/quick-gate-python`<br>`claude plugin install quick-gate-python@quick-gate-python` | `claude plugin list` |\n| OpenAI Codex CLI | `codex plugin marketplace add hermes-labs-ai/quick-gate-python`<br>`codex plugin add quick-gate-python@quick-gate-python` | `codex plugin list` |\n| Gemini CLI | `gemini extensions install https://github.com/hermes-labs-ai/quick-gate-python --ref main` | `gemini skills list` |\n| Any skills.sh agent | `npx skills add https://github.com/hermes-labs-ai/quick-gate-python --skill quick-gate-python` | `npx skills list` |\n\nClaude Code reads `.claude-plugin/`; Codex reads `.agents/plugins/marketplace.json`\n(entry `./`, the root) and `plugin.json`; Gemini CLI reads `gemini-extension.json`.\nAll of them load the same `skills/` directory. Keep `--ref main` for Gemini:\nreleases up to v0.3.1 predate `gemini-extension.json`.\n\n## What PyGate does\n\nPyGate coordinates three existing tools:\n\n| Gate | Default command | Canary | Full |\n| --- | --- | --- | --- |\n| Lint | `ruff check --no-cache --output-format json --exclude .pygate .` | Yes | Yes |\n| Type check | `pyright --outputjson .` | Yes | Yes |\n| Tests | `pytest -p no:cacheprovider -q` without artifacts; JSON-report mode with an explicit output directory | No, unless configured | Yes |\n\nThe default commands scan the configured project command. `--changed-files` identifies the paths whose bytes are snapshotted and recorded in the result; it does not, by itself, rewrite the default Ruff or Pyright commands into changed-file-only analysis. Configure commands explicitly when a repository needs narrower scope.\n\nThe flow is intentionally small:\n\n1. Load `pygate.toml` or `[tool.pygate]` from `pyproject.toml`.\n2. Run the configured or default gate commands with bounded time and output capture.\n3. Normalize Ruff, Pyright, and pytest findings and bind the result to input snapshots.\n4. Return a pass, fail, timeout, or error result.\n5. Optionally run a bounded deterministic repair loop for Ruff-fixable findings.\n\nPyGate does not replace Ruff, Pyright, or pytest. It gives their results a predictable decision and evidence shape for a human or an automation layer.\n\n## CLI\n\n~~~text\npygate --version\npygate run --mode canary|full [--changed-files <path>] [--output-dir <directory>] [--unsafe-shell]\npygate summarize --input <failures.json>\npygate repair --input <failures.json> [--max-attempts N]\n~~~\n\n`--changed-files` accepts either one path per line or a JSON array of strings. Relative paths are resolved from the current working directory. When omitted, PyGate runs the configured whole-project commands and snapshots the project tree (excluding generated and hidden state directories) so the result remains bound to the checked inputs.\n\nExit codes are:\n\n- `pygate run`: `0` for pass; `1` for fail or timeout.\n- `pygate summarize`: `0` after writing the brief.\n- `pygate repair`: `0` when repair passes; `2` when it escalates.\n\n### Output and artifacts\n\nThere are two intentionally different run paths:\n\n- **Current CLI path:** `pygate run` without `--output-dir` prints the contract JSON and is side-effect-free with respect to project files.\n- **Explicit artifact path:** `pygate run --output-dir DIR` creates `DIR` and writes `failures.json`, `run-metadata.json`, and `gate-result.json` there. A default full-mode pytest command also writes `pytest-report.json` there. The selected artifact directory is excluded from input snapshots, including when it is a non-hidden directory inside the project.\n- **Legacy artifact commands:** `pygate summarize` and `pygate repair` use `.pygate/` in the current working directory for their outputs. `pygate repair` may modify eligible Python files while applying Ruff fixes; it writes `repair-report.json` on repair success or `escalation.json` when it stops without passing.\n\nImportant artifact files include:\n\n| File | Purpose |\n| --- | --- |\n| `gate-result.json` | Versioned gate result from an explicit `run --output-dir` |\n| `failures.json` | Normalized findings and gate statuses |\n| `run-metadata.json` | Environment and command traces, including timeouts and truncation |\n| `agent-brief.json` / `agent-brief.md` | Prioritized follow-up actions from `summarize` |\n| `repair-report.json` | Bounded repair attempts when repair passes |\n| `escalation.json` | Reason and evidence when repair escalates |\n\nThe repository includes sample artifacts in [`demo/artifacts/`](demo/artifacts/) and JSON Schemas in [`schemas/`](schemas/).\n\n### The `gate-result/v1` contract\n\nThe `gate-result/v1` JSON contract is the stable result boundary exposed by the side-effect-free API and the CLI's no-output-directory path. It includes:\n\n- `schema`, `status`, and the canonical `checked_paths`;\n- per-check status, argv, exit code, duration, captured output, and diagnostics;\n- normalized `findings` plus command versions;\n- snapshot digests, configuration identity/digest, package version, elapsed time, and truncation/error state.\n\nThe contract describes what these configured local checks observed. It is not a universal correctness proof, a security verdict, or a guarantee that a repository is safe to merge.\n\n## Embeddable Python API\n\nUse `pygate.api.evaluate` when the caller needs the result in memory and does not want PyGate to create files or mutate environment state:\n\n~~~python\nfrom pathlib import Path\n\nfrom pygate.api import evaluate\nfrom pygate.models import RunMode\n\nresult = evaluate(\n    mode=RunMode.CANARY,\n    checked_paths=[\"src/app.py\"],\n    cwd=Path.cwd(),\n)\n\nprint(result.schema)  # gate-result/v1\nprint(result.status)  # pass, fail, timeout, or error\n~~~\n\nThe API returns a Pydantic `GateResultV1` model. It does not write `.pygate`, `gate-result.json`, or other artifacts. Use the explicit CLI output path or the legacy artifact commands when files are required.\n\n### Machine-readable result envelope\n\nFor tooling that compares results across products, the same evaluation can be\nemitted as a Hermes Reliability Lab result envelope — the ordinary\n`gate-result/v1` payload embedded verbatim, plus tool version, a hash of the\nexact input, one finding per non-passing check and per lint/typecheck finding\npygate already produced, the exit code, a timestamp, and the Git commit when\nrun from a checkout:\n\n~~~bash\npython -m pygate.evidence --mode canary\npython -m pygate.evidence --mode full --path path/to/project\n~~~\n\nIt changes no gate resolution or scoring and writes nothing beyond what\n`pygate.api.evaluate` already writes (nothing). A check pygate could not run —\nmissing executable, or an error starting it — is reported as `unknown`, not\nfolded into a pass or silently treated as a code-quality failure.\n\n## Bounded repair\n\n`pygate repair` is deliberately narrower than an AI coding agent. For Ruff findings in eligible Python files, it can run `ruff check --fix` and `ruff format`, re-run the gates, and stop when the result passes, worsens, exceeds the patch budget, reaches the attempt limit, or stops improving.\n\nThe default policy is:\n\n| Policy | Default |\n| --- | ---: |\n| Maximum attempts | 3 |\n| Maximum patch lines | 150 |\n| No-improvement abort | 2 consecutive attempts |\n| Overall time cap | 1,200 seconds |\n\nRepair does not invent semantic fixes for type errors or failing tests. Review any file changes before accepting them.\n\n## Configuration\n\nPyGate reads `pygate.toml` first. If that file is absent, it reads `[tool.pygate]` from `pyproject.toml`.\n\n~~~toml\n[tool.pygate]\nallow_unsafe_shell = false\n\n[tool.pygate.policy]\nmax_attempts = 3\nmax_patch_lines = 150\nabort_on_no_improvement = 2\ntime_cap_seconds = 1200\ncommand_timeout_seconds = 120\ngate_timeout_seconds = 600\noutput_cap_bytes = 1048576\n\n[tool.pygate.commands]\nlint = \"ruff check --output-format json --exclude .pygate .\"\ntypecheck = \"pyright --outputjson .\"\ntest = \"pytest --json-report --json-report-file=.pygate/pytest-report.json -q\"\n\n[tool.pygate.gates]\ntest_in_canary = false\n~~~\n\nThe same keys can be used in a standalone `pygate.toml` with `[policy]`, `[commands]`, and `[gates]` sections. String commands are tokenized into argv-safe arguments by default, so shell metacharacters remain data. `--unsafe-shell` or `allow_unsafe_shell = true` is an explicit compatibility escape hatch for legacy shell command strings. Review those commands as executable code.\n\n## GitHub Actions\n\nThe current root action dependencies run on Node.js 24. GitHub-hosted runners are ready; self-hosted runners must use Actions Runner v2.327.1 or later.\n\nThe repository ships the root Marketplace action at [`action.yml`](action.yml) and a copyable example at [`.github/workflows/example-usage.yml`](.github/workflows/example-usage.yml). Pin the root action to this currently audited immutable commit:\n\n`hermes-labs-ai/quick-gate-python@aef81b5e832f98aaef02b0a1f64f8eed277e92f1`\n\n~~~yaml\nname: \"Example: PyGate Quality Gates\"\n\non:\n  pull_request:\n    branches: [main]\n\npermissions:\n  contents: read\n\njobs:\n  quality-gates:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1\n        with:\n          fetch-depth: 0\n      - uses: hermes-labs-ai/quick-gate-python@aef81b5e832f98aaef02b0a1f64f8eed277e92f1\n        with:\n          mode: canary\n          python-version: \"3.12\"\n          fail-on-error: \"true\"\n~~~\n\nThis first example is read-only and blocking: it grants only `contents: read`, leaves repair and comments disabled, and fails the job when the final action status is `fail` or `escalated`.\n\nThe root action installs PyGate from its own checkout, then installs Ruff and Pyright, detects changed files, writes run artifacts to `.pygate/`, optionally attempts repair, optionally posts a pull-request comment, and uploads the artifact directory. Canary mode skips tests by default. Full mode is caller-owned: before the action step, install the project and test dependencies into the same Python version selected by the action, for example:\n\n~~~yaml\n- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0\n  with:\n    python-version: \"3.12\"\n- run: python -m pip install -e \".[dev]\"\n- run: python -m pip install pytest pytest-json-report\n~~~\n\nThe action does not provide an install-command, execute package installation for full mode, or infer project dependencies. It preflights `pytest` and `pytest-json-report` after setup and reports an actionable error before changed-file discovery when either is unavailable.\n\n### Optional pull-request comments\n\nTo post a failure summary on a pull request, add `pull-requests: write` beside `contents: read` and set `post-comment: \"true\"` on the root action:\n\n~~~yaml\npermissions:\n  contents: read\n  pull-requests: write\n\nsteps:\n  - uses: hermes-labs-ai/quick-gate-python@aef81b5e832f98aaef02b0a1f64f8eed277e92f1\n    with:\n      mode: canary\n      python-version: \"3.12\"\n      post-comment: \"true\"\n~~~\n\nKeep `repair: \"false\"` unless workspace mutation is explicitly intended. Pull requests from forks commonly receive a read-only `GITHUB_TOKEN`, so comment posting may be unavailable there; the read-only example above remains the safe baseline.\n\n### Root action contract\n\nThe root action accepts these inputs:\n\n| Input | Default | Contract |\n| --- | --- | --- |\n| `mode` | `canary` | `canary` runs Ruff and Pyright; `full` also runs pytest. |\n| `repair` | `false` | When `true`, permits bounded Ruff repair that may mutate eligible consumer files. |\n| `max-attempts` | `3` | Positive integer from `1` through `10`; applies to repair. |\n| `python-version` | `3.12` | Python version selected for the action and its preflight. |\n| `post-comment` | `false` | When `true`, posts a pull-request summary and requires `pull-requests: write`. |\n| `changed-files` | empty | Optional path to newline-delimited or JSON-list changed paths. |\n| `artifact-name` | `pygate-artifacts` | Caller-configurable uploaded artifact name. |\n| `fail-on-error` | `true` | When `true`, a final `fail` or `escalated` status fails the action; `false` is observation-only. |\n| `render-summary` | `false` | When `true`, adds a bounded Actions summary with only status, check counts, repair state, version, and source link. |\n| `render-annotations` | `false` | When `true`, adds up to 100 GitHub annotations for diagnostics with one relative file and a positive exact line. |\n\nIt exposes these outputs:\n\n| Output | Values or meaning |\n| --- | --- |\n| `status` | Final result: `pass`, `fail`, or `escalated`. |\n| `gate-status` | Raw initial gate result before repair: `pass` or `fail`. |\n| `repair-status` | `pass`, `escalated`, or `skipped`. |\n| `full-mode-dependency-status` | `ready`, `missing`, or `not-required`. |\n| `full-mode-dependency-classification` | `caller-owned-dependencies`, `missing-test-dependency`, or `canary-does-not-run-tests`. |\n| `changed-files-strategy` | Source label such as `caller-supplied`, `pull-request-diff`, `push-diff`, `parent-diff`, or `first-or-shallow-tracked-files`. |\n| `failures-json` | Path to `.pygate/failures.json` when the gate runs. |\n\nThe action uploads `.pygate/` as an action-owned artifact, including hidden files. It includes `gate-result.json`, `failures.json`, `run-metadata.json`, and, in full mode, the pytest report; repair and summary artifacts are included when produced. Treat command output in these artifacts as untrusted data. The default artifact name is `pygate-artifacts`; set `artifact-name` to avoid collisions in matrices.\n\n### Optional file annotations\n\nSet `render-annotations: \"true\"` on a revision that includes this input to show supported failures beside their file and line in GitHub Actions. Annotations use fixed generic messages; they do not include raw tool output. Diagnostics without one unambiguous relative file and a positive line are skipped, including test summaries without an exact location. Annotation rendering does not change the gate result or require additional token permissions.\n\nThe renderer accepts a failure receipt of up to 1 MB and emits at most 100 annotations. Use the uploaded receipt for the full diagnostics when there are more failures or unsupported locations.\n\n### Pinning policy\n\nUse the root action at an immutable commit. The examples use the annotation-capable commit `aef81b5e832f98aaef02b0a1f64f8eed277e92f1`.\n\nThe `v0.2.1` release is the first package tag that also contains the root\n[`action.yml`](action.yml), so that tag is a valid convenience reference.\nThe earlier `v0.1.0`, `v0.1.1`, and `v0.2.0` tags predate the action and\ndo not resolve. For reproducible supply-chain pinning, use the audited commit\nshown above; do not use a mutable branch reference.\n\nPyGate never grants merge authority. A workflow still decides whether a failed, timed-out, or escalated job blocks a pull request, and any comment or artifact should be treated as untrusted command output before security-sensitive rendering.\n\n## Pre-commit\n\nThe repository ships a native [`.pre-commit-hooks.yaml`](.pre-commit-hooks.yaml)\nwith two hook ids: `pygate` (canary mode — Ruff + Pyright) and `pygate-full`\n(full mode — Ruff + Pyright + pytest). Add the hook to `.pre-commit-config.yaml`:\n\n```yaml\nrepos:\n  - repo: https://github.com/hermes-labs-ai/quick-gate-python\n    rev: 00e561917dd1772252773aef060c07c2b8719377  # immutable commit; no tagged release contains .pre-commit-hooks.yaml yet\n    hooks:\n      - id: pygate\n```\n\nInstall and run it:\n\n```bash\npre-commit install\npre-commit run pygate\n```\n\nBoth hooks run against the whole working tree (`pass_filenames: false`,\n`always_run: true`) rather than only staged files, matching how `pygate run`\nsnapshots the repo. `pygate` and `pygate-full` exit non-zero on a `fail`\nverdict and on internal errors, so either blocks the commit; a `pass` verdict\nexits 0. The hook entry writes no artifacts into your repository: it prints the\n`gate-result/v1` document and leaves the working tree untouched.\n\n### What the hook environment installs\n\npre-commit builds one isolated environment per hook. Installing PyGate there\nbrings only its runtime dependencies (`pydantic`, plus `tomli` on Python 3.10);\nRuff, Pyright, and pytest are `[dev]` extras and are not part of that install.\nThe manifest therefore declares the gate tools each hook shells out to:\n\n| Hook | `additional_dependencies` |\n| --- | --- |\n| `pygate` | `ruff==0.16.8`, `pyright==1.1.414` |\n| `pygate-full` | the same two, plus `pytest==9.1.1` and `pytest-json-report==1.5.0` |\n\nThe pins are exact rather than ranges. A consumer pins `rev` to an immutable\ncommit, so a range would still let the installed tool version drift with the\ninstall date and change the verdict for the same pinned hook. Bump them\ndeliberately, or override them in your own config (see below).\n\n`pytest-json-report` is present because PyGate's report-backed test findings\nread a pytest JSON report, which is the shape the documented\n`[tool.pygate.commands] test` command emits; the default hook entry runs pytest\nwithout it and falls back to exit-code findings.\n\nThe Pyright distribution on PyPI provisions its own Node runtime the first time\nit runs, so the first hook run needs network access — as does any pre-commit\nenvironment install.\n\n### Project test dependencies stay caller-owned\n\n`pygate-full` runs your test suite inside that same isolated environment. The\nenvironment contains the pinned tools above and nothing else: not your project,\nand not the third-party packages your tests import. If your suite needs them,\ndeclare them on the hook:\n\n```yaml\nrepos:\n  - repo: https://github.com/hermes-labs-ai/quick-gate-python\n    rev: 00e561917dd1772252773aef060c07c2b8719377  # immutable commit; no tagged release contains .pre-commit-hooks.yaml yet\n    hooks:\n      - id: pygate-full\n        additional_dependencies:\n          - ruff==0.16.8\n          - pyright==1.1.414\n          - pytest==9.1.1\n          - pytest-json-report==1.5.0\n          - httpx==0.28.1        # your own test dependencies\n```\n\n`additional_dependencies` in your config **replaces** the manifest list rather\nthan extending it, so repeat the pins you still want.\n\nPyGate does not promise that `pygate-full` fits every project. A suite that has\nto import the project itself, or that depends on a locally built extension or a\nservice fixture, is not reachable from an isolated hook environment. For those\nprojects run `pygate` as the pre-commit hook and keep full mode where the\nproject's own environment already exists — CI, or a `language: system` local\nhook that calls the `pygate` you installed yourself.\n\n## Privacy, egress, and safety\n\n- PyGate itself does not make network requests or silently install packages.\n- It executes the configured local commands for Ruff, Pyright, and pytest. Those tools may have their own network or plugin behavior; review their configuration and dependency policy.\n- Command stdout and stderr can be copied into artifacts. Treat `.pygate/` as project data, not as a trusted security report.\n- The repair loop reads and writes eligible files in the current project and keeps backups while it works. Run it on a clean branch or review the resulting diff.\n- Custom commands and `--unsafe-shell` can execute arbitrary commands. Do not enable them for untrusted configuration without review.\n- The GitHub Action needs only `contents: read` for its normal work. Pull-request comments require `pull-requests: write`.\n\n## Limitations\n\nPyGate does not promise:\n\n- a proof of universal correctness or a complete quality audit;\n- a security guarantee, vulnerability scan, or safe-to-merge verdict;\n- semantic repair for type errors, test failures, or architectural changes;\n- automatic merge authority or approval of a pull request;\n- changed-file-only analysis unless the configured underlying commands implement that scope;\n- support for every project layout or monorepo workflow;\n- zero network activity from the underlying tools it invokes.\n\nThe default integration is intentionally narrow: Ruff linting, Pyright type checking, and pytest testing, with deterministic parsing and bounded repair around them.\n\n## Troubleshooting\n\n**`ruff` or `pyright` is missing.** Install the underlying tools in the same environment as `pygate`:\n\n~~~bash\npython -m pip install ruff pyright\n~~~\n\n**Full mode cannot run pytest.** Install both pytest and the JSON-report plugin, or configure a test command that emits output PyGate can consume:\n\n~~~bash\npython -m pip install pytest pytest-json-report\n~~~\n\n**No artifacts appeared.** That is expected for `pygate run` without `--output-dir`. Use `--output-dir .pygate` for run artifacts. `summarize` and `repair` retain their `.pygate/` compatibility behavior.\n\n**A custom command behaves differently than a shell command.** Commands are argv-tokenized by default. If legacy shell syntax is unavoidable, pass `--unsafe-shell` or set `allow_unsafe_shell = true` only after reviewing the command.\n\n**The gate fails after a file changes during execution.** PyGate compares snapshots before and after the run and reports the result as stale. Re-run after the writer has stopped.\n\n## Development and contribution\n\nFor a local development checkout:\n\n~~~bash\ngit clone https://github.com/hermes-labs-ai/quick-gate-python.git\ncd quick-gate-python\npython -m venv .venv\nsource .venv/bin/activate\npython -m pip install -e \".[dev]\"\npytest tests/ -v\nruff check src/ tests/\nruff format --check src/ tests/\npyright src/\n~~~\n\n`pytest tests/ -v` runs the offline suite only; the mocked gate outputs described in [`AGENTS.md`](AGENTS.md) never shell out to Ruff or Pyright. Run `pytest -m integration` separately to exercise the real fresh-repository fixtures above.\n\nSee [`CONTRIBUTING.md`](CONTRIBUTING.md) for contribution guidance and [`SECURITY.md`](SECURITY.md) for vulnerability reporting and execution-safety notes.\n\n## Relationship to PyGate and Quick Gate\n\nThree Hermes Labs tools share one result contract and stay separate on purpose:\n\n| Tool | Package / command | Role |\n| --- | --- | --- |\n| **HermesGate** ([hermes-gate](https://github.com/hermes-labs-ai/hermes-gate)) | PyPI `hermes-gate`, `hermes-gate` | Language-agnostic completion rail: runs a repository's declared checks and binds a PASS to exact content and tool versions in a receipt. |\n| **PyGate** ([quick-gate-python](https://github.com/hermes-labs-ai/quick-gate-python)) | PyPI `pygate-ci`, `pygate` | Python check primitive: normalizes Ruff, Pyright, and pytest into one `gate-result/v1`, with bounded lint repair. |\n| **Quick Gate** ([quick-gate-js](https://github.com/hermes-labs-ai/quick-gate-js)) | npm `quick-gate`, `quick-gate` | JavaScript/TypeScript check primitive: normalizes ESLint, TypeScript, build, and Lighthouse into one `gate-result/v1`, with bounded repair. |\n\n- **Shared primitive:** the `gate-result/v1` schema (`schemas/gate-result-v1.schema.json`) is byte-identical in all three repositories. PyGate and Quick Gate emit it; HermesGate validates it.\n- **Composition is optional:** HermesGate can wrap PyGate (0.2.0+) or Quick Gate (0.2.3+) through an opt-in [primitive adapter](https://github.com/hermes-labs-ai/hermes-gate#optional-primitive-adapters); it never installs them, and an absent, old, or invalid primitive is an error, not a pass. Each primitive is fully usable on its own.\n- **Which to use:** use PyGate or Quick Gate alone to get one normalized result for a Python or JS/TS project. Use HermesGate when you need a receipt proving which exact bytes passed which declared commands, across any stack.\n- **Names and interfaces are stable:** the three products keep their own names, commands, versions, and release cadence; this relationship adds no API change.\n\n## Listed in\n\n[cleder/awesome-python-testing](https://github.com/cleder/awesome-python-testing/pull/108)\nlists quick-gate-python (PyPI `pygate-ci`) among Python testing tools (merged\n2026-08-07) — a listing, not an endorsement.\n\n## License\n\nPyGate is licensed under the [Apache License 2.0](LICENSE).\n",
  "bytes": 27655,
  "sha": "87db2cc2660735e599a2f591c68bfe384c3a7726930f6bb5c1790ccf8b4d9d76",
  "repo_slug": "roli-lpci/quick-gate-python",
  "fonte": "repo",
  "truncated": false,
  "api": "https://api.agentalog.com/api/listings/plg_roli_lpci_quick_gate_python_5fb94e0e/readme"
}