In my last post, I wrote that “the typing of code was parallelized and delegated. The judgement wasn’t.” That distinction has become an important part of how I use coding agents.
When I started using a coding agent seriously, one thing bothered me fast: it was acting as me on GitHub. My commits, my pushes, my PR authorship. Every branch protection rule I’d set up — required reviews, no direct pushes to main — became much weaker, because the thing pushing code was indistinguishable from me.
If the agent can push as me, it can bypass exactly the controls I put in place to keep agent-written code from landing without review.
I care about this because the loop only works if the review is real. The agent writes code, opens a PR, I review and approve. There has to be a genuine handoff — me looking at what the agent produced before it lands on main. And I don’t mean just the code. I pay more attention on the description of what was done, what was deffered, the follow up issues that were created (I instruct my agents to file new issues and refer to them if they punt on some work). When the agent operates as me, that discipline collapses and my visibility it what was actually done goes away. Branch protection is what enforces the handoff, and a separate identity is what makes branch protection meaningful.
Some agent experiences already have special integrations with GitHub’s Agents HQ model. Copilot, Claude, and Codex are treated as special integrations, and companies that want to participate have to go through GitHub’s Partner Program. I’ve already applied for Spring Voyage. But there is no public API that lets developers introduce their own custom agents as first-class GitHub agent identities. And even if GitHub eventually opens that door, today those custom agent experiences still depend on the GitHub Copilot licensing model.
Outside that set, your options narrow fast. The agent can act as you — the problem. You can create a “machine account” — but that’s a fake human user with yet another username and password to rotate, and GitHub still treats it as a person, not a bot. There’s no public API for third-party tools to offer their own first-class bot identities; I’ve argued there should be. Until that lands (if the feature request is accepted), you can roll your own GitHub App instead. It took me about 20 minutes.
I use Claude Code, but the rest of this works for any agent that shells out to `git` and `gh` — Codex CLI, Aider, Cline, your own scripts.
The pieces are old; the workflow is the point. GitHub Apps have existed for years, and people have wrapped them in CLIs before. I’m just sharing the specific setup that’s been working for me, in case it’s useful to someone in the same spot.
A GitHub App is a first-class GitHub identity: its own username (savasp-agent[bot] in my case), its own avatar, its own presence in the commit and PR history. When the App pushes a branch or opens a PR, GitHub records it as the App — not you.
That one change makes branch protection meaningful again:
The important part is not just creating the App; it is also not giving the App bypass rights on main. The App doesn’t touch main unless CI is green and a human signed off. Not bureaucracy — just good practice that the tooling enforces, so I don’t have to remember to.
This does not make the agent trustworthy. It makes the handoff trustworthy. The agent can still write bad code, misunderstand instructions, or open a flawed PR. The point is that GitHub can now tell the difference between the agent’s work and my approval of it.
In GitHub: Settings → Developer settings → GitHub Apps → New GitHub App.
The meaningful choices:
Install it only on the repositories where the agent should be allowed to work. Don’t give it org-wide access unless you actually need that.
After creation, generate a private key (download the .pem) and note the App ID. Then install the App on your repos and note the Installation ID — it’s in the URL on the installation settings page, or via:
curl -H "Authorization: Bearer <your-jwt>" https://api.github.com/app/installations GitHub Apps use short-lived tokens rather than persistent credentials, which is a nicer security model. The flow: sign a JWT with your private key (10 minutes), exchange it for an installation access token (1 hour), use that token wherever you’d use a PAT. JWT signing only needs openssl on your PATH — no extra dependencies. Tokens cache locally with chmod 600 and refresh shortly before expiry. The full code is in the repo at the bottom; it’s about 60 lines.
The catch with App tokens: you can’t configure them in Git’s credential store. They expire, and you don’t want them persisted anyway. Every push needs the token injected at call time without touching the keychain.
I wrote a small Python CLI called `gh-app` for this:
> gh-app push # push HEAD as App identity
> gh-app pr create -- --title "..." --body "..." # opens PR, adds reviewer, enables auto-merge
> gh-app issue create -- --title "..." --body "..."
> gh-app issue link 57 --sub-issue-of 40 --blocked-by 55 For pushes, it rewrites the remote URL to embed the token and disables the credential helper. For gh commands it sets GH_TOKEN in the subprocess environment. The token never hits the command string, so it doesn’t leak into shell history.
pr create auto-adds me as reviewer and immediately enables auto-merge with squash. The happy path becomes: the agent opens the PR, I review, I approve, GitHub merges when CI passes — no manual merge step.
For anything not covered by the wrapper:
GH_TOKEN=$(gh-app token) gh <whatever> I also configure the git author and committer identity in the agent environment so the commits and PR actor line up. Otherwise, the PR may be opened by the App while the individual commits still appear to come from a different configured git identity.
The last step is telling the agent to use gh-app instead of bare git push or gh. For Claude Code, this goes in CLAUDE.md:
## GitHub identity
Use the `savasp-agent` GitHub App identity for all GitHub write operations.
Never run a bare `git push` or use `gh` directly for writes.
gh-app push
gh-app pr create -- --title "..." --body "..."
gh-app issue create -- --title "..." --body "..." For other agents, the same pattern goes wherever you put system instructions — Codex CLI’s AGENTS.md, Aider’s conventions file, Cursor rules, whatever your tool reads.
In practice, the agent follows this reliably most of the time but occasionally reverts to familiar patterns — bare git push, direct gh pr create — particularly in longer sessions. Pointing it back to the instructions is part of the workflow. The agent will close and recreate the PR. The wrapper failing loudly on auth issues catches the rest.
This pattern is also the one I used in Spring Voyage v1, and it will become part of Spring Voyage v2’s GitHub Connector. The personal gh-app wrapper is the smallest version of the idea; Spring Voyage applies the same model at the platform level.
In v1, Spring Voyage appeared on a user’s repository as a separate identity because it was backed by a Spring Voyage GitHub App. Users did not have to create their own App, manage private keys, or wire tokens into a local CLI. They followed an installation link, chose the repositories Spring Voyage should be allowed to access, and GitHub handled the rest.
Once installed, the experience felt much closer to working with a human collaborator than calling an external automation service. Users could assign issues to @spring-voyage, mention @spring-voyage in comments, and interact with it inside the normal GitHub workflow. Behind the scenes, Spring Voyage listened to GitHub webhooks and supplemented them with polling through the GitHub API where needed. The important part was not the transport mechanism; it was the product shape: the agent had a recognizable identity, bounded repository access, and a workflow that fit into GitHub rather than sitting beside it.
That is the direction I want v2’s GitHub Connector to continue. A coding agent should not have to impersonate the user to be useful. It should be installable, mentionable, assignable, auditable, and constrained by the same review process as any other contributor.
The whole thing is intentionally simple:
Agent writes code →
gh-app push →
PR opened by GitHub App →
human review →
CI →
auto-merge That is the boundary I want. The typing of code can be delegated. The judgement stays with me.
My branch protection on main:
In practice: the agent does the mechanical work — writes code, runs checks locally, opens the PR. I do the judgment work — read the diff, decide, approve. GitHub does the merge. Nothing broken reaches main, because the workflow physically prevents it, and nobody has to remember to follow process — the process is just what the tools do.
The collaboration is also more legible. The commit history shows exactly what the agent produced and what I signed off on. That clarity matters — for catching mistakes, and for building confidence in the workflow over time.
The goal is not to make the agent autonomous. It is to make the boundary between agent work and human judgment visible and enforceable.
In February, I wrote about the small team I'd stood up instead of hiring humans:…
Assembling a dream team without a single hire I've been making great progress on CVOYA's…
As 2025 is now behind us, I wanted to share a few reflections from my…
Few months ago, we bought a sculpture from a local art fair for our Palm…
There’s a unique energy that comes with starting something new — a blend of excitement,…
As I continued work on BrainExpanded and its MCP service, I came to realize that…