main deploys. A bad merge in a payments service can cost more than
any single ₹50,000 refund ever would, and unlike a CRM amount it cannot be cleanly undone: the merge commit is in the
history the moment it lands.
The problem: “checks passed” is a sentence, not a fact
To decide whether a merge is safe you need to know whether reviews are done, whether checks passed, whether the branch is protected. An agent can tell you all of that. An agent can also be told what to tell you. If the merge depends on the agent’s account of those facts, the merge depends on whoever wrote the text the agent last read. I found this in my own policy. Recorded as G-73 on 2026-09-28:github-pr-approval 1.0.0 approved a merge when five
facts were true, reviews, status checks, branch protection, repository allowed, and a low risk score. All five were
declared by the caller and nothing checked them. An agent with the merge permission could merge any pull request the
GitHub App could reach by sending the right values. Only GitHub’s own branch protection stood in the way.
The idea: the facts can only say no
The fix was not to check those facts better. It was to stop letting them say yes. A merge now needs a person who signs for that exact pull request. The agent’s facts still matter, but only in one direction: if the agent itself reports a failed check, the merge stops.How Parmana does it
The GitHub connector has two actions:github:pr-fetch to read a pull request and github:pr-merge to merge one.
- A signed approval for that pull request. Under
github-pr-approval1.1.0, approved in production on 2026-09-28, a merge is approved only whenmergeApprovedis backed by a signed approval from a trusted approver, for that exact repository and pull request number, for merging, not expired, used once. Without one, every declared fact at its most favourable value is still refused. - Reads need one too. Since 2026-09-30, reading a pull request runs under
github-pr-read1.1.0, which also needs a signed approval for that pull request. - A fresh credential per merge. The connector holds a GitHub App key. For each execution it mints a new installation token that lives about an hour, uses it for that one call, and never caches it. The agent never sees either.
- Narrow by default. Only the merge methods merge, squash and rebase are accepted; anything else is refused before any call. A failed answer from GitHub fails closed.
- The head cannot move under the approval. When the request carries the commit the decision was made on, it is passed to GitHub, and GitHub refuses the merge if the pull request has changed since.
POST /execute, against a mock GitHub server, the tests show a merge with an approval lands once; no approval,
an approval for another pull request, and a reused approval are refused with zero calls to GitHub.
Never run against a real GitHub App
This is the honest headline. The connector has been tested only against a mock GitHub server. A live test suite exists and is gated behind real test credentials; it has never been run with them, in any session. Even when it is, it is designed to do no damage: it checks that a merge request reaches GitHub using a pull request number that does not exist, and that a refusal makes no call. Proving a real merge end to end is left to a deliberate manual run on a throwaway pull request. That has not happened. Whether the GitHub connector is configured in Parmana’s production deployment was not checked when the policy changed on 2026-09-28. And the authorization certification recorded in the claims register (Phase 3D) predates this connector, so it does not cover merges in either direction.What it does not do
- Parmana does not read GitHub’s review or check state. The review, check, branch protection and risk facts are still the agent’s word. They can refuse a merge, never authorize one.
- GitHub branch protection is still your job. It remains a separate, useful control.
- Approvers are not limited to an action. Any trusted approver can sign a merge approval, including one you added for refunds. Today one approver is trusted in production, and I hold that key.
- The GitHub App key is a static secret in the server’s environment. Moving it out is listed, not done.
- It does not stop a direct call. An agent with its own GitHub token can merge without Parmana.