A push that dies because a pre-push hook caught a lint violation and a push that dies because your credentials expired look identical in most tools: a red toast, a vague "push failed," and you're on your own to figure out which one it is. Influxx treats them as the different problems they are. A new classifier inspects exactly why a push was rejected, and only when the cause is a local hook — not a bad password, not a protected branch, not a stale ref — does it offer a one-click AI fix scoped to that specific failure.
The Problem With One Error Message for Every Kind of Failure
Push failures aren't one category of problem wearing different disguises. They're several unrelated problems that happen to surface through the same git exit code. A pre-push hook failing because ESLint caught an unused import is a local, fixable, deterministic problem — the fix is usually mechanical, and an agent with the right context can often resolve it without you touching the keyboard. An expired GitHub token, a push rejected because the branch is protected, or a non-fast-forward error because someone else pushed first are none of those things. They require a human decision — re-authenticate, open a PR instead, pull and rebase — not a code fix.
Most git tooling collapses this distinction because it's easier to build one generic "push failed" handler than several specific ones. The cost of that shortcut lands on the developer: every failure gets the same shallow error text, and if a tool bolts on an "ask AI" button to make up for it, that button appears for auth failures and protected-branch rejections too — cases where an AI agent can't do anything useful and burning a model call just adds latency and confusion.
Influxx's Source Control panel takes the opposite bet: build a narrow detector that only recognizes the one failure mode where an automated fix is both possible and safe, and say nothing extra for everything else.
Two Failures That Look Identical From the Outside
Both scenarios below produce a non-zero exit code from git push and, without inspection, read the same to any tool watching stdout/stderr:
- Hook-blocked push: a local pre-push hook runs lint, tests, or a commit-message check, decides the push shouldn't proceed, and exits non-zero — the remote never even sees the push attempt.
- Remote-rejected push: the push reaches the remote, and the remote (or the user's own credentials) is the reason it fails — expired auth, a protected branch policy, a pre-receive hook on the server side, a non-fast-forward because the remote has commits you don't, or a submodule reference the remote won't accept.
The distinction matters because only the first category has a fix that lives entirely inside your working tree. Everything in the second category needs information Influxx doesn't have and shouldn't guess at — your credentials, the repository's branch protection rules, or the state of a remote you don't control. Offering an "AI Fix" button on a protected-branch rejection wouldn't just be unhelpful, it would actively mislead the developer into thinking there's a code-level fix for a policy-level block.
"The easy version of this feature is a button that says 'Ask AI' next to every red toast, and we prototyped exactly that internally. It felt worse, not better — you'd click it on an auth failure, watch an agent spin up, and then it would just tell you to log in again. That's slower than reading the error yourself. The only way to make the button trustworthy is to make it disappear when it can't help."
— Priya Ramaswami, Staff Engineer, Source Control at ETAPX
How the Classifier Actually Decides
What Counts as a Hook Failure
The detector is deliberately conservative rather than clever. It doesn't try to parse arbitrary hook output and guess intent — it looks for a small set of explicit signals: pre-push-hook markers in the failure text, the literal "hook declined to push" phrasing that git itself emits, hook-runner output appearing specifically in the context of a push, or lint output surfacing during that same push attempt. If none of those signals are present, the failure is treated as a generic remote error and gets the existing plain-language messaging — no AI Fix button, no scoped prompt, just the error as git reported it.
This narrowness is the point. A classifier that tried to pattern-match "anything that looks like it might be fixable" would eventually misfire on an auth error that happens to mention a word like "reject" in a way that resembles hook output, or on a protected-branch message that contains formatting similar to a linter's. By requiring one of a small number of concrete markers, the detector trades recall for precision — it would rather miss an edge case and fall back to the generic message than wrongly launch an agent against a problem it can't solve.
What's Explicitly Excluded
The exclusion list is as deliberate as the inclusion list. Auth failures, protected-branch rejections, pre-receive hook failures on the remote side, non-fast-forward errors, and submodule push errors are all explicitly carved out and routed to the existing generic remote-error messaging. None of them can trigger the recovery panel, even if their raw text happens to contain a substring that superficially resembles hook output.
- Auth failures need a credential fix, not a code fix — no amount of AI assistance changes an expired token.
- Protected-branch rejections are a policy decision by the remote, not a defect in your commit.
- Pre-receive hook failures run on the server, where Influxx has no visibility into what the hook checked or why it declined.
- Non-fast-forward errors mean the remote has history you don't — the fix is fetch-and-reconcile, not a lint patch.
- Submodule push errors point at a nested repository problem outside the scope of the current worktree's failure.
Bounded by Design: The Numbers Behind the Recovery Panel
A feature that reads raw process output and hands it to an AI agent has an obvious failure mode: a hook that misbehaves and floods stdout could balloon the size of what gets sent to the model, along with the latency and cost that comes with it. Influxx bounds this explicitly — the failure-output scan is hard-capped at 64KB of text. A runaway hook that prints megabytes of debug noise still only ever contributes at most 64KB to what the classifier inspects and what an eventual AI fix prompt is built from. That's a deliberate ceiling, not an incidental buffer size.
The second numeric detail worth understanding is how a sync operation is staged. When Influxx runs a sync, it executes in three distinct steps: fetch, then an upstream-status check, then push. Only a failure in that third stage — the push itself — can be marked as hook-blocked. This matters because lint-sounding or hook-sounding text can theoretically surface as noise during an earlier stage (say, a fetch that happens to echo something hook-adjacent), and without stage isolation that text could falsely trip the classifier and launch a recovery panel for a failure that was never actually a push problem at all.
The third number is more of a guarantee than a count: every remote action increments a per-worktree sequence number before it starts. When a push fails, the failure can only display its error and offer recovery if its sequence number still owns that worktree at the time it resolves. If you retry the push, or a newer operation supersedes it, the sequence number moves — and the earlier, slower failure loses the right to render anything, even if its network call happens to finish last. This closes a real race: without it, a stale failure from an old attempt could pop up after you've already fixed the problem and pushed successfully, showing you an error for a version of the world that no longer exists.
"I have a pre-push hook that runs our full lint config, and I used to dread seeing it fail because the terminal output was a wall of text I'd have to scroll through to find the actual line. Now the panel just tells me a hook blocked the push and gives me a fix button. On an auth failure last week it correctly didn't show that button at all, which honestly told me more than the button would have — I knew instantly to go check my token instead of wasting time."
— Marcus Deleon, backend engineer at a logistics software company, Influxx user
Staying Honest About Branch State
Because the panel is attached to a specific failure captured at a specific moment, it has to account for the world moving on underneath it. If the branch shown in the UI has diverged from the branch that was current when the failure was captured — because you checked out something else, or the worktree's branch changed in the interim — the recovery panel hides itself rather than launching an agent against context that no longer matches what's actually checked out. Combined with the sequence-number guard, this means the panel only ever offers to fix the exact failure, on the exact branch, that's still live. It doesn't try to be clever about reconciling stale state; it just declines to show up when it can't guarantee correctness.
One Click, Not a New Subsystem
When the classifier does identify a hook-blocked push, clicking AI Fix doesn't spin up a bespoke push-recovery pipeline. It reuses the same generic agent-launcher and command-template builder that already power Influxx's commit-failure recovery feature — the flow that helps you fix a commit that gets rejected by a commit-time hook. The push case supplies its own scoped failure text — bounded to that 64KB window and tied to the push stage specifically — into the same launcher that commit recovery already uses.
That reuse is a design choice as much as an engineering shortcut. Building a second, push-only agent-launch pathway would mean two places to fix bugs, two places to add new agent CLIs, and two slightly different behaviors for what should be conceptually the same action: "an agent looked at exactly why a hook blocked me, and now it's fixing that."
"We keep coming back to the same principle across the product: an AI action is only as trustworthy as the boundary around what triggers it. It would be trivial to ship a button that fires an agent at any red error text and call it a feature. What actually earns a developer's trust is a system that can tell you, with evidence, why it's confident this one is fixable and that one isn't — and reuses existing, already-tested machinery to do the fixing rather than growing a new surface for every failure type we discover."
— Elena Voss, VP of Engineering at ETAPX
Where This Sits in Influxx's Settings
The AI Fix button for blocked pushes isn't a separate toggle — it's gated behind the same Source Control AI settings that govern other AI-assisted git actions in Influxx. If that setting is disabled for a given repository, the blocked-push notice and the underlying error details still render normally; you still get the classifier's determination that this was a hook failure rather than a remote rejection. What disappears is only the AI Fix button itself. This keeps the classification and the AI action decoupled: the detector's job is to tell you accurately what happened, and that job runs regardless of whether you've opted into letting an agent act on it.
Why Restraint Is the Feature
It's tempting to measure an AI feature by how many situations it covers. This one is deliberately narrow, and that's what makes it usable. Because the trigger conditions are explicit — specific markers, a specific pipeline stage, a specific text-size bound, a specific sequence-number check, a specific branch-match requirement — a developer can reason about when the button will and won't appear, instead of learning through trial and error which failures a fuzzy "ask AI" button actually helps with. When the button shows up, it means something: this failure is local, it's bounded, it's still relevant to what's currently checked out, and an agent has a real shot at fixing it without any information Influxx doesn't already have. When it doesn't show up, that's also information — go check your credentials or your branch protection rules instead of waiting on a model call that was never going to help.
Frequently Asked Questions
Does the AI Fix button ever appear for authentication failures?
No. Auth failures are explicitly excluded from the hook-blocked classifier and always fall through to the existing generic remote-error messaging, with no AI Fix option offered.
What happens if my pre-push hook prints an enormous amount of output?
The classifier and the resulting AI fix prompt only ever see up to 64KB of the failure text, regardless of how much the hook actually printed. A verbose or runaway hook can't inflate the size of what gets scanned or sent to the agent.
Can a failed pull or fetch accidentally trigger the push recovery panel?
No. A sync runs as fetch, then an upstream-status check, then push, and only a failure in the push stage itself can be marked hook-blocked — a lint-sounding string surfacing during an earlier stage can't falsely trigger the panel.
What if I retry the push before the recovery panel finishes evaluating the first failure?
Every remote action increments a per-worktree sequence number when it starts, and a failure can only display if its sequence number still owns that worktree. A slower, stale failure can't overwrite the outcome of a newer retry or a success that already landed.
Does the panel still show up if I switch branches after the failed push?
No. If the branch currently shown in the UI has moved on from the branch captured at failure time, the recovery panel hides itself rather than launching an agent against outdated branch context.
Is this a separate AI system from the rest of Influxx's source control features?
No. It reuses the same generic agent-launcher and command-template builder that already power the existing commit-failure recovery feature, rather than a parallel subsystem built specifically for pushes.
What if I have Source Control AI turned off for a repository?
The blocked-push notice and the underlying error details still display normally, but the AI Fix button itself does not appear. The classification runs independently of whether the AI action is enabled.
The interesting engineering decision here wasn't building an AI fix button — it was building the boundary that decides when the button is allowed to exist, and trusting that boundary enough to say nothing at all everywhere else.

