Record default-branch protection as a B2 requirement
CI / checks (pull_request) Successful in 39s
CI / checks (push) Successful in 32s

B2 specified the service account and its scoped token but never said
the project repo's default branch must be protected — which turns out
to be the load-bearing part of the PR gate.

In Gitea, write access includes merging pull requests, and repository
ownership does not restrict merging either. So an agent with the
access it needs to push branches can also merge its own PR, and the
review gate holds only because the agent's code does not call the
merge endpoint. That is the advisory guarantee A4 exists to rule out.

Verified rather than assumed: before protection was configured, the
agent's own token merged its own PR (HTTP 200). With protection it is
refused (405), as is a direct push to the default branch. The
amendment says to assert both, so relaxing the protection later fails
a test instead of silently removing the gate.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
dongarandClaude Opus 5 committed 2026-09-07 22:48:42 +00:00
1 parent 914bc26221
commit 846e5494ca
1 file changed
+19
+19
View File
@@ -550,6 +550,25 @@ PR_OPEN
- *Outbound:* `git` + Gitea API to push branches and open PRs, via a dedicated
Gitea **service account** with a scoped token. Record any runnable test service's
URL/port in the PR description.
- **The project repo's default branch must be protected, and this is what makes
the PR gate real.** The service account needs write access to push branches —
and in Gitea, *write access includes merging pull requests*. Repository
ownership does not help: an account that merely collaborates can still merge,
and one that owns the repo can merge and delete it. So without branch
protection the agent can merge its own PR, and the review gate holds only
because the agent's code does not call the merge endpoint. That is precisely
the advisory guarantee A4 exists to rule out.
- Required on the default branch: **block direct pushes** by the agent
account, and **restrict merging** to the operator (a merge whitelist, or
required approvals that the agent's own account cannot satisfy).
- Feature-branch pushes are unaffected, so the agent's normal workflow is
unchanged.
- **Verify it rather than assume it.** The property is observable from
outside: the agent's own token must be refused when it attempts to merge
its own PR and when it pushes to the default branch. Assert both, so that
relaxing the protection later fails a test instead of silently removing
the gate. (Discovered the hard way — before protection was configured, the
agent's token merged its own PR successfully.)
- *Inbound:* react to PR comments, commits, pushes. **Poll** the Gitea API
(15–30s) rather than take webhooks, to keep the VM outbound-only. Intake is
structured JSON — never via a chat bridge.