Skip to main content

Merge Policy

Whether an Agent may land its own pull request is a policy you configure, not something the platform hardcodes. The default is conservative — Agents open pull requests, humans merge them — and you can loosen it, per Work or per Agent, as far as you are comfortable.

Note the distinction: opening a pull request is an Agent permission (canOpenPullRequests). Merging one is this policy.

The five knobs

FieldPlatform defaultMeaning
allowAgentMergefalseMay an Agent land a pull request at all?
requireGreenGatetrueThe run's quality gate must be green first.
requireHumanApprovaltrueA recorded human approval must exist first.
allowedMergeMethods['squash']Strategies an Agent may use. An empty list refuses every merge.
protectedBranches['main','master','develop','stage']Branches an Agent may never merge into. Matched case-insensitively.

Four scopes, resolved field by field

A policy can be stored at four scopes. Least specific first:

platform default < tenant < organization < Work < Agent

Resolution is a deep merge, not a replace: the most specific scope that declares a field wins for that field. A Work that sets only allowAgentMerge: true still inherits requireGreenGate, requireHumanApproval, allowedMergeMethods and protectedBranches from the organization, then the tenant, then the platform default. An omitted or null key means "inherit" — never "false".

Writing a policy

Each scope accepts an additive optional mergePolicy object on its existing update endpoint:

  • PATCH /api/works/:id — the Work scope
  • PATCH /api/agents/:id — the Agent scope (most specific)
  • PATCH /api/organizations/:id — the organization scope

Pass null to clear a scope's override entirely and inherit everything. The tenant scope is resolvable and storable but has no HTTP write surface today — an operator-level ceiling there is set out of band.

Previewing a policy

GET /api/merge-policy/resolve?workId=…&agentId=…

returns the complete effective policy, the source (the most specific scope that contributed anything, or default), and the full chain — least to most specific, starting at the platform default, with the fields each link actually contributed.

The chain matters: "Agents may not merge here" is only actionable if the answer also says where that came from and which knob to change.

Refusals

When a merge is refused, the decision carries a stable machine-readable code alongside the human explanation:

CodeMeaning
agent-merge-disabledallowAgentMerge is false at the winning scope.
protected-branchThe pull request's base branch is protected.
target-branch-unknownBranches are protected but the target could not be determined — fails closed.
merge-method-not-allowedThe requested strategy is not in allowedMergeMethods.
gate-not-greenrequireGreenGate is on and the gate is not green.
human-approval-requiredrequireHumanApproval is on and no approval is on record.

Every path that could land a pull request on an Agent's behalf routes through one decision point, so the answer is the same wherever it is asked.

A worked example

You want Agents to land their own work on a Work, but only when the tests pass and never onto main:

{
"mergePolicy": {
"allowAgentMerge": true,
"requireHumanApproval": false
}
}

requireGreenGate and protectedBranches are left out, so they keep the platform defaults — green gate required, main still protected.