Metadata-Version: 2.4
Name: hermes-gate
Version: 0.1.6
Summary: Bounded, receipt-bound completion rail for coding sessions
Author-email: Hermes Labs <roli@hermes-labs.ai>
License-Expression: Apache-2.0
Project-URL: Homepage, https://hermes-labs.ai/open-source
Project-URL: Documentation, https://github.com/hermes-labs-ai/hermes-gate#readme
Project-URL: Repository, https://github.com/hermes-labs-ai/hermes-gate
Project-URL: Issues, https://github.com/hermes-labs-ai/hermes-gate/issues
Project-URL: Changelog, https://github.com/hermes-labs-ai/hermes-gate/blob/main/CHANGELOG.md
Keywords: ai-agents,coding-agent,completion-gate,ci,developer-tools,hermes-labs,quality-assurance,receipts,reproducibility
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Quality Assurance
Classifier: Topic :: Software Development :: Testing
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: test
Requires-Dist: pytest==9.1.1; extra == "test"
Requires-Dist: ruff==0.16.7; extra == "test"
Requires-Dist: build==1.6.1; extra == "test"
Dynamic: license-file

# HermesGate

[![Hermes quality rail](https://github.com/hermes-labs-ai/hermes-gate/actions/workflows/hermes-quality.yml/badge.svg)](https://github.com/hermes-labs-ai/hermes-gate/actions/workflows/hermes-quality.yml)
[![PyPI](https://img.shields.io/pypi/v/hermes-gate)](https://pypi.org/project/hermes-gate/)
[![Python](https://img.shields.io/pypi/pyversions/hermes-gate)](https://pypi.org/project/hermes-gate/)
[![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-2ea44f)](LICENSE)

HermesGate turns “run the checks again” into a bounded completion ceremony. It
runs the repository's own commands, records exactly which content and tool
versions were checked, and reuses a PASS only while those bytes still match.

It has no daemon and no runtime dependencies beyond Python 3.11+ and Git.
Lifecycle hooks inspect receipts; they do not repair code, invoke a model, or
authorize a push, pull request, or release.

Used this way, it is a deterministic quality gate for AI-generated pull
requests: the same commit passes or fails the same declared checks the same
way every time, so a coding agent's own changes get a repeatable, receipt-bound
result before a human reviews the PR — not a merge decision or a correctness
proof (see [What PASS means](#what-pass-means)).

## Five-minute quickstart

Install the published package, then enter the repository you want to gate:

```bash
python3 -m venv .venv
. .venv/bin/activate
python -m pip install hermes-gate
cd /path/to/your/git-repository
hermes-gate init
hermes-gate fast
hermes-gate full
hermes-gate doctor
```

For HermesGate development, run `python -m pip install .` from a local checkout
instead. Release builds verify the tag, package and runner versions, tracked
runner, distribution metadata, and packaged source bytes before upload; they also reject versions
superseded on PyPI after environment approval.

### Time-limited distribution exception

The v0.1.6 release carries one direct, twelve-hour exception record for its
[Hermes Registry submission](https://github.com/hermesonehq/hermes-registry/pull/5).
It can be inspected locally with:

```bash
python scripts/verify_release.py --tag v0.1.6 \
  --distribution-exception release/distribution-exceptions/v0.1.6-hermes-registry.json
```

The command expires automatically at the recorded UTC time. It is not used by
the publish workflow and never bypasses the normal source, runner, tag, package,
or artifact identity checks.

`init` detects repository-native Python or JavaScript commands and writes a
reviewable `.hermes/gate.toml`, a checksum-bound stdlib runner, and a CI
workflow. Existing integration files are not overwritten unless
`hermes-gate init --force` is explicit.

The copied runner also requires Python 3.11 or newer, even when the project
it checks supports older Python versions. Run Gate under a supported interpreter;
profile commands may select the project's own interpreter separately.

New default whitespace checks inspect selected working-tree, staged and untracked
files using Git's whitespace rules. Existing profiles are preserved; adopting the
new check in an existing repository requires a reviewed runner/profile update.
The copied runner drains stdout and stderr concurrently while retaining at most
32 KiB of each stream; its truncation flag reports any discarded output.

## Completion workflows

| Workflow | Use it for | Receipt boundary |
| --- | --- | --- |
| `hermes-gate fast` | Short feedback over current changed paths | Exact content digest, selected paths, checks, elapsed time, and command versions |
| `hermes-gate review` | One bounded semantic review after fast passes | Exact digest, provider version, normalized findings, and suppression count |
| `hermes-gate full` | Complete repository-local test contract | Exact digest, full checks, elapsed time, and command versions |

A typical local ceremony is:

```bash
hermes-gate fast
hermes-gate review
hermes-gate full
hermes-gate boundary commit
```

The boundary command verifies configured receipts. Repositories that have never
been adopted (no profile and no Gate install manifest) are outside this rail and
pass the boundary unchanged; do not run `init` merely to satisfy a global hook.
An adopted repository with a missing profile remains fail-closed until its
profile is restored or `uninstall-repo` completes. The boundary never performs
the Git operation or grants owner authorization.

## CLI

```text
hermes-gate init [--force]
hermes-gate fast
hermes-gate repair
hermes-gate review
hermes-gate full
hermes-gate boundary commit|push|pr-create|pr-ready
hermes-gate doctor
hermes-gate uninstall-repo
hermes-gate delegate-judge
hermes-gate --version
```

Commands emit JSON with one terminal state: `PASS`, `FAIL`,
`NOT_APPLICABLE`, `NOT_CONFIGURED`, `REVIEW_UNAVAILABLE`, `PARKED`, or
`ERROR`.

## Repository-native default

The generated profile uses argv arrays and the repository's existing Ruff,
pytest, npm, lint, typecheck, test, or build commands. It never installs these
tools. Runtime receipts, baselines, install manifests, and adapter artifacts live
under `.git/hermes-gate/`; generated tracked source contains no machine-specific
absolute paths.

```toml
[gate]
fast_budget_seconds = 8.0
full_required_local = false

[[fast]]
name = "ruff"
argv = ["ruff", "check", "{files}"]
timeout_seconds = 6.0
globs = ["**/*.py"]
```

`{files}` expands to separate argv entries. Shell strings are rejected.

## Optional primitive adapters

HermesGate packages a standard-library loader and validator for the shared
`gate-result/v1` schema. PyGate and QuickGate.js adapters are opt-in and
isolated; HermesGate never downloads or installs them.

PyGate 0.2.0 or newer:

```toml
[adapter]
enabled = true
name = "pygate"
argv = ["pygate"]
minimum_version = "0.2.0"
```

QuickGate.js 0.2.3 or newer:

```toml
[adapter]
enabled = true
name = "quick-gate"
argv = ["quick-gate"]
minimum_version = "0.2.3"
```

Adapter output is written below `.git/hermes-gate/adapters/`, validated against
the packaged contract, and then normalized into a HermesGate receipt. An absent,
old, or invalid primitive returns `ERROR`; it never falls through to an
unreviewed installation.

## Review and hooks

`review` requires an exact fast PASS. CodeRabbit agent-mode JSONL is the initial
provider boundary. If it is unavailable, a clean committed diff may use an
installed `hermes-pr-review` fallback. Unparseable, unauthenticated, timed-out,
or unavailable review output is never relabeled PASS.

`review.provider` names that output contract and must be `coderabbit`;
`review.argv[0]` names the executable and may differ (for example a wrapper or
the `cr` alias). Any other provider name is a profile error: `fast`, `review`,
`full`, `repair`, and `boundary` all return `ERROR` with
`invalid profile: review.provider …`, and `doctor` reports `profile.status:
ERROR`, because a command the engine cannot bind to the diff or parse can only
ever produce an unavailable or wrong review. `doctor` probes the executable the
profile names, resolved the same way `review` launches it.

`install-codex` and `uninstall-codex` are intentionally hidden integration
commands. Codex integration preserves unrelated hook and instruction content.
Claude settings and instruction surfaces remain outside Codex's ownership.

## Claude Code plugin

The repository ships a self-contained Claude Code plugin at
[`claude-plugin/`](claude-plugin/) — it bundles its own copy of
`src/hermes_gate` so a marketplace install works with no separate `pip
install`. It wires three lifecycle hooks to the same `hermes-gate hook`
event handlers used by the manual Claude integration above:

| Event | Hook | Behavior |
|---|---|---|
| `SessionStart` | `session-start` | Injects the compact completion contract as additional context. Always continues. |
| `Stop` | `stop` | Runs the cached fast gate against session-changed code paths and reports a non-PASS reason as an advisory `systemMessage`. Never blocks. |
| `PreToolUse` (matcher `Bash`) | `pre-tool-use` | Denies a detected commit/push/PR boundary command that is missing its matching receipt in an adopted repository; never-adopted repositories stay outside the rail. |

Every path fails open: a malformed payload, an unrecognized event, or an
internal error returns `{"continue": true}` rather than blocking the
session. Install it from this marketplace:

```
/plugin marketplace add hermes-labs-ai/claude-plugins
/plugin install hermes-gate@hermes-labs
```

`tests/test_claude_plugin.py` verifies the bundled `claude-plugin/src`
runtime is byte-identical to `src/hermes_gate` and exercises the hook
script as a subprocess with no `hermes-gate` package installed, so the
marketplace artifact is tested the same way Claude Code runs it.

## Hermes Agent quality-gate seam

`hermes-gate delegate-judge` is a hidden integration command for one specific
seam: NousResearch's Hermes Agent quality gate, which spawns an argv-only
judge command per subagent task and sends one JSON object on stdin:

```json
{"version": 1, "goal": "...", "summary": "...", "attempt": 1, "max_retries": 2,
 "previous_feedback": ["..."], "task_index": 0, "subagent_id": "..." | null,
 "session_id": null, "model": null, "api_calls": null, "completed": true,
 "workspace": "/absolute/path/to/the/child/workspace" | null,
 "workspace_isolated": true}
```

and reads exactly one JSON object from stdout:

```json
{"verdict": "pass" | "retry" | "reject" | "error", "feedback": "..."}
```

Configure Hermes Agent's quality gate with:

```bash
hermes-gate delegate-judge
```

The command validates the request, then runs `fast` against `workspace`'s own
Git repository root using the same internal engine `hermes-gate fast` uses —
no shell, no API-backed review, and no process-wide working-directory change,
so concurrent judge processes for different workspaces cannot race. A Gate
`PASS` or `NOT_APPLICABLE` maps to verdict `pass`. `attempt` is one-based (the
first attempt is `1`) and `max_retries` counts the allowed correction turns
after that first attempt, so a material `FAIL` maps to `retry` while
`attempt <= max_retries`, and `reject` once that budget is exhausted — a
`max_retries: 1` profile still retries the first (`attempt: 1`) failure.

`previous_feedback` may be a string, an array of strings, or `null`; the judge
never reads it. `subagent_id` and `workspace` may be `null`. `workspace_isolated`
is validated as a boolean when present. A `null` or absent `workspace` cannot be
judged and returns the fixed verdict `error` with feedback
`workspace unavailable: request has no workspace path`; an unusable path
returns `workspace unavailable: path is not a directory`. Neither echoes the
request. An invalid request, a missing `.hermes/gate.toml` profile, an
unavailable tool or adapter, or an internal error all map to `error`.
Feedback is a short, deterministic summary of the failing check names and
reasons; it never includes raw command stdout/stderr, full diffs, or source
content.

## What PASS means

A PASS proves only that the declared commands passed for the receipt-bound local
content. It does not prove universal correctness, security, mergeability, public
release, adoption, or permission to push, publish, or create a pull request.

See [SECURITY.md](SECURITY.md), [CONTRIBUTING.md](CONTRIBUTING.md), and
[CHANGELOG.md](CHANGELOG.md) for project policy and release history.
