Gitea dismisses an approval made stale by a push in a job it queues on the push (services/pull AddTestPullRequestTask, v28.1.0), and its merge check counts approvals not yet dismissed. Measured here, the approval stands for 0.15-0.23 s after the push; the test's merge came a couple of API calls later, so it usually lost the race, and once won it. The test now waits for the dismissal, with a timeout, before expecting Gitea's refusal, and checks the agent's own rule at once: the PR's head is read from the branch, so the approval is for an older commit straight away. Also notes in the Development Plan why the helper loop doesn't compact. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J5Utyfzw7gF7UhjRDEtjTk