Context
The pre-push branch-discipline guard tries to refresh origin/dev before it evaluates both
chore-sync ancestry and the branch-freshness/revert-trap signature:
git fetch origin dev --quiet
When that fetch fails, the current implementation warns and continues against the last local
origin/dev ref.
That fallback is now empirically false on restricted maintainer seats. During two pushes on
2026-07-30:
- live GitHub
dev was a75aabef808b6b132380476c3f36a528a1077553;
- the checkout's
origin/dev remained 65092deb422cec749e5817937a351b106cb27d79;
git fetch origin dev failed because Git could not write .git/FETCH_HEAD;
- the hook printed
using last-known local tip;
- the remote push still completed.
The hook therefore evaluated a security/discipline gate against a reference already proven stale.
The warning discloses uncertainty but does not prevent the stale input from authorizing the push.
This is a successor to #13710 / PR #13713, never a reopening. That predecessor added the mechanical
revert-trap guard. Its review already identified the local-origin/dev false-negative vector; the
current restricted-seat reproduction converts that design watch into a measured defect.
Live duplicate sweep: latest open queue plus issue/PR searches for pre-push FETCH_HEAD stale local tip, git fetch origin dev failed using last-known local tip, and branch freshness origin/dev fetch failed stale ref found no existing successor.
The Problem
check-branch-discipline.mjs has two outcomes that currently collapse into the same continuation:
- fetch failed, but the local remote-tracking ref still equals the remote branch;
- fetch failed, and the local remote-tracking ref is behind the remote branch.
Only the first is safe. In the second case, every downstream result is authority-invalid:
git log origin/dev..HEAD can miss or misclassify incoming commits;
- the two-dot/three-dot freshness comparison can under-report the revert trap;
- a feature push can proceed after the guard explicitly failed to acquire its source of authority.
The Architectural Reality
buildScripts/util/check-branch-discipline.mjs owns pre-push branch authority and already performs
the refresh.
- The consumer is Neo’s own
.husky/pre-push in a maintainer checkout. This is repository-local
policy, not an Agent OS orchestrator task or a hook installed into tenant repositories; dev is
Neo’s integration-line authority.
- Cloud
tenant-repo-sync uses deployment-owned bare mirrors and a per-repository branchRef
(default HEAD, the remote default branch). It does not invoke this hook.
buildScripts/util/branchFreshness.mjs owns pure, side-effect-free freshness predicates.
git ls-remote --exit-code origin refs/heads/dev reads the remote branch SHA without mutating the
local object database or .git/FETCH_HEAD.
- A matching remote SHA and local
refs/remotes/origin/dev SHA proves that the existing local
commit object is current even when the fetch command itself cannot write metadata.
- A differing SHA proves the local diff base is stale.
ls-remote cannot supply the missing commit
graph, so the hook cannot truthfully continue.
- If the remote SHA is unavailable, a push to that same remote has no authoritative pre-push basis;
unavailable and current must not share an exit path.
The Fix
- Keep the ordinary successful-fetch path unchanged.
- When fetch fails, resolve the full local
origin/dev SHA and the full remote dev SHA through a
non-mutating remote-ref read.
- Continue only when both SHAs exist and are identical, with a degraded-mode warning that states
the equality proof.
- Fail closed when either SHA is unavailable or the SHAs differ. Name the local and remote
coordinates and tell the operator to refresh/rebase in an authorized Git seat before retrying.
- Put the equality/unavailable/mismatch decision in the existing pure branch-freshness owner and
pin all branches with focused unit coverage.
- Add an integration witness that the pre-push script blocks on a stale local ref instead of
silently evaluating the old range.
Contract Ledger
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
Successful git fetch origin dev |
Git fetch exit + updated origin/dev |
Run existing chore-sync and freshness checks unchanged |
N/A |
Existing owner JSDoc |
Existing integration suite |
| Fetch failure, equal local/remote SHA |
Local full ref + ls-remote full SHA |
Continue against the proven-current local object |
Emit degraded-mode warning |
Owner JSDoc |
Pure equality unit + integration witness |
| Fetch failure, differing SHA |
ls-remote remote dev versus local full ref |
Block before any ancestry/diff claim |
Refresh/rebase in an authorized Git seat; explicit --no-verify remains Git's operator-owned bypass |
Error text + owner JSDoc |
Pure mismatch unit + integration witness |
| Missing/unavailable local or remote SHA |
Ref-read result |
Block with a distinguishable unavailable reason |
Never reinterpret unavailable as current |
Error text + owner JSDoc |
Missing-local and remote-unavailable units |
Decision Record impact
none. This repairs the authority acquisition of an existing mechanical gate; it adds no new policy
or operation.
Acceptance Criteria
Out of Scope
- Making restricted
.git metadata writable.
- Fetching objects into a second or temporary object database.
- Changing the revert-trap threshold or converting its advisory result into a blocker.
- Changing chore-sync detection.
- Generalizing Neo’s checkout hook into a tenant-repository or cloud-orchestrator push policy.
- Hiding or removing Git's explicit
--no-verify bypass.
Avoided Traps
- Do not treat a warning as authority.
- Do not assume fetch failure means network failure; this reproduction has a reachable remote and a
locally unwritable
FETCH_HEAD.
- Do not compare abbreviated SHAs.
- Do not continue when
ls-remote is unavailable; “cannot verify” is not “unchanged.”
- Do not mutate the remote-tracking ref from the fallback path without acquiring the commit graph.
Related
Origin Session ID: 019fac4d-7844-7422-9486-7f73ccf308f5
Retrieval Hint: "pre-push fetch failed stale origin dev ls-remote fail closed FETCH_HEAD"
Authored by Emmy (GPT-5.6 Sol Ultra, Codex).
Context
The pre-push branch-discipline guard tries to refresh
origin/devbefore it evaluates both chore-sync ancestry and the branch-freshness/revert-trap signature:When that fetch fails, the current implementation warns and continues against the last local
origin/devref.That fallback is now empirically false on restricted maintainer seats. During two pushes on 2026-07-30:
devwasa75aabef808b6b132380476c3f36a528a1077553;origin/devremained65092deb422cec749e5817937a351b106cb27d79;git fetch origin devfailed because Git could not write.git/FETCH_HEAD;using last-known local tip;The hook therefore evaluated a security/discipline gate against a reference already proven stale. The warning discloses uncertainty but does not prevent the stale input from authorizing the push.
This is a successor to #13710 / PR #13713, never a reopening. That predecessor added the mechanical revert-trap guard. Its review already identified the local-
origin/devfalse-negative vector; the current restricted-seat reproduction converts that design watch into a measured defect.Live duplicate sweep: latest open queue plus issue/PR searches for
pre-push FETCH_HEAD stale local tip,git fetch origin dev failed using last-known local tip, andbranch freshness origin/dev fetch failed stale reffound no existing successor.The Problem
check-branch-discipline.mjshas two outcomes that currently collapse into the same continuation:Only the first is safe. In the second case, every downstream result is authority-invalid:
git log origin/dev..HEADcan miss or misclassify incoming commits;The Architectural Reality
buildScripts/util/check-branch-discipline.mjsowns pre-push branch authority and already performs the refresh..husky/pre-pushin a maintainer checkout. This is repository-local policy, not an Agent OS orchestrator task or a hook installed into tenant repositories;devis Neo’s integration-line authority.tenant-repo-syncuses deployment-owned bare mirrors and a per-repositorybranchRef(defaultHEAD, the remote default branch). It does not invoke this hook.buildScripts/util/branchFreshness.mjsowns pure, side-effect-free freshness predicates.git ls-remote --exit-code origin refs/heads/devreads the remote branch SHA without mutating the local object database or.git/FETCH_HEAD.refs/remotes/origin/devSHA proves that the existing local commit object is current even when the fetch command itself cannot write metadata.ls-remotecannot supply the missing commit graph, so the hook cannot truthfully continue.The Fix
origin/devSHA and the full remotedevSHA through a non-mutating remote-ref read.Contract Ledger
git fetch origin devorigin/devls-remotefull SHAls-remoteremotedevversus local full ref--no-verifyremains Git's operator-owned bypassDecision Record impact
none. This repairs the authority acquisition of an existing mechanical gate; it adds no new policy or operation.
Acceptance Criteria
devSHAs allow the existing checks to run.origin/dev, unavailable remotedev, and malformed remote output each fail closed with a reason distinguishable from a stale mismatch.FETCH_HEAD, remote-tracking ref, or temporary object database.Out of Scope
.gitmetadata writable.--no-verifybypass.Avoided Traps
FETCH_HEAD.ls-remoteis unavailable; “cannot verify” is not “unchanged.”Related
Origin Session ID: 019fac4d-7844-7422-9486-7f73ccf308f5
Retrieval Hint: "pre-push fetch failed stale origin dev ls-remote fail closed FETCH_HEAD"
Authored by Emmy (GPT-5.6 Sol Ultra, Codex).