A merge request changes one README line. The pipeline still calls a model.
It costs tokens. It adds latency. It tells you almost nothing.
Sound familiar?
If you maintain a small CI setup, this failure keeps showing up. The instinct is to put model-based review everywhere. Then the free tier dies in a week.
The fix isn't another monitor. It's a small decision gate that decides whether a diff deserves a model call at all.
The operator-supplied availability claims for MonkeyCode include free model access and a free server option. I treat those claims as a starting point, not a quota guarantee.
Disclosure: This article was prepared as part of MonkeyCode's product outreach.
Why every diff shouldn't hit the model
Free model access is not infinite. Even if it feels free, there are hidden ceilings.
- Free tiers often cap requests, tokens, or time-based windows.
- Model output variance on trivial diffs adds noise, not signal.
- CI latency grows. A two-second call across a hundred merge requests is real time.
- The highest-value model review is rare, not constant.
If you call a model on every change, you pay the full cost while getting almost none of the benefit. The gate is supposed to fix that.
A three-tier escalation ladder
I use a small decision table. It doesn't need to be perfect. It needs to be boring and predictable.
| Tier | Trigger | Action | Model call? |
|---|---|---|---|
| 0 | Up to 50 added+removed lines, only docs or config suffixes, no sensitive paths | Run lint and skip the model | No |
| 1 | Code or test files touched, 51–400 lines, no lockfile, no migration, no sensitive path | Send one bounded prompt to the free model | Yes, once |
| 2 | Over 400 lines, new lockfile, migration, auth or secret paths | Require human review first. Use a model only to summarize, not to decide | Optional |
The exact numbers are arbitrary. They matter less than the fact that tier 0 never reaches the model.
The code
Here is a plain Python gate. It reads simple diff stats and changed paths.
from pathlib import Path
DOC_OR_CONFIG = {'.md', '.txt', '.yml', '.yaml', '.toml', '.json'}
SENSITIVE = {'auth', 'secret', 'token', '.env', 'migration'}
def classify(diff_stats, changed_paths):
total_lines = diff_stats.get('added', 0) + diff_stats.get('removed', 0)
suffixes = {Path(p).suffix for p in changed_paths}
sensitive = any(
any(word in p.lower() for word in SENSITIVE)
for p in changed_paths
)
if total_lines <= 50 and suffixes <= DOC_OR_CONFIG and not sensitive:
return 0
if (
total_lines > 400
or sensitive
or any('lock' in p.lower() for p in changed_paths)
):
return 2
return 1
def gate(diff_stats, changed_paths):
tier = classify(diff_stats, changed_paths)
if tier == 0:
return {'call': False, 'reason': 'trivial docs or config diff'}
if tier == 2:
return {'call': False, 'reason': 'large or sensitive diff'}
return {'call': True, 'reason': 'single bounded model pass'}
That is the whole decision layer. The model call itself stays behind a placeholder.
MAX_PROMPT_CHARS = 8_000
def call_free_model(diff_text):
prompt = diff_text[:MAX_PROMPT_CHARS]
# Point this at your provider's supported free-model endpoint.
raise NotImplementedError
Why the placeholder? Because the gate should not depend on a specific model name, quota, or endpoint. You can run this same script locally or on a free server option as a sidecar before the model call.
Wire it into CI
The smallest integration is just two steps.
python gate.py > decision.env
source decision.env
if [ "$CALL" = "true" ]; then
python model_job.py
else
echo "Skipping model call: $REASON"
fi
For a GitLab pipeline, put this in a preflight job. The model job only runs when the gate says call: true.
What this gate will not do
Be honest about its limits.
- It does not judge model output quality. A tier 1 call can still return nonsense.
- It can miss a 40-line config change that matters more than 300 lines of formatting.
- It requires thresholds to be maintained as the repo changes.
- It reduces wasted calls, but it does not remove provider rate limits.
Do not use this gate if every diff must be reviewed by a model, or if your repository's docs changes can hide security-critical behavior.
A better place for the saved calls
Take the calls you save at tier 0 and spend them where the signal is high: security paths, new dependencies, and test plan drafts. That is the real payoff.
Try the gate on one repo. Count how many diffs actually reach tier 1. If the answer is fewer than you expected, the gate is doing its job.
Top comments (0)