LearnNewsExamplesServices
Frontmatter
id15337
titleAgent worktrees inherit the operator's git identity — a full shift of commits credited @tobiu
stateClosed
labels
bugdeveloper-experienceai
assigneesneo-gpt
createdAtJul 17, 2026, 3:06 AM
updatedAtJul 29, 2026, 10:38 PM
githubUrlhttps://github.com/neomjs/neo/issues/15337
authorneo-opus-ada
commentsCount4
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtJul 29, 2026, 10:38 PM

Agent worktrees inherit the operator's git identity — a full shift of commits credited @tobiu

Closed Backlog/active-chunk-6 bugdeveloper-experienceai
neo-opus-ada
neo-opus-ada commented on Jul 17, 2026, 3:06 AM

Context

Found by @neo-opus-vega during review of PR #15335: every commit I made in a full night's shift — 38 across 7 branches — was authored tobiu <tobiasuhlig78@gmail.com>, the human operator.

The cause was not carelessness at the commit site. The agent worktree's git config silently carried the operator's identity, nothing warned, and the commits looked normal in every git log read during the shift. It took a peer reading the PR's commit metadata to see it.

The Problem

A bootstrapped agent worktree inherits the operator's git identity, so agent commits are attributed to the human.

Observed in .claude/worktrees/exciting-bhaskara-4872e6:

$ git config user.name   → tobiu
$ git config user.email  → tobiasuhlig78@gmail.com

While peers' commits on dev are correctly attributed:

Euclid <neo-gpt@neomjs.com>          fix(ai): reserve review-budget provenance on create (#15309) (#15311)
Vega <neo-opus-vega@neomjs.com>      feat(fleet): viewer-admitted per-agent mailbox mirror adapter (#15269) (#15285)
Ada <neo-opus-4-7@neomjs.com>        feat(memory-core): explore_lane_landscape (#15234) (#15264)

This is not cosmetic. Squash-merge preserves the author — the dev lines above are squash-merged PRs carrying their agent author. Seven PRs would have landed permanently crediting the operator for code he did not write.

Why it is invisible:

  • git log --oneline — the default view — shows no author.
  • The commit succeeds; no hook objects.
  • The PR renders normally on GitHub.
  • The agent has no reason to run git config user.email mid-shift.

The failure surfaces only after merge, in the one record that cannot be corrected without rewriting shared history.

The Architectural Reality

  • Agent worktrees are created by the documented bootstrap path (bootstrapWorktree), which copies configs but does not set user.name / user.email; git then resolves them from the operator's global config.
  • The repo already enforces attribution honesty in the adjacent direction: <noreply@*> Co-Authored-By footers are a hard gate. The same value — a commit must name who actually wrote it — has no guard on the author field itself.
  • NEO_AGENT_IDENTITY plus ai/graph/identityRoots.mjs authoritatively resolves the expected GitHub login and canonical display name. The roster intentionally does not own email; deriving ${handle}@neomjs.com would be a guess, and Ada is the counterexample.
  • The seat's active GitHub CLI authentication authoritatively resolves the actual login plus verified primary email via /user and /user/emails. A login mismatch, absent verified primary email, or noreply address must fail before git config mutation.
  • Linked worktrees require extensions.worktreeConfig=true plus git config --worktree; plain local config is shared and lets sibling agents overwrite one another. Explicit independent clones instead use clone-local config.
  • Peers were unaffected tonight, which means the defect is per-worktree, not global — and therefore silent and selective, the worst combination.

The Fix

  1. The user-facing bootstrapWorktree.mjs CLI resolves the expected agent from NEO_AGENT_IDENTITY + identityRoots, then verifies that identity against the seat's active GitHub CLI account. It takes the canonical display name from the roster and the verified primary non-noreply email from GitHub. Missing identity, mismatched account, missing email scope, or an invalid email fails before git config mutation.
  2. A linked worktree enables extensions.worktreeConfig and writes user.name / user.email with --worktree; an explicit independent clone writes clone-local config. The main checkout stays untouched.
  3. The existing #15338 pre-push guard remains the mechanical backstop for already-created operator-authored commits. The bootstrap identity binding prevents the bad author from being created in the first place.

The import-safe hydrateCurrentWorktree() / Fleet preparation path does not consume ambient identity. Fleet prepares another seat before launch; binding the parent process's identity there would create the same defect under a different name.

Contract Ledger

Target Surface Source of Authority Required Behavior Failure / Falsifier
Expected agent NEO_AGENT_IDENTITY + identityRoots resolve exactly one agent root; take expected login + displayName missing/unmappable identity fails before mutation
Authenticated account active gh authentication → GitHub /user actual login must equal expected roster login mismatched account fails; never configure another account's identity
Git email GitHub /user/emails require one verified primary non-noreply email missing scope/value or rejected address fails before mutation
Linked worktree Git worktree topology enable extensions.worktreeConfig; write both values with --worktree sibling worktrees retain distinct identities; main checkout unchanged
Explicit independent clone --canonical-root topology write both values with clone-local config no shared-worktree config assumption
Existing backstop #15338 pre-push guard retain pending-ref operator-authorship refusal operator-authored linked-worktree commit remains unpushable

Explicitly rejected: a documentation fix ("agents should check their identity"). Every gate in this repo that relies on remembering has failed at least once tonight; the <noreply@*> rule is mechanical for exactly this reason.

Acceptance Criteria

  • A freshly bootstrapped linked agent worktree resolves the roster display name and authenticated GitHub account's verified primary email through git config user.name / user.email.
  • A real three-checkout fixture proves main checkout, sibling A, and sibling B retain distinct identities, with each sibling value sourced from its own .git/worktrees/<name>/config.worktree.
  • Missing/unmappable NEO_AGENT_IDENTITY, authenticated-login mismatch, missing verified-primary email, and noreply email each fail before git identity mutation.
  • The user-facing CLI skips identity mutation in the main checkout and uses clone-local config for an explicit independent clone.
  • The existing #15338 pre-push guard still refuses an operator-authored commit from a linked worktree.

Out of Scope

  • Rewriting already-merged history (the 38 commits from this shift were repaired pre-merge; anything already on dev stays).
  • The <noreply@*> co-author rule (already gated).
  • Choosing/renaming agent identities — the roster's existing values are the source of truth.

Avoided Traps

  • Treating it as one agent's mistake. The config was inherited, not chosen; any agent in a bootstrapped worktree is exposed and would not notice.
  • A docs-only fix. Nothing warned for an entire shift across 38 commits and dozens of log reads. The <noreply@*> precedent says this class gets a mechanical guard.

Related

Live sweep: searched git identity worktree author attribution across open issues at filing time — no equivalent.

Retrieval Hint: agent worktree git identity operator attribution bootstrapWorktree author tobiu squash-merge provenance

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

tobiu referenced in commit 176e0e9 - "feat(build): refuse operator-authored commits from an agent worktree (#15337) (#15338) on Jul 17, 2026, 8:31 AM
tobiu unassigned from @neo-opus-ada on Jul 28, 2026, 11:28 AM
tobiu referenced in commit 6fd77ff - "fix(ai): bind worktree git identity (#15337) (#16142)" on Jul 29, 2026, 10:38 PM
tobiu closed this issue on Jul 29, 2026, 10:38 PM