workflows
AI code quality gates: blocking merges until automated checks pass
Automated quality gates enforce standards on AI-generated code by blocking merges until tests, linters, and policy checks pass.

When your CI pipeline reports a 60% pass rate on AI-generated pull requests, you have two choices: manually review every diff before merge, or automate the quality bar. The second option is faster, more consistent, and preserves the velocity gains that justified adopting AI tooling in the first place.
AI code quality gates are pre-merge validation rules that block automated commits until they meet compiler, linter, test, and policy requirements. They extend the familiar CI/CD gate pattern to handle the volume, variance, and velocity of machine-generated code. The goal isn't perfection—it's establishing a floor below which code never reaches main, regardless of how it was authored.
Why human review alone doesn't scale for AI-generated code
A human reviewing 20 AI pull requests per day will miss patterns that a static analyzer catches instantly. Fatigue sets in, context switches pile up, and subtle issues like missing null checks or inconsistent error handling slip through. The problem compounds when multiple agents or teammates use different prompting strategies, producing stylistically divergent code that all "looks fine" in isolation.
Quality gates solve this by running the same validation suite on every commit. The agent loop reruns automatically when a gate fails, iterating until the code passes or hits a retry limit. You still review code, but you're reviewing passing code—diffs that already compiled, passed tests, and met style rules. That shifts human attention to architecture, edge cases, and business logic rather than syntax errors and formatting drift.
The agent loop: plan → constrain → edit → validate → retry
Goatfied's agent loop treats validation as a first-class step, not an afterthought. Here's how it works:
1. Plan: The agent analyzes the task and proposes a series of small, reversible edits.
2. Constrain: Pre-flight checks ensure the plan respects repository policies (no changes to protected files, no introduction of banned dependencies).
3. Edit: The agent writes code, typically in sub-500-line diffs to keep changes reviewable.
4. Validate: Automated gates run: compile, lint, unit tests, integration tests, coverage checks.
5. Retry: If any gate fails, the agent receives the failure log and reattempts the edit, up to a configured limit.
This loop surfaces errors before a human sees the PR. When a Python import fails or a TypeScript type mismatch occurs, the agent fixes it in the next iteration rather than waiting for a reviewer to comment "this won't compile."
Defining your gate hierarchy
Not all checks carry equal weight. A useful gate hierarchy separates mandatory stops from advisory signals:
Mandatory gates (hard blocks):
- Compilation / syntax validation
- Critical linter rules (unsafe operations, deprecated APIs)
- Existing test suite passes at 100%
- No new high-severity static analysis findings
Advisory signals (warnings, not blocks):
- Code coverage delta (flag if new code is untested, but allow merges)
- Complexity metrics (cyclomatic complexity above threshold)
- Performance regression indicators from benchmark runs
Start with a minimal mandatory set—compilation and existing tests—then promote advisory checks to mandatory as your team builds confidence. Blocking on every possible signal upfront creates frustration and workaround culture.
Handling transient failures and flaky tests
AI-generated code doesn't make tests flakier, but it does expose flake you've been ignoring. When an agent retries a failing test three times and it passes on the fourth, you have a test problem, not a code problem. Quality gates force you to fix or quarantine flaky tests, because they become bottlenecks in the automated loop.
Two pragmatic mitigations:
1. Retry limits with exponential backoff: Allow one immediate retry for transient network or timing issues, then require a longer delay. This catches 90% of environmental flake without masking real failures.
2. Flake quarantine: Tag known-flaky tests and run them asynchronously. They don't block merges but generate alerts when they fail, creating pressure to fix or delete them.
The key principle: if a gate can fail for reasons unrelated to code quality, it shouldn't be a hard block. Fix the gate or demote it to advisory.
Example: TypeScript compile gate with error feedback
A simple TypeScript gate looks like this in a CI config:
- name: TypeScript compile check
run: |
npm run type-check 2>&1 | tee type-errors.log
if [ ${PIPESTATUS[0]} -ne 0 ]; then
echo "::error::Type errors detected. See type-errors.log"
exit 1
fi
When this fails, the agent receives type-errors.log as context for its next iteration. It doesn't need a human to interpret "Property 'userId' does not exist on type 'Request'"—it reads the error, updates the diff, and resubmits. Typically this resolves in one retry for straightforward type mismatches.
For Goatfied users, this validation step runs inside the platform's agent loop. The agent sees the failure, the exact error message, and the diff that caused it, then generates a fix without leaving the editing session. You configure the gate once; every agent respects it automatically.
Policy enforcement: dependency and security gates
Beyond syntax and tests, gates can enforce organizational policies. Common examples:
- Dependency allow-lists: Block introduction of packages not on an approved list. Useful in regulated industries or when managing supply chain risk.
- Secrets scanning: Reject any commit containing patterns that resemble API keys, tokens, or passwords.
- License compliance: Fail if new dependencies introduce licenses incompatible with your project (e.g., GPL in a proprietary codebase).
These gates catch mistakes that humans make too, but they're especially valuable for AI agents that lack context on procurement policies or legal constraints. An agent doesn't know your company prohibits AGPL libraries unless you encode that rule in a gate.
# Example: block known problematic dependencies
npm ls --depth=0 --json | jq -e '.dependencies | keys | map(select(. == "left-pad" or . == "event-stream")) | length == 0'
This is a blunt tool, but it stops a category of error at the gate rather than in code review or, worse, in production.
Diff size and reviewability constraints
AI can generate thousand-line PRs as easily as ten-line fixes. Quality gates should enforce a maximum diff size to keep changes human-reviewable. Goatfied defaults to sub-500-line diffs for exactly this reason: large changes are harder to validate, harder to revert, and harder to understand in incident retrospectives.
You can implement this as a pre-commit hook or CI check:
DIFF_LINES=$(git diff --cached --shortstat | awk '{print $4+$6}')
if [ "$DIFF_LINES" -gt 500 ]; then
echo "Diff exceeds 500 lines. Split into smaller PRs."
exit 1
fi
When an agent hits this limit, it should decompose the task into multiple PRs with clear dependencies. Each PR passes gates independently, and you merge them in sequence.
Measuring gate effectiveness
Track two metrics to assess whether your gates are working:
1. Gate failure rate by type: What percentage of AI commits fail each gate on the first attempt? High rates on a specific gate (e.g., linting) indicate a prompting or training issue. Low rates suggest the gate is either too permissive or the agent is learning to avoid that class of error.
2. Human review findings post-gate: What issues do reviewers catch after gates pass? If the same category of bug appears repeatedly in review (say, off-by-one errors or incorrect null handling), consider adding a gate or test pattern to catch it earlier.
These metrics aren't about judging the AI—they're feedback loops for refining your validation pipeline. A gate that never fails might be redundant; one that fails 50% of the time might need better agent context or clearer failure messages.
Self-hosted vs. managed gate execution
Where the gates run is a real decision. Self-hosted CI runners give you full control over runner infrastructure, secrets management, and network policies—critical in air-gapped or highly regulated environments. Managed runners simplify ops overhead and scale automatically with PR volume, but you're trusting the provider's infrastructure for validation. Goatfied itself is a managed service: its cloud agents run on Goatfied infrastructure, and the checks they trigger run wherever your CI already runs.
The choice matters most for gates that interact with internal services (database migrations, integration tests against staging environments). Self-hosted runners can access your VPC and internal APIs directly. Managed runners require exposing test endpoints or forwarding results through a secure tunnel.
Most teams start managed for speed, then move high-security projects or sensitive gates to self-hosted runners as usage grows.