brain-commit-push.sh reports "PUSH FAILED / NEEDS ATTENTION" on pushes that succeeded
Summary
The generated brain_push() helper treats a rejected push as failure without asking the
remote whether the commit actually landed. When a concurrent pusher wins the race, the
remote rejects our push for a stale expected ref — and the rejection message names our
own commit as the ref's current value, i.e. the work is safe. The helper then tries
git pull --rebase, which aborts on a dirty tree, and exits 4 with:
PUSH FAILED — commit is local-only, NEEDS ATTENTIONThis happened on five consecutive commits in one session. All five were on origin.
The concurrent pusher is gbrain itself: a gbrain serve process watching the checkout
pushes the moment a commit lands, so this is self-inflicted and probably common for
anyone running serve against a source repo.
Evidence
From ~/.gbrain/brain-push.log, one commit:
2026-09-16T04:34:32Z [push] ok master 7ddb4bf
To https://github.com/<user>/<repo>.git
! [remote rejected] HEAD -> master (cannot lock ref 'refs/heads/master':
is at 7ddb4bf46... but expected 23444350...)
error: failed to push some refs
2026-09-16T04:34:33Z [push] rejected; rebase-pull master
error: cannot pull with rebase: You have unstaged changes.
2026-09-16T04:34:33Z [push] LOCAL-ONLY, NEEDS ATTENTION: master @ 7ddb4bf could not reach origin7ddb4bf is the commit being pushed. The remote reports it as the ref's current value in
the same line that declares failure.
Why it matters
The helper's own header states the contract:
THE DURABILITY GUARANTEE: add -> commit -> push, atomically. Refuses to exit 0 without a confirmed push
Exiting 4 on a confirmed push inverts that. Under cron it trains the operator — and any agent reading the log — to ignore the one alarm that is supposed to be unignorable.
Two contributing causes
- A rejected push is not proof of failure. Nothing verifies against the remote.
- The recovery path cannot run on a dirty tree.
git pull --rebaseaborts with "You have unstaged changes", which is the normal unattended state. Notably,bin/x-ingest.shin my own repo already documents this exact trap and uses--autostashfor it.
Suggested fix
After a failed push, ask origin before recovering:
if git fetch --quiet origin "$_branch" &&
git merge-base --is-ancestor HEAD "origin/$_branch"; then
echo "... [push] ok-already-on-remote $_branch $(git rev-parse --short HEAD)" >>"$_log"
return 0
fi…and add --autostash to the recovery pull, plus the same ancestor check once more before
declaring LOCAL-ONLY.
I've been running exactly this locally for a day: commits that previously reported
NEEDS ATTENTION now log ok-already-on-remote and exit 0, and a genuine unreachable-origin
failure still exits 4. Happy to open a PR.
One request either way
The function is marked --- gbrain durability push-retry (generated; one source of truth) ---,
and gbrain sources harden regenerates it, silently reverting local fixes. A documented way
to keep an operator patch across regeneration — or a checksum warning when one is
overwritten — would help regardless of whether this bug is fixed.
Environment
gbrain 0.50.5.0 (install_method: binary) · macOS 26 (Darwin 25.6.0, arm64) · git 2.x · remote GitHub over HTTPS
Source: garrytan/gbrain