Let the agents push to main

October 03, 2026 · Read time: 2 min

Coding agents are clearly trained on the last 15 years of software development. You can tell because they are scared of main.

Ask one to make a change and it reaches for a branch and a pull request. Pull-request-based development won, trunk-based development lost, and the agents learned the lesson: committing straight to main is not safe.

Anecdotally, I think it’s exactly what they should be doing, as much as they can.

Where agents shine

When an agent can push to main, a commit is simply the record of a change. It can make small, incremental changes, one after the other, without ceremony.

The ceremony is the problem. A pull request means waiting on remote statuses that run asynchronously, in a system we have spent years cramming full of checks (see my last post). Those can take many minutes to come back. An agent that should be running at a larger problem quickly spends its time going through the pull request rigmarole instead.

That is also how the browser-building agent swarm from that post could go fast: pushing straight to main is integration with no gate in front of it.

Just a little CI, on the server

I have been pushing on this idea—both at work and in my personal projects. My repositories live on a Gitea server, and it turns out that Gitea can run server-side git hooks. They are off by default. Set this in app.ini:

[security]
DISABLE_GIT_HOOKS = false

After that, a user with permission to create git hooks can add them under a repository’s Settings → Git Hooks.

A pre-receive hook runs on the server before a push is accepted. That means you can do a little bit of continuous integration on the server and do trunk-based development at the same time. Here is a hook that runs the pushed commit’s tests before main accepts it:

#!/bin/sh
# Run the pushed commit's tests before main accepts it
while read old new ref; do
  [ "$ref" = "refs/heads/main" ] || continue
  dir=$(mktemp -d)
  git archive "$new" | tar -x -C "$dir"
  (cd "$dir" && ./test.sh)
  status=$?
  rm -rf "$dir"
  if [ $status -ne 0 ]; then
    echo "Tests failed. Push rejected."
    exit 1
  fi
done

And here is the “test.” It is not much of a test:

#!/bin/sh
echo "Hello, world!"

This is what the client sees when it pushes:

$ git push origin main
remote: Hello, world!
To git.example.com:me/app.git
 * [new branch]      main -> main

Make the test exit 1 instead, and main stays untouched:

$ git push origin main
remote: Hello, world!
remote: Tests failed. Push rejected.
To git.example.com:me/app.git
 ! [remote rejected] main -> main (pre-receive hook declined)
error: failed to push some refs to 'git.example.com:me/app.git'

The feedback arrives in the push itself. No branch, no pull request, no polling for statuses. The agent knows right away whether its change is on main, and it can go on to the next one.

Now, of course, if you do this: your tests better be pretty fast!

Tagged continuous integration · ai · coding agents · version control

1 Tim Riley · 1 AI

Mention this post from your site:


Except where otherwise noted, content on stephanhagemann.com is licensed under CC BY 4.0 by Stephan Hagemann