A deterministic verify-gate for AI-generated code changes. A patch is applied only if the oracle is green — closed-world check passes, the project's own test suite still passes, and no regression is introduced. Otherwise the change is refused, untouched. Headline guarantee: WRONG-APPLY = 0.
Greenlock is not another coding agent. It is the safety layer that sits between any agent (Devin, Copilot, Cursor, a local model — or a human) and your repository. The model proposes; the oracle decides.
Scope of the guarantee: Greenlock defends against accidental breakage and hallucinations from a non-adversarial author. It is not a sandbox and the verifier executes code — run it only in an isolated/ephemeral environment, and read SECURITY.md for the threat model and a malicious-author's limits.
Today's coding agents try to run tests, but still hand you plausible-looking code that may be broken — their guarantee is probabilistic. Review bots (Qodo, CodeRabbit) advise; CI is dumb and reactive; security guardrails check policy, not correctness.
Greenlock gives a refusal contract instead:
| Layer | What it does | What it misses |
|---|---|---|
| CI / branch protection | deterministic, but dumb & reactive | needs tests up front; not in the agent's loop; no grounding |
| Review bots | comment / flag | advisory, probabilistic |
| Security guardrails | block secrets / policy / access | not correctness or regression |
| Greenlock | deterministic merge/reject on correctness | (only as strong as the test suite — see testgen on the roadmap) |
patch (unified diff) ─► sandbox copy of repo
├─ apply patch (git apply / patch)
├─ closed-world check (no references to symbols that don't exist)
├─ run the project's OWN test suite, independently
└─ compare to baseline → regression?
│
green + no regression ──► ✅ MERGE
anything else ──► 🛑 REJECT (with the failing reason)
Two modes:
- gate-only — verify a diff produced by any source. Model is never called. This is the product: an insurance layer over other agents.
- generate-and-gate — a local writer model proposes a patch and the same oracle loops repair → apply-only-if-green. Cheap, private, on-prem.
Project-agnostic by design: language is auto-detected per file (Python & JavaScript
get deep closed-world grounding; Go/Rust/Java/C/C++/C#/Ruby/PHP via tree-sitter), and the
verifier is auto-detected by manifest (pyproject.toml/setup.py → pytest,
package.json → node, go.mod → go test, Cargo.toml → cargo test). Drop in any repo.
Requirements: Python 3.10+. For the generate mode and the web UI you also need an Ollama endpoint; the gate-only mode needs neither.
pip install greenlock # once published to PyPI → gives the `greenlock` command
# or from source:
git clone https://github.com/abaykopenov/greenlock && cd greenlock
pip install -e . # core (pure stdlib)
pip install -e ".[all]" # + tree-sitter (more languages) + certifi (cloud HTTPS)After install, run greenlock doctor in your repo to see what it can verify.
# verdict: MERGE or REJECT, with the reason
greenlock gate path/to/repo my_change.diff # unified CLI (after install)
greenlock check # gate your git changes — no manual diff
greenlock check --staged # only staged changes
greenlock check --against main # changes vs a ref/PR base
greenlock gate path/to/repo my_change.diff --apply # apply the patch iff MERGE
greenlock gate path/to/repo my_change.diff --json
# equivalent module form (no install needed):
python3 -m greenlock.gate path/to/repo my_change.diff
git diff | python3 -m greenlock.gate path/to/repo -greenlock --version, greenlock init, greenlock harden, and greenlock mcp are the
other subcommands. Run greenlock -h for the full list.
Exit code is 0 on MERGE, 1 on REJECT — drop it straight into a CI step or pre-merge hook.
The verifier executes the repo's tests, so for untrusted patches run the whole gate
inside a locked, throwaway Docker container — --network none, read-only rootfs, non-root,
all caps dropped, CPU/mem/PID limits, repo mounted read-only. Even an RCE in a test is
trapped in the container.
docker build -t greenlock:latest . # build the image once
git diff | python3 -m greenlock.gate path/to/repo - --isolated--isolated is fail-closed: if Docker isn't available it rejects (never silently
downgrades to the unsafe path). Turn it on by default with GREENLOCK_DOCKER=1
(or "docker": "1" in greenlock.json); override the image via --image /
GREENLOCK_DOCKER_IMAGE; force it off for a single run with --no-isolated.
GREENLOCK_DOCKER drives the same strong isolation everywhere (CLI and the MCP
server). A separate, weaker per-command runner (test command in an official language
image) is opt-in via GREENLOCK_VERIFIER_DOCKER — distinct so the two never collide.
All settings resolve as env var → greenlock.local.json → default. The OSS default
endpoint is http://localhost:11434; keep your own endpoint/keys in a local file that is
git-ignored:
Env equivalents: GREENLOCK_OLLAMA_URL, GREENLOCK_WRITER_MODEL, GREENLOCK_QA_MODEL,
GREENLOCK_EMBED_MODEL, GREENLOCK_ESCALATE, GREENLOCK_GEMINI_KEY. Cloud API keys are
read from GEMINI_API_KEY/GOOGLE_API_KEY or a git-ignored .gemini_key file — never
committed.
python3 webapp/server.py 8012 # → http://127.0.0.1:8012When the gate has nothing to verify against (confidence=degraded), Greenlock can
generate characterization tests (golden-master): the model proposes deterministic
scenarios, the truth is captured by executing the current code (not guessed), and
only stable, green-on-baseline tests are kept. harden_and_verify auto-generates them
for the changed files and re-verifies — so a behavior change is caught even in an
untested repo.
from greenlock.testgen import generate_characterization_tests, harden_and_verify
generate_characterization_tests("path/to/repo", "module.py") # → golden-master tests
harden_and_verify("path/to/repo", open("change.diff").read()) # gate + auto safety netBlock non-green commits locally. Either greenlock init (writes a raw git pre-commit
hook), or via the pre-commit framework — add to your
.pre-commit-config.yaml:
repos:
- repo: https://github.com/abaykopenov/greenlock
rev: v0.1.1
hooks:
- id: greenlock # runs `greenlock check --staged` before each commitBlock any PR that breaks green. In the consuming repo's workflow:
on: pull_request
permissions:
contents: read
pull-requests: write # so Greenlock can post the verdict as a PR comment
jobs:
greenlock:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 } # full history → PR diff
- uses: actions/setup-python@v5
with: { python-version: "3.x" }
- run: pip install -e . # install YOUR test deps (oracle runs your suite)
- uses: abaykopenov/greenlock@v0.1.1 # the gate: merge or block (pin to a release)The step exits non-zero on reject, so with branch protection the PR can't merge. It
also writes a job summary and (on reject) posts the verdict + reason as a PR comment
(needs pull-requests: write; disable with comment: "false").
Expose the gate as a tool any MCP client (Claude Code, Cursor, …) can call:
pip install "greenlock[mcp]"
greenlock-mcp # stdio MCP server; tools: verify_patch, harden_and_verify, generate_characterization_testsA non-circular benchmark (independent test suites as anchors + adversarial tasks proven to break them) measures the headline metric on every run:
python3 -m benchmarks.run_bench pricing # Python
python3 -m benchmarks.run_bench node_pricing # JavaScriptAlpha. The gate (closed-world + oracle + regression; gate-only & generate-and-gate),
characterization testgen, the web UI, an MCP server, and a CI Action all work today.
- gate — closed-world + oracle + regression, gate-only & generate-and-gate.
- testgen — characterization tests so untested code gets a safety net.
- delivery rails — MCP tool (
greenlock-mcp) + GitHub Action (action.yml). - nested-package & src-layout support in
testgen. - [~] closed-world for tree-sitter languages — Go & Rust (conservative, zero false positives); other languages stay oracle-only until validated.
- danger-check — rejects patches that introduce
eval/exec/os.system/subprocess/test-environment detection, before the oracle runs (see SECURITY.md).--trust/GREENLOCK_TRUSTmakes it advisory (non-blocking) for trusted authors / self-CI. - execution isolation — opt-in
--isolated(orGREENLOCK_DOCKER=1) runs the whole gate inside a locked Docker container (--network none, read-only rootfs, non-root, dropped caps, CPU/mem/PID limits, repo mounted read-only), fail-closed if Docker is unavailable. The real fix for untrusted-code RCE. - more tree-sitter languages; PyPI release; dashboard; on-prem packaging.
Greenlock executes your test suite to produce a verdict, and its guarantee targets accidental breakage — not a malicious author. See SECURITY.md for the threat model, known attacker capabilities (RCE via tests, closed-world bypass, test-evasion), and mitigations. Run the verifier only in an isolated/ephemeral environment.