Context
Found 2026-07-29 while reviewing PR #16142 (fix(ai): bind worktree git identity (#15337)), then confirmed against live board state after that PR merged.
The operator observed that 2 of the last 214 commits were attributed to him and asked what they were. Investigation showed dev is clean — all 214 commits carry correct agent authorship, and GitHub's squash-merge preserves the PR author, so branch-level misattribution does not reach dev. Verified: 973863610c (the merged #16137) is authored Emmy <neo-gpt-emmy@neomjs.com> even though its branch head bacef9d1ff was authored tobiu <tobiasuhlig78@gmail.com>.
The misattributed commits live on branch heads, and one is live: a989e491ba, authored tobiu <tobiasuhlig78@gmail.com> at 2026-07-29 19:42:42 +0200, is the current head of the open PR #16138 (confirmed via GET /repos/neomjs/neo/pulls/16138/commits, not only local refs). That commit was created after the #15338 pre-push guard shipped and reached a PR head anyway.
The Problem
#15337's fix covers two checkout topologies. Its mechanical backstop covers one.
buildScripts/util/check-commit-authorship.mjs (184 lines, wired into .husky/pre-push) scopes itself explicitly in its own doc comment at :23:
"In a LINKED worktree (git-dir !== git-common-dir — i.e. .git/worktrees/<name>, which is how every agent worktree is created), no pushed commit may carry the identity from the operator's GLOBAL git config."
and implements exactly that at :96:
return path.resolve(gitDir) !== path.resolve(commonDir)
An explicit independent clone has gitDir === commonDir, so that predicate returns false and the guard's rule does not apply to it.
Meanwhile #15337's own Contract Ledger carries a separate row for that topology — "Explicit independent clone | --canonical-root topology | write both values with clone-local config" — and PR #16142 implemented it: the bootstrap binder writes clone-local config for an independent clone and worktree-scoped config for a linked worktree.
So the asymmetry is:
|
linked worktree |
explicit independent clone |
| bootstrap identity binding (#16142) |
✅ |
✅ |
| pre-push backstop (#15338) |
✅ |
❌ not covered |
A seat on an independent clone now gets the prevention and no backstop. #15337 rejected exactly this shape of reliance: "Every gate in this repo that relies on remembering has failed at least once tonight; the <noreply@*> rule is mechanical for exactly this reason."
The second half is the same fix. PR #16142 is forward-only by design — it binds identity at bootstrap, so a seat provisioned before it merged keeps the operator's global identity until re-bootstrapped. Its Post-Merge Validation correctly says "on the next fresh resident linked-worktree bootstrap." Nothing covers already-provisioned seats. Widening the guard's topology coverage closes that too: a stale seat stops being silently misattributed and starts being loudly refused at push, which is the only signal that reaches the agent in time.
Observation vs inference, kept separate. Verified: the guard's scope is narrower than the fix's scope, and an operator-authored commit reached an open PR head today after the guard existed. Not verified: why that specific commit was not refused. Three live candidates, none confirmed — the pushing seat is an independent clone (the scope gap above); git push --no-verify was used (a documented bypass at :32); or the seat predates the hook being installed. Determining which is part of this ticket's work, not a premise of it.
The Architectural Reality
buildScripts/util/check-commit-authorship.mjs — isLinkedWorktree() (:79–:96) is the topology predicate; the refusal path and its offender report live at :159. The comparison at :28 is deliberately against the global config rather than a roster: "the leak IS the global identity resolving through an unset local one, so that value is exactly the thing to detect." That reasoning is topology-independent — it holds for a clone just as it does for a worktree.
.husky/pre-push — the invocation site.
ai/scripts/migrations/bootstrapWorktree.mjs — the #16142 binder; :244 already names this guard as "the mechanical backstop," which is the contract this ticket makes true for both topologies.
- The independent-clone topology is a first-class deployment shape, not hypothetical:
--canonical-root / NEO_AI_CANONICAL_ROOT exist precisely to support per-peer clones that mirror the canonical checkout.
The Fix
- Widen the guard's applicability from "linked worktree" to "any checkout that is not the operator's main checkout." The existing predicate answers a topology question when the rule needs an ownership question.
isLinkedWorktree() becomes one branch of that decision rather than the whole of it.
- Establish how an independent clone identifies itself as agent-owned rather than operator-owned.
NEO_AGENT_IDENTITY is the candidate already used by the #16142 binder for exactly this discrimination, which keeps one source of authority across prevention and backstop. Resolve this explicitly rather than by inference — it is the one genuine design decision here.
- Preserve the documented
--no-verify bypass and the global-config comparison unchanged. Neither is the defect.
- If a topology genuinely cannot be covered, say so in the guard's doc comment with the reason, so the next reader does not have to re-derive the gap from a live misattributed commit.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Evidence |
isLinkedWorktree() (check-commit-authorship.mjs:79) |
git topology (--absolute-git-dir vs --git-common-dir) |
remains correct for what it measures; stops being the sole gate on rule applicability |
unchanged for linked worktrees |
existing linked-worktree witnesses stay green |
| guard applicability decision |
agent-ownership, not topology |
refuse operator-global-authored commits from any agent-owned checkout |
topology undecidable → fail closed and say why |
new witness: independent clone with operator identity → push refused |
| agent-ownership discrimination |
NEO_AGENT_IDENTITY (same authority #16142's binder uses) |
one source of authority shared by prevention and backstop |
absent identity → treat as operator checkout, do not refuse |
witness: main checkout unaffected |
| existing-seat remediation |
the widened guard itself |
stale pre-#16142 seat fails loud at push instead of misattributing silently |
none needed — refusal is the remediation path |
witness: seat with global identity and no local binding → refused |
--no-verify bypass (:32) |
documented operator escape |
unchanged |
n/a |
existing behavior preserved |
Decision Record impact
none. This corrects a scope gap in an existing guard; it introduces no new authority and no ADR governs the hook layer. Aligned-with the attribution-honesty value established by #15337 and by §critical_gates rule 4 (the <noreply@*> co-author ban), which exists for the same reason in the adjacent direction.
Acceptance Criteria
Out of Scope
- Rewriting history.
dev is clean (214/214 correctly attributed); branch-level commits are repaired by squash-merge on the way in.
- Repairing
a989e491ba on PR #16138 specifically — that is Emmy's PR and its author's call; squash-merge will land it as hers regardless.
- The bootstrap binder itself (#15337 → PR #16142, merged). This ticket touches only the backstop.
- The
<noreply@*> co-author gate — already mechanical.
- Re-bootstrapping peer seats as an operational task. If the widened guard proves that necessary, it is operator/peer authority and belongs in its own artifact.
Avoided Traps
- Do not treat this as "#16142 was incomplete." It delivered its ledger row for both topologies. The gap is in the older backstop, which predates the independent-clone row existing.
- Do not assert why
a989e491ba escaped. Three candidates fit the symptom; picking one without evidence would be a confabulated cause, and a fix aimed at the wrong one leaves the hole open. The doc comment at :32 documents a legitimate bypass, so "the guard is broken" is not the only reading.
- Do not add a second identity resolver. #16142's binder already resolves agent identity; a parallel resolver in the guard could disagree with it, which is the failure ADR 0019 §10.1 retired the "twin" shape over.
- Do not conflate
dev cleanliness with correctness. 214/214 clean on dev measures what squash-merge repairs, not what the guard prevents. The counter aggregates over both outcomes.
- Do not fix this by documentation. #15337 rejected a docs-only fix for the parent defect on the grounds that nothing warned across 38 commits and dozens of log reads. The same reasoning applies to its backstop.
Related
- Parent: #15337 (operator-identity inheritance) → PR #16142 (bootstrap binder, merged 2026-07-29)
- The backstop this ticket widens: #15338
- The review that surfaced it: PR #16142 review — the coverage boundary I stated there ("AC5 taken from the 19/19 targeted run rather than reading the guard myself") is exactly where this gap sat
- §critical_gates rule 4 — the
<noreply@*> co-author ban, the adjacent already-mechanical attribution rule
- ADR 0019 §10.1 — the retired "twin" shape, cited above as the reason not to add a second identity resolver
Live latest-open sweep: checked the latest 20 open issues plus an all-state search for authorship worktree identity clone guard immediately before filing on 2026-07-29 — only #15337 (CLOSED, the parent) matched; no equivalent open ticket. A2A in-flight sweep: 25 most recent messages, all read-states — recent [lane-claim]s are #16113 and #16128 (both @neo-gpt-emmy, unrelated scope); no competing claim on commit attribution.
Origin Session ID: 0a7f5f1d-cf12-4698-984c-17b64eea5178
Retrieval Hint: check-commit-authorship linked worktree independent clone scope gap operator identity pre-push backstop isLinkedWorktree gitDir commonDir
Context
Found 2026-07-29 while reviewing PR #16142 (
fix(ai): bind worktree git identity (#15337)), then confirmed against live board state after that PR merged.The operator observed that 2 of the last 214 commits were attributed to him and asked what they were. Investigation showed
devis clean — all 214 commits carry correct agent authorship, and GitHub's squash-merge preserves the PR author, so branch-level misattribution does not reachdev. Verified:973863610c(the merged #16137) is authoredEmmy <neo-gpt-emmy@neomjs.com>even though its branch headbacef9d1ffwas authoredtobiu <tobiasuhlig78@gmail.com>.The misattributed commits live on branch heads, and one is live:
a989e491ba, authoredtobiu <tobiasuhlig78@gmail.com>at 2026-07-29 19:42:42 +0200, is the current head of the open PR #16138 (confirmed viaGET /repos/neomjs/neo/pulls/16138/commits, not only local refs). That commit was created after the #15338 pre-push guard shipped and reached a PR head anyway.The Problem
#15337's fix covers two checkout topologies. Its mechanical backstop covers one.
buildScripts/util/check-commit-authorship.mjs(184 lines, wired into.husky/pre-push) scopes itself explicitly in its own doc comment at:23:and implements exactly that at
:96:return path.resolve(gitDir) !== path.resolve(commonDir)An explicit independent clone has
gitDir === commonDir, so that predicate returnsfalseand the guard's rule does not apply to it.Meanwhile #15337's own Contract Ledger carries a separate row for that topology — "Explicit independent clone |
--canonical-roottopology | write both values with clone-local config" — and PR #16142 implemented it: the bootstrap binder writes clone-local config for an independent clone and worktree-scoped config for a linked worktree.So the asymmetry is:
A seat on an independent clone now gets the prevention and no backstop. #15337 rejected exactly this shape of reliance: "Every gate in this repo that relies on remembering has failed at least once tonight; the
<noreply@*>rule is mechanical for exactly this reason."The second half is the same fix. PR #16142 is forward-only by design — it binds identity at bootstrap, so a seat provisioned before it merged keeps the operator's global identity until re-bootstrapped. Its Post-Merge Validation correctly says "on the next fresh resident linked-worktree bootstrap." Nothing covers already-provisioned seats. Widening the guard's topology coverage closes that too: a stale seat stops being silently misattributed and starts being loudly refused at push, which is the only signal that reaches the agent in time.
Observation vs inference, kept separate. Verified: the guard's scope is narrower than the fix's scope, and an operator-authored commit reached an open PR head today after the guard existed. Not verified: why that specific commit was not refused. Three live candidates, none confirmed — the pushing seat is an independent clone (the scope gap above);
git push --no-verifywas used (a documented bypass at:32); or the seat predates the hook being installed. Determining which is part of this ticket's work, not a premise of it.The Architectural Reality
buildScripts/util/check-commit-authorship.mjs—isLinkedWorktree()(:79–:96) is the topology predicate; the refusal path and its offender report live at:159. The comparison at:28is deliberately against the global config rather than a roster: "the leak IS the global identity resolving through an unset local one, so that value is exactly the thing to detect." That reasoning is topology-independent — it holds for a clone just as it does for a worktree..husky/pre-push— the invocation site.ai/scripts/migrations/bootstrapWorktree.mjs— the #16142 binder;:244already names this guard as "the mechanical backstop," which is the contract this ticket makes true for both topologies.--canonical-root/NEO_AI_CANONICAL_ROOTexist precisely to support per-peer clones that mirror the canonical checkout.The Fix
isLinkedWorktree()becomes one branch of that decision rather than the whole of it.NEO_AGENT_IDENTITYis the candidate already used by the #16142 binder for exactly this discrimination, which keeps one source of authority across prevention and backstop. Resolve this explicitly rather than by inference — it is the one genuine design decision here.--no-verifybypass and the global-config comparison unchanged. Neither is the defect.Contract Ledger Matrix
isLinkedWorktree()(check-commit-authorship.mjs:79)--absolute-git-dirvs--git-common-dir)NEO_AGENT_IDENTITY(same authority #16142's binder uses)--no-verifybypass (:32)Decision Record impact
none. This corrects a scope gap in an existing guard; it introduces no new authority and no ADR governs the hook layer. Aligned-with the attribution-honesty value established by #15337 and by §critical_gates rule 4 (the<noreply@*>co-author ban), which exists for the same reason in the adjacent direction.Acceptance Criteria
gitDir === commonDir) carrying the operator's global identity has its push refused, with the same offender report linked worktrees already producebootstrapWorktree.mjs, not a second independent resolver--no-verifybypass and the global-config comparison are preserved unchangeda989e491ba(independent clone /--no-verify/ pre-hook seat) are narrowed to the actual mechanism, and the finding recorded — the fix must not rest on the wrong oneOut of Scope
devis clean (214/214 correctly attributed); branch-level commits are repaired by squash-merge on the way in.a989e491baon PR #16138 specifically — that is Emmy's PR and its author's call; squash-merge will land it as hers regardless.<noreply@*>co-author gate — already mechanical.Avoided Traps
a989e491baescaped. Three candidates fit the symptom; picking one without evidence would be a confabulated cause, and a fix aimed at the wrong one leaves the hole open. The doc comment at:32documents a legitimate bypass, so "the guard is broken" is not the only reading.devcleanliness with correctness. 214/214 clean ondevmeasures what squash-merge repairs, not what the guard prevents. The counter aggregates over both outcomes.Related
<noreply@*>co-author ban, the adjacent already-mechanical attribution ruleLive latest-open sweep: checked the latest 20 open issues plus an all-state search for
authorship worktree identity clone guardimmediately before filing on 2026-07-29 — only #15337 (CLOSED, the parent) matched; no equivalent open ticket. A2A in-flight sweep: 25 most recent messages, all read-states — recent[lane-claim]s are #16113 and #16128 (both @neo-gpt-emmy, unrelated scope); no competing claim on commit attribution.Origin Session ID:
0a7f5f1d-cf12-4698-984c-17b64eea5178Retrieval Hint:
check-commit-authorship linked worktree independent clone scope gap operator identity pre-push backstop isLinkedWorktree gitDir commonDir