Skip to content
Goatfied

deployment

Shipping agent-generated PRs safely: gates, budgets, and small diffs

Goatfied deployment playbook covering pre-merge checks, diff review tactics, and retry budget tuning to ship agent-generated PRs safely to production.

2026-07-258 min readBy Goatfied
Shipping agent-generated PRs safely: gates, budgets, and small diffs

You've configured the agent, wired up the compile gates, and watched it produce a syntactically valid PR. Then someone merges without checking the diff closely, and production breaks in a way your linter didn't catch. Or the agent churns through five retry loops because a transitive dependency version wasn't pinned, burning context window and developer patience. Deployment playbooks exist to close the gap between "it runs" and "it ships safely."

This guide focuses on the mechanical steps that turn Goatfied's agent loop—plan, constrain, edit, validate, retry—into a repeatable workflow for getting PRs from draft to main. We'll cover pre-merge checks that catch silent logic errors, diff review tactics that surface unintended changes, and retry budget tuning so you're not waiting ten minutes for an agent to discover a missing import.

Wire compile-time gates before the first PR

Goatfied validates every edit against compile, lint, and test steps you define. The default agent loop won't propose a diff that fails `tsc --noEmit` or `cargo check`, but only if you've told it those commands matter. Start by declaring your validation pipeline in `.goatfied/validate.yml`:


steps:

  - name: typecheck

    run: npm run typecheck

    required: true

  - name: unit-tests

    run: npm test -- --coverage --maxWorkers=2

    required: true

  - name: integration-smoke

    run: npm run test:integration -- --bail

    required: false

Mark critical steps `required: true` so the agent can't proceed if they fail. Non-required steps still run and report results, giving you signal without blocking iteration. If your codebase has flaky tests, keep them non-required initially and track the failure rate; promote them once stability improves.

The validation step runs inside the agent loop after each edit, so fast feedback matters. If your full test suite takes eight minutes, carve out a subset that exercises core invariants in under thirty seconds. You can always run the complete suite in CI after the PR opens.

Constrain the scope before the agent starts

The plan phase translates your instruction into a sequence of edits. Without constraints, an agent asked to "add user authentication" might rewrite your entire router, introduce a new ORM, and update twelve unrelated files. Constraints act as guardrails:


goatfied plan \

  --files src/auth/*.ts,src/middleware/session.ts \

  --max-files 4 \

  --forbidden-patterns "database/migrations/*,config/production.yml"

This tells the agent it can only touch files matching the glob, limits the total file count, and blocks changes to migrations or production config. The agent will still propose a plan, but Goatfied rejects edits that violate the constraints before they reach validation.

Use `--max-diff-lines` to cap the size of individual changes. A 600-line diff in a single file often signals the agent is rewriting logic instead of making a surgical fix. If you hit the limit, refine your prompt or break the task into smaller instructions.

Review diffs for unintended side effects

A passing validation suite confirms syntax and known invariants, not correctness. The agent might add a null check that satisfies the type checker but changes runtime behavior in a way your tests don't cover. Manual diff review catches these.

Goatfied surfaces diffs as standard Git patches. Before merging, check:

  • **Deleted lines you didn't expect.** The agent sometimes removes code it considers dead, but "unused" according to static analysis isn't always unused at runtime—think reflection, dynamic imports, or feature-flagged paths.
  • **Changed variable names or reordered imports.** Small cosmetic changes are fine, but widespread renaming in a PR scoped to "fix validation bug" suggests the agent drifted.
  • **New dependencies.** If the agent added a package, verify it's necessary and inspect the version. Auto-installed dependencies often default to `latest`, which can introduce breaking changes.

Use `git diff --stat origin/main` to see the file-level summary. If a file outside your original scope changed, investigate why. The agent may have followed a legitimate dependency chain, or it may have misunderstood the constraint.

Set retry budgets to avoid infinite loops

When validation fails, Goatfied re-plans and retries. This recovers from fixable errors—missing imports, typos, incorrect function signatures. But if the failure is structural (conflicting type requirements, impossible constraint), retries burn time without progress.

Configure retry limits in your agent invocation:


goatfied apply \

  --max-retries 3 \

  --retry-backoff exponential \

  --timeout 600

Three retries handle most recoverable mistakes. Exponential backoff gives the agent more time to reason on later attempts if the first fix was close. The ten-minute timeout prevents runaway loops if the agent gets stuck.

Log the failure reason on each retry. If you see the same validation error three times in a row with different attempted fixes, the prompt likely needs clarification or the constraint is contradictory. Stop the agent, inspect the partial diff, and adjust.

Pin agent context to relevant modules

The agent's plan quality depends on what it can see. By default, Goatfied indexes your entire repository, but large codebases create noisy context. If you're fixing a bug in the payment processor, the agent doesn't need to parse your entire frontend directory.

Use workspace scoping to limit context:


goatfied plan \

  --workspace services/payment \

  --context-includes "shared/types/*.ts,shared/validation/*.ts"

This focuses the agent on the payment service and explicitly pulls in shared type definitions. Narrower context reduces the chance of hallucinated dependencies and speeds up planning.

For multi-step workflows, persist context between runs with a named session:


goatfied session create fix-payment-bug \

  --workspace services/payment



goatfied apply --session fix-payment-bug "Add retry logic for declined cards"

goatfied apply --session fix-payment-bug "Log decline reasons to audit table"

The session keeps workspace and context settings across multiple apply calls, so you don't re-specify them each time.

Use progressive validation for expensive checks

Some validations—integration tests against a real database, end-to-end Playwright suites—take minutes and aren't practical in the inner agent loop. Run them after the agent finishes, before you merge.

Structure your validate config with tiers:


steps:

  # Fast: runs in agent loop

  - name: typecheck

    run: tsc --noEmit

    required: true

    tier: fast



  # Slow: runs on-demand or in CI

  - name: e2e-smoke

    run: npm run test:e2e -- --smoke

    required: false

    tier: slow

By default, `goatfied apply` only runs `tier: fast` steps. Invoke slow checks manually once the agent loop completes:


goatfied validate --tier slow

If the slow tier fails, you have a clean agent-generated diff to iterate on. The agent's context is still warm, so you can run another apply with a refined prompt referencing the failure.

Commit small, reversible diffs

The agent produces a single consolidated diff by default. For complex tasks, this might bundle multiple logical changes—schema migration, handler update, test additions—into one commit. Break it apart before merging:


# Agent finished, validation passed

git add src/database/schema.sql

git commit -m "Add user_preferences table"



git add src/handlers/preferences.ts tests/preferences.test.ts

git commit -m "Implement preferences GET/POST endpoints"

Smaller commits make bisecting easier if something breaks later, and they align with Goatfied's philosophy of reversible steps. If the second commit causes issues, you can revert it without undoing the schema change.

For teams that require linear history, use `git rebase -i` to split the agent's diff into a clean commit sequence before pushing.

Handle merge conflicts without restarting

If someone merges a conflicting change while the agent is running, Goatfied pauses and surfaces the conflict. Don't discard the agent's work—resolve it:


# Agent paused due to conflict in src/auth/middleware.ts

git checkout --theirs src/auth/middleware.ts  # or --ours, depending on intent

goatfied apply --resume --context "Resolved conflict by keeping upstream CORS change"

The `--resume` flag tells the agent to re-plan from the current state, incorporating your conflict resolution. Include a `--context` note explaining what you chose, so the agent doesn't reintroduce conflicting logic.

For high-conflict branches, rebase onto main before starting the agent. Goatfied works best on a clean base.

Measure agent effectiveness, adjust prompts

After a few dozen PRs, patterns emerge. Track:

  • **Retry rate per task type.** If "add API endpoint" tasks consistently hit max retries, your validation config might be too strict or your prompt template needs more specificity.
  • **Manual edit rate.** If you're tweaking 30% of agent-generated lines before merging, the constraint scope is likely too broad.
  • **Reverted PRs.** If agent-authored changes get reverted more often than human-authored ones, your validation suite has gaps.

Use this data to refine your playbook. Tighten constraints for high-retry tasks, expand validation for high-revert areas, and document successful prompt patterns in your team wiki.

  • [Self-hosted AI coding assistants: a complete Goatfied deployment guide](/blog/self-hosted-ai-coding-assistants-goatfied-deployment-guide)
  • [PR triage automation with risk scoring](/blog/pr-triage-automation-with-risk-scoring)

Related posts

Shipping agent-generated PRs safely: gates, budgets, and small diffs | Goatfied Blog