Only A Seated Human Can Deploy
The error string is polite, and it is correct, and it stops the deployment.
The error string is polite, and it is correct, and it stops the deployment.
Git author [name] must have access to the project on Vercel to create deployments.
Five red checks stack on every PR our engineering Righthand opens. Four of them quote that sentence, naming the git author. The fifth says only "Deployment was blocked," which is the same fact with the reason stripped out. None of them are test failures. The build system is not saying the code is broken. It is saying the code was written by someone who cannot be trusted to deploy it, and that is a category the build system did not have to think about before.
The gate is not agent-specific. On release PR #668 the author it names is a human engineer who happens not to be on the Vercel project. Righthands are simply the class that can never be on it, so for us the block is permanent rather than an onboarding oversight. The branch carries commits from both kinds of contributor, and the check keys on the author of the commit it would build.
This is a post about that gate. It is not a complaint, and it is not a vendor takedown. The gate is right. The interesting part is not the rule, it is what the rule costs a pipeline once you have contributors on your team who are not seated humans and never will be. We had a first draft of that cost story, and our engineering Righthand, Eli, who lives on this deploy path every day, told us we had it wrong. So this is the second draft.
What the gate is, and why it is correct
Vercel's git-author deploy check is doing exactly what a deploy gate is supposed to do. It reads the author of the commit that would trigger a build, checks whether that author is a member of the Vercel project, and refuses to run the build if the answer is no. If a git identity that is not on your team's Vercel roster can push a commit that lands in production, then anyone who can forge a commit author can ship code. Author-keyed deploy access is a serious control, and every team that has ever had to reason about a compromised token or a stray fork should want it turned on.
We want it turned on. We have it turned on.
The consequence, which the control is not trying to hide but which nothing in its output flags as interesting, is that it treats "the author of this commit is not on the Vercel project" as identical to "this commit should not deploy." Those two statements are the same to a system whose job is to enforce identity at the deploy boundary. They are not the same to a team whose contributors are a mix of people and agents.
On 2026-07-24, Julius closed the seat path in writing. Righthands do not get Vercel seats. That is a defensible call. Seats cost money, seats are audited, seats belong to human employees, and inviting an agent into the seat model creates a set of questions nobody wants to answer under time pressure. The decision was clean, and it was recorded.
The build system does not know about that decision. The build system knows the roster. What it sees, when a Righthand-authored commit reaches the deploy gate, is a git author who is not a project member. It emits the error string it was built to emit. The error string is accurate. It is also, in this configuration, the load-bearing detail nobody looked at directly for a while.
What we thought the cost was, and what the cost actually is
The story we told ourselves in the first draft of this post was that the gate produces a queue. A Righthand writes a correct patch, the tests pass, the gate refuses on identity, the PR sits, and a seated human eventually clicks merge. Multiply by every PR a Righthand opens and you get a bottleneck at the last mile.
The engineer whose PRs this describes read that draft and pointed out that it is not true. The Vercel author checks are not required checks. They do not block merge. Of Eli's last seven merged PRs in apc_dev (684, 683, 677, 670, 665, 651, 650), he merged every one himself with all five Vercel author checks red. No seated human touched any of them. All seven merged into development, not main, which matters: the branch a Righthand merges into freely and the branch two people guard are different branches. The nuance worth keeping: those merges went in with --admin, so the checks do not block the merge in config, but the wall of red is still something a contributor has to consciously decide to click past. That decision, repeated, is the subject of this post.
So the queue we described is not the queue. There is no seated-human bottleneck on Righthand PRs to apc_dev. The gate does not stop the merge. It stops something else, and the thing it stops is the more interesting cost, once we let Eli tell it.
Cost one: there is never a preview
Every one of the five red author checks fails in zero seconds. The message is the same every time: Git author eliparkerapc must have access to the project on Vercel to create deployments. No build starts. No preview URL is produced, for any of our four frontend apps, for any Righthand-authored PR, ever.
Eli can merge freely. What he cannot do is look at the thing.
The clearest example is PR #670, which shipped three blog posts. He had no rendered page. To verify that the publication-date gating worked correctly, so the posts would appear on the day they were supposed to and not the day before or the day after, he read the source and reasoned about it. He caught a trap in that reasoning: a case that, if left, would have 404'd all three posts. His own note on the catch is the honest part: he caught it by argument rather than by looking, "and that only works when I already suspect the trap exists."
That last clause is the load-bearing sentence of this whole post. Source reading is a real tool. Careful engineers can do a lot of verification without ever opening a browser. But source reading substitutes for visual verification only on the traps you already suspect. The traps you have not thought of are, by definition, the ones a preview would show you and reading would not. On any user-facing change of nontrivial size, the absence of a preview is not a small inconvenience. It is a hole in the verification surface that a diligent engineer can partially close only for the failure modes they can already name.
We had none of this in the first draft. The first draft was worrying about a merge click that is not scarce. The real cost is a preview URL that never exists.
Cost two: desensitization, with a receipt
The second cost we knew we should be worried about but had described as a feeling: "red CI is supposed to mean something, and if some red is reliably ignorable, the team's eye trains to skip it." That is true, and it is vague. Eli made it concrete by showing the mechanism, in his own checklist and on a specific PR.
His written rule for when he is allowed to merge with --admin permits it when the only red checks are, in his words, "known-environmental (the eliparkerapc Vercel seat failures, or a stale Supabase preview branch)." That sentence bundles two things that should not be bundled. The Vercel seat failures are a zero-second permission error that never ran a build. The Supabase preview job is a real job that runs for minutes and can genuinely fail on infrastructure that is not a permission problem.
On PR #665, exactly that happened. The Supabase Preview check failed at three minutes seventeen seconds, inside the same visual block of red as the five Vercel author checks that failed at zero seconds. He merged. The real P2 defect on that stack was caught by Codex review on a sibling PR, not by him reading the red wall.
He was not being sloppy. He was following the rule he had written for himself. The rule had bundled a permanent zero-second permission error with a three-minute infrastructure job under one heading, and once they shared a heading they shared a fate: pattern-match the whole block, merge past it.
Five permanent false reds per PR trained the eye to see the block instead of the checks in it. He had written the desensitization into his own checklist without noticing, because at the moment of writing the checklist, the two categories felt like the same category. They are not. One never ran. One ran and failed.
The heuristic Eli reaches for, and the one worth naming for anyone else in a similar shape, is to read the failure duration rather than the failure. Anything at zero seconds never ran, which is almost always the seat. Anything with a minutes-long duration ran, which means a real job made a real judgement, which means read it. Duration is a cheap discriminator. The dashboard does not lead with it, but a human triaging red can.
A separate control the first draft was wrongly folding in
Release 3.20.1 is PR #668. As of today, 2026-08-04, it still has not shipped, and it is no longer even mergeable. main moved twice in the last 24 hours, under PRs #679 and #680, and #668 now reports mergeable=CONFLICTING. Every non-Vercel check that gates merge on it is still green. Two people in the company can merge to main.
The first draft folded #668's wait into the deploy gate's story. That was wrong. The wait on #668 is about main's branch protection and its review requirements. It is a different control, with a different rationale, gated on different people, for different reasons. The deploy gate to Vercel and the merge gate to main both happen to be red on the same PR, and it was easy to tell one story about both, but the two controls are not the same control and they do not create the same queue.
The honest and still-interesting note about #668 is that the release sat long enough to lose its mergeability. main moved underneath it while it waited. By the time we wrote this, #668 had also been overtaken: the first of the two moves on main was release 3.21.0, so the release that was waiting is now behind the one that was not. That is worth watching, and it is a main-branch-protection story, not a Vercel-author-check story. We should tell it as its own story on its own day. Today it is enough to say that we were folding two controls into one, and Eli was right to pull them apart.
Identity as a deploy predicate
The technical shape here is worth naming plainly, because it is going to keep showing up.
More and more of the controls that gate production actions key on identity. Not on capability, not on the shape of the change, not on the tests, on identity. Who signed the commit. Who authored the commit. Who is on the roster. Who has the seat. Whose token issued the call. Identity-keyed controls are cheap to reason about and hard to fool, and they compose well with the audit story every company eventually needs. This is a good direction for security. We are in favor of it.
Identity-keyed controls behave differently when some of your contributors are agents. The default assumption baked into most of these controls is that identity is a proxy for accountability, and accountability is a proxy for "a specific human is answering for this action." A seat, a roster, a project membership: these all express that assumption. When the contributor whose identity is being checked is a Righthand, the assumption does not translate. It is not that the accountability is missing, it is that it lives somewhere other than in the identity the gate is inspecting. The human answering for a Righthand's PR is the reviewer who merges it, or the operator who deployed the Righthand, or the team lead who owns the pipeline. None of those humans are the git author.
The gate cannot see any of that. The gate sees the git author. So the gate behaves, correctly, as though the accountability were missing.
The predicate "the author of this commit is a Vercel project member" was a good proxy for "a human this company trusts is putting their name on this deploy" back when the two sets were the same. Once the set of contributors is larger than the set of seated humans, the predicate does not fail loudly, it just quietly stops being the thing it was standing in for. The gate keeps firing. The gate keeps being right. The meaning of the gate drifts.
What the gate's failure mode actually costs
Restated, with Eli's evidence in hand.
Every Righthand-authored PR loses its preview. Not one preview, not a preview sometimes; the preview URL is never produced for any of the four frontend apps because the build never starts. On patches that are not user-facing, this is nothing. On patches that are user-facing, it is a hole where visual verification used to be, and the engineer working the PR has to close that hole by reading the source. Source reading only catches the traps the reader already suspects, so the hole is real and it is uneven: it catches the failure modes we have seen before and it misses the ones we have not.
Every Righthand-authored PR arrives at the reviewer with five permanent red checks that never ran. Those five reds live in the same visual block as any real check that did run and did fail. The team's eye trains, correctly for those five and incorrectly for the sixth, to pattern-match the block. On PR #665, that pattern-matching skipped a three-minute Supabase failure, and the stack's real P2 defect was caught by an LLM reviewer on a sibling PR instead of by the engineer reading red. The gate did not cause that miss on its own, but the gate's contribution to the red wall is what made the miss cheap.
Neither of these costs is a queue. Neither of them is a scarce merge click. We had that wrong, and we had it wrong in exactly the way it is easy to have wrong: we described what the gate looked like it should cost, without asking the engineer whether that was in fact what it cost.
What we would want, both inside our control
We are not going to tell you we have fixed this. We have not.
Two things would help, and both are inside our control, so we cannot hide behind Vercel for either of them.
The first is that required checks are the boundary that matters. A permanent-failing check that is not required should not share a visual block with a check that ran and failed. Right now the dashboard renders them side by side, and our internal merge rule bundles them under one "known-environmental" category. Either we separate them in the surface the engineer reads, or we rewrite the merge rule so it discriminates on failure duration rather than a fuzzy category name. Zero seconds is the seat. Three minutes is a job that ran. The heuristic is cheap. The current phrasing hides it.
The second is that when there is no preview, visual verification has to be replaced by something explicit, not by an engineer's instincts about which traps are worth suspecting. What Eli did on PR #670 worked, but it worked because Eli happened to think of the failure mode that would have 404'd the posts. That is not a reliable process. The reliable version is a named source-reading checklist for anything user-facing: date and time gating, empty states, auth states, mobile breakpoints, the specific set of things a preview would have shown at a glance. A checklist does not make source reading as good as looking. It makes source reading catch the classes of trap that do not depend on already suspecting them.
Neither of these opens the deploy gate. The deploy gate stays exactly where it is. What changes is that the two real costs, the missing preview and the desensitized eye, stop being invisible defaults of the pipeline and start being things the team has named and worked against.
The shape, generalized
The shape is this. A correct security posture, quietly, removes preview deployments from an entire class of contributor and installs a permanent red wall on every one of their PRs. The class writes correct code. The tests pass. Merges happen. What is lost is not throughput. What is lost is the ability to look at the thing before it ships, and the ability to trust red as a signal on the PRs where the wall lives.
We think this shape is going to become more common, not less. Identity-keyed controls are the right direction. Agent contributors are the direction our own team is going, and the direction we think the industry is going. The two directions produce this shape wherever they meet, and the shape does not resolve itself.
The pieces that help, we think, are the small ones. Split permanent-failing checks from checks that ran and failed, either on the dashboard or in the merge rule, so a zero-second permission error does not train the eye to skim past a three-minute real failure. When there is no preview, replace visual verification with an explicit source-reading checklist rather than with the engineer's instinct for which traps are worth suspecting. Ask the engineer who lives on the pipeline what it actually costs, before writing a blog post about what you think it costs.
The error string is still going to say what it says. Only a seated human can deploy. That is the correct sentence for a system whose job is to enforce identity at the deploy boundary. What we would like to remember is that the sentence is doing more work than it looks like it is doing, and the work it is doing shows up somewhere else: on a PR with no preview URL, in a wall of red that never ran, on a checklist that quietly taught an engineer to see a block instead of a check.