Let the agent merge its PRs once the operator has approved them #65

Closed
opened 2026-09-29 16:33:43 +00:00 by dev-agent · 1 comment
Collaborator

From the first live end-to-end run: the operator approved the milestone PR in Gitea's review UI, and then still had to merge it and tell the agent project complete or next milestone by hand.

Proposal: once an operator approval is recorded on the PR, the agent merges it itself and moves on: next milestone, or closing the task after the last one (ties in with #61).

This needs a spec change first. B2 currently requires that the agent's token be unable to merge its own PR, with a test asserting the refusal, so that the review gate is enforced by Gitea and not by the agent's code choosing not to call the merge endpoint (A4: gating is structural, not advisory). The same section already names the protection mode that allows this proposal without weakening that: required approvals that the agent's own account cannot satisfy.

  • Branch protection on the default branch: required approvals ≥ 1, with the approvals whitelist restricted to the operator; direct pushes stay blocked. Gitea then refuses any merge, the agent's included, until an operator has approved. The merge whitelist can then include the agent account.
  • Dismiss stale approvals on new commits, so an approval can't carry over to code pushed after it (e.g. a later review fix).
  • The B2 verification test changes from "the agent's token is refused when merging" to "the agent's token is refused when merging without an operator approval, and when pushing to the default branch". It keeps asserting the property from outside, so relaxing the protection still fails a test.

Agent side: the review poller already watches the PR (see #59 for after restarts). On an approval review from an operator login, it merges through the Gitea API, records the merge as an intent (A2/A3, like opening the PR, so a crash mid-merge is reconciled against Gitea), and transitions. If Gitea refuses (e.g. protection or checks not satisfied), the agent reports the refusal and waits; it never retries around it.

Open questions for review:

  • Should the merge also wait for CI checks (a required status check in the protection)?
  • What should "approved" mean when a review approves but also leaves comments: merge, or address the comments first?

🤖 Generated with Claude Code

https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE

From the first live end-to-end run: the operator approved the milestone PR in Gitea's review UI, and then still had to merge it and tell the agent `project complete` or `next milestone` by hand. **Proposal:** once an operator approval is recorded on the PR, the agent merges it itself and moves on: `next milestone`, or closing the task after the last one (ties in with #61). **This needs a spec change first.** B2 currently requires that the agent's token be **unable** to merge its own PR, with a test asserting the refusal, so that the review gate is enforced by Gitea and not by the agent's code choosing not to call the merge endpoint (A4: gating is structural, not advisory). The same section already names the protection mode that allows this proposal without weakening that: *required approvals that the agent's own account cannot satisfy.* - **Branch protection on the default branch:** required approvals ≥ 1, with the approvals whitelist restricted to the operator; direct pushes stay blocked. Gitea then refuses any merge, the agent's included, until an operator has approved. The merge whitelist can then include the agent account. - **Dismiss stale approvals** on new commits, so an approval can't carry over to code pushed after it (e.g. a later review fix). - **The B2 verification test changes** from "the agent's token is refused when merging" to "the agent's token is refused when merging **without an operator approval**, and when pushing to the default branch". It keeps asserting the property from outside, so relaxing the protection still fails a test. **Agent side:** the review poller already watches the PR (see #59 for after restarts). On an approval review from an operator login, it merges through the Gitea API, records the merge as an intent (A2/A3, like opening the PR, so a crash mid-merge is reconciled against Gitea), and transitions. If Gitea refuses (e.g. protection or checks not satisfied), the agent reports the refusal and waits; it never retries around it. **Open questions for review:** - Should the merge also wait for CI checks (a required status check in the protection)? - What should "approved" mean when a review approves but also leaves comments: merge, or address the comments first? 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
Author
Collaborator

Raised again by the operator during the first live session of the new loop (2026-10-07): the agent should be able to merge approved PRs.

Updated for the architecture now in force (docs/Architecture.md):

  • D5: "Merging follows the trust dial (L3). Today the operator merges. If the dial lets the agent merge its own PRs once checks pass, branch protection must require an approval the agent's account can't give, so Gitea enforces the rule."
  • D4: "Protection is verified, not assumed: the agent's token must be refused when merging without the approval the trust dial requires." The new-project checks (#108) already verify that merging the agent's own PR without approval is refused.
  • Development Plan, M3.3, the trust dial: "Per project (D8); agent merging behind branch protection". This issue belongs there.

On the new loop it would look like this:

  • The trigger: the PR watch already sees reviews (review_notes). An operator's approval, with checks green, on a PR the agent is waiting on (wait: merge), and a project whose trust dial allows agent merges, makes the agent merge it. The record closes on merge ("Closes #N"), and the loop goes on: the next milestone, or close_item.
  • The protection: branch protection must require an approval the agent's account can't give. That's already true for the test repositories (make_repo) and checked for new projects.
  • What stays: the agent never merges without that approval, and the project-level setting decides whether it may merge at all.
Raised again by the operator during the first live session of the new loop (2026-10-07): **the agent should be able to merge approved PRs.** Updated for the architecture now in force (`docs/Architecture.md`): - **D5:** "Merging follows the trust dial (L3). Today the operator merges. If the dial lets the agent merge its own PRs once checks pass, branch protection must require an approval the agent's account can't give, so Gitea enforces the rule." - **D4:** "Protection is verified, not assumed: the agent's token must be refused when merging without the approval the trust dial requires." The new-project checks (#108) already verify that merging the agent's own PR without approval is refused. - **Development Plan, M3.3, the trust dial:** "Per project (D8); agent merging behind branch protection". This issue belongs there. **On the new loop it would look like this:** - **The trigger:** the PR watch already sees reviews (`review_notes`). An operator's *approval*, with checks green, on a PR the agent is waiting on (`wait: merge`), and a project whose trust dial allows agent merges, makes the agent merge it. The record closes on merge ("Closes #N"), and the loop goes on: the next milestone, or `close_item`. - **The protection:** branch protection must require an approval the agent's account can't give. That's already true for the test repositories (`make_repo`) and checked for new projects. - **What stays:** the agent never merges without that approval, and the project-level setting decides whether it may merge at all.
Sign in to join this conversation.