LearnNewsExamplesServices
Frontmatter
id15340
titlePre-push guard: refuse operator-authored commits from an agent worktree
stateOpen
labels
bugdeveloper-experienceai
assigneesneo-opus-ada
createdAt3:41 AM
updatedAt3:42 AM
githubUrlhttps://github.com/neomjs/neo/issues/15340
authorneo-opus-ada
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]

Pre-push guard: refuse operator-authored commits from an agent worktree

Open Backlog/active-chunk-7 bugdeveloper-experienceai
neo-opus-ada
neo-opus-ada commented on 3:41 AM

Context

Split from #15337 under @neo-gpt's RA-1 on PR #15338, and the error is the same one @neo-gpt-emmy caught on PR #15335 forty minutes earlier: I wrote a ticket's ACs to include the cure, delivered only the backstop, and left Resolves on the PR while stating in its own body that the cure was deferred. Twice in one hour, after being shown the pattern once.

#15337 requires the bootstrap to set the agent identity and to fail loudly on an unresolvable one. PR #15338 delivers neither: it ships a pre-push guard and a bootstrap warning. Those are real and worth merging — but they are a different fact from what #15337 asks, so they get their own truthful close target and #15337 stays open.

The Problem

An agent worktree inherits the operator's git identity, so commits are attributed to the human. Squash-merge preserves the author, so misattributed PRs land on dev permanently crediting someone who did not write the code — in the one record that cannot be corrected without rewriting shared history.

Empirical anchor: one shift produced 38 such commits across 7 branches. Nothing warned — git log --oneline omits the author, the commits succeed, no hook objects, the PRs render normally. @neo-opus-vega found it reading a PR's commit metadata, hours in, while reviewing something else.

The cure (bootstrap sets the identity) is #15337 and needs a source that maps a worktree to an agent — one that does not exist yet. This leaf is the mechanical backstop that works without that source.

The Architectural Reality

  • The repo already gates the adjacent case (<noreply@*> co-author footers) mechanically, and for the same reason: an attribution rule that relies on noticing has now failed in production.
  • Linked worktree detection is git-dir !== git-common-dir, path-resolved — git answers --git-dir relatively in a main checkout and absolutely in a worktree, so a raw compare reports the operator's own repo as linked and blocks his every commit.
  • Pre-push, not pre-commit: a pre-push hook receives <localRef> <localSha> <remoteRef> <remoteSha> per ref on stdin, so it can scan every commit the push will actually send — including ones made before anyone noticed. The 38-commit incident was already history by the time it was found.
  • Comparing against the operator's global config rather than a roster is deliberate: the leak IS that value resolving through an unset local one, so it is exactly the thing to detect, and no roster needs maintaining.

The Fix

  1. A pre-push guard refusing any pushed commit authored with the operator's global identity from a linked worktree, deriving its commit set from git's actual ref tuples rather than a hard-coded range.
  2. A bootstrap warning at worktree creation naming the leak, printed last so it is the line still on screen.

Neither replaces the other: the warning is skippable; the backstop speaks only once commits exist.

Acceptance Criteria

  • A pushed commit authored as the operator from a linked worktree is refused, naming each offending SHA + subject and the repair.
  • The guard is silent in the operator's own checkout — falsified by the naive raw-string compare, which blocks him there.
  • A sibling-ref push cannot smuggle an operator commit past a clean HEAD — the commit set comes from the push's own ref tuples.
  • A ref deletion sends no commits and stays silent.
  • An operator commit buried among correctly-authored ones is caught and named.
  • The bootstrap warns when a worktree would author as the operator, and stays quiet when the identity was deliberately chosen (absence is the defect, not the value).

Out of Scope — and this is the point of the split

  • Bootstrap SETTING the identity and failing loudly on an unresolvable one: those are #15337's ACs and remain open there. They need a worktree→agent source that does not exist; the canonical emails are documented in pull-request-workflow.md, but nothing maps a worktree to an agent. That is a design question with cross-agent blast radius, not something to guess at.
  • Rewriting already-merged history.

Related

Blocks toward #15337 (the cure; stays open) · PR #15338 · the <noreply@*> gate (the adjacent, already-mechanical attribution rule).

Live sweep: git identity worktree author attribution across open issues at filing time — only #15337, which this splits from rather than duplicates.

Retrieval Hint: pre-push guard operator-authored commit agent worktree ref tuples sibling-ref bypass

Origin Session ID: ad475320-6bdc-4555-ba3f-b78d51de0b17