Repository navigation
Change your commit name #5
Description
Activity
- addedenhancementNew feature or requestNew feature or request
on Aug 22, 2026 Plan
Give agent-authored commits their own git identity (config-driven), so they're visibly distinct from the human author's
Context
Issue #5 asks that commits the loop produces carry a different git user name/email than the repo owner's, so a human can tell agent work apart from their own at a glance.
Two gaps today:
git.Manageralready hasAuthorName/AuthorEmailfields (internal/git/workspace.go:29-32) with fallbackscoding-agent-loop/coding-agent-loop@localhost(author()/email(), lines 264-276), applied via-c user.name=…inCommitAll— but nothing ever sets them:cmd/agent.go:171constructs theManagerwithout them, and there is no config key at all. The identity is therefore unconfigurable.- More importantly, the harness is not the only committer.
systemPromptininternal/orchestrator/prompt.go:41explicitly says "You MAY commit your work locally. If you do not, the harness commits it for you." — andCommitAllshort-circuits when the tree is clean (workspace.go:247-249, comment: "agent committed its own work"). When Claude commits, theclaudesubprocess inherits the host environment and the clone's config, which resolve to the host's global gitconfig — i.e. the human repo author. That is exactly the confusion the issue reports, and it's the path that fires most often, since capable models usually do commit their own work.
So the fix is: make the identity configurable, and make it apply to every commit made under a harness worktree, not just the harness's own fallback commit.
Approach
1. New
gitconfig block —internal/config/config.go- Add
type GitConfig struct { AuthorName string \json:"author_name"`; AuthorEmail string `json:"author_email"` }and aGit GitConfig `json:"git"`field onConfig`. - Populate in
Default():author_name: "coding-agent-loop[bot]"author_email: "coding-agent-loop@users.noreply.github.com"
The[bot]suffix is GitHub's convention for machine authors, and a bare@users.noreply.github.comaddress deliberately maps to no GitHub account, so the commit renders with the bot name and no human avatar/hovercard. (This supersedes the current@localhostfallback — see Decisions below.)
- Extend
Validate()with a cheap sanity check: reject a name or email containing<,>, or a newline, since those characters corrupt the commit header line git builds. Empty is legal and means "use the built-in default" (matching howauthor()/email()already behave). - No migration needed:
Loadoverlays JSON ontoDefault(), so a deployedconfig.jsonwithout agitblock simply gets the defaults.DisallowUnknownFieldsonly rejects unknown keys, so adding a field is backward compatible.
2. Apply the identity to the shared clone —
internal/git/workspace.go- Add a small unexported
applyIdentity(ctx, repoPath)that runsgit config user.name …andgit config user.email …(through the existingm.runhelper, reusingm.author()/m.email()), and call it fromEnsureRepoafter the clone/fetch, on every pass — it's idempotent and costs two local git invocations per run. - This is the key mechanism: linked worktrees created by
AddWorktreeshare$GIT_COMMON_DIR/configwith the clone underReposRoot, so a localuser.name/user.emailthere overrides the host's global gitconfig for every commit in every worktree of that repo — Claude's included. These clones are harness-owned and never used by a human, so writing to their config is safe. Side benefit: a service user with no global gitconfig at all can now commit, where today Claude's owngit commitwould fail outright. - Leave the explicit
-c user.name=…flags inCommitAll(workspace.go:253-257) as-is: independent of repo config, and defense in depth. - Add an exported
IdentityEnv() []stringreturningGIT_AUTHOR_NAME,GIT_AUTHOR_EMAIL,GIT_COMMITTER_NAME,GIT_COMMITTER_EMAIL, built from the sameauthor()/email()fallbacks so the resolution logic stays in one place.
3. Pass the identity into the Claude subprocess —
internal/claude/runner.go+internal/orchestrator/loop.go- Add
Env []stringtoclaude.Optionsand, inRun, setcmd.Env = append(os.Environ(), opts.Env...)(currentlycmd.Envis left nil, i.e. inherit; the explicit append preserves that behaviour and is the same patterngit.Manager.runalready uses atworkspace.go:93). - In
loop.go:562, addEnv: o.opts.Git.IdentityEnv()to theclaude.Optionsliteral. It applies to both phases; harmless on the read-only plan run. - Env vars outrank repo config in git's precedence order, so this holds even if Claude's cwd resolution or a stray config ends up somewhere unexpected — belt and braces with step 2, which additionally covers commits made outside the Claude process.
4. Tell the agent not to fight it —
internal/orchestrator/prompt.goAdd one bullet to
systemPrompt's "harness owns version control" list (near line 40): "Do NOT change git'suser.name/user.emailor pass--author/--reset-author; the harness has already set the commit identity for this worktree." Keeps a helpful model from "fixing" the unfamiliar author.5. Wiring and surfaces
cmd/agent.go:171— setAuthorName: cfg.Git.AuthorName, AuthorEmail: cfg.Git.AuthorEmailon thegitpkg.Managerliteral.config.example.json— add the"git": { "author_name": …, "author_email": … }block. (The repo-rootconfig.jsonthatembedded.gocompiles in is gitignored and generated bymake configfrom this template, so the template is the only file to touch.)README.md— add the block to the config JSON sample (~line 240), two rows to the field table (~line 300), and one sentence in the flow description at step 6 noting that both harness-made and agent-made commits carry this identity while the PR itself is still opened by theghtoken's owner.
Tests
internal/config/config_test.go— defaults are populated; agitblock in JSON overlays correctly; a name/email containing<is rejected byValidate(mirroring the existingTestEmptyLabelIsRejectedstyle).internal/git/workspace_test.go— extendTestWorktreeLifecycle(which already setsAuthorName: "agent") to assertgit log -1 --format=%an <%ae>afterCommitAll. Add a new test for the important case: witht.Setenv("GIT_CONFIG_GLOBAL", <temp file naming "Human Author">), runEnsureRepo+AddWorktree, write a file, then commit without any-cflags (simulating Claude), and assert the commit is attributed to the agent identity, not the global one. Requires git ≥ 2.32 forGIT_CONFIG_GLOBAL; the file already skips when git is unavailable.internal/claude/runner_test.go— reuse thestubCLIhelper: a stub that echoes$GIT_AUTHOR_NAMEinto the result event (or a side file), assertingOptions.Envreaches the child.internal/orchestrator/prompt_test.go— assert the new system-prompt bullet is present, matching the existing prompt assertions.
Decisions / risks for the reviewer
- Default email change. The current hardcoded fallback is
coding-agent-loop@localhost; I proposecoding-agent-loop@users.noreply.github.com(and namecoding-agent-loop[bot]) so GitHub renders it sensibly. Any commits already pushed keep their old identity — this only affects new ones. If you'd rather not touch the existing fallback, keepworkspace.go's hardcoded values and set the new defaults only inconfig.Default(); the two would then disagree, which is why I'd rather align them. - Config placement. New top-level
"git"block rather than folding into"workspace", since it mirrorsinternal/git.Manager. Easy to move if you preferworkspace.author_name. - Writing config into the shared clone. This mutates
<repos_root>/<owner>/<name>/.git/config. Those clones are created and owned solely by the daemon, so no human config is at risk — but it is the one place this change writes outside a worktree, and worth a conscious ack. - Out of scope, adjacent: the PR and issue comments are still created by the
ghtoken's account, so GitHub attribution of the PR is unchanged — only commit authorship moves. Likewise, if a host global config setscommit.gpgsign=true, Claude's own commits could fail signing under a service user; that's pre-existing behaviour and I'd leave it alone unless you wantcommit.gpgsign=falseset alongside the identity.
Reply with exactly
implementto approve this plan and start the change. Reply with anything else and the plan will be revised to address it.coding-agent-loop run
38ef73c7-56fb-4e29-8436-075567754a33, modelclaude-opus-5, cost $1.3554- addedagent-plannedManaged by coding-agent-loopManaged by coding-agent-loopand removed
on Aug 22, 2026 implement
The coding agent could not complete this issue (attempt 2).
claude run failed: claude exited with error: exit status 1 (stderr: )It will try again after Sun, 23 Aug 2026 11:11:10 UTC (the wait doubles with each consecutive failure). Remove the
agent-readylabel to stop it retrying.coding-agent-loop run
bf2f4698-998c-4cfb-8742-afd0c07b721925 remaining items
The coding agent could not complete this issue (attempt 8).
claude run failed: claude exited with error: exit status 1 (stderr: )It will try again after Mon, 24 Aug 2026 18:47:11 UTC (the wait doubles with each consecutive failure). Remove the
agent-readylabel to stop it retrying.coding-agent-loop run
7bf2f54f-5f56-4c70-a7fc-1a7a26a864c2The coding agent could not complete this issue (attempt 9).
claude run failed: claude exited with error: exit status 1 (stderr: )It will try again after Tue, 25 Aug 2026 18:48:11 UTC (the wait doubles with each consecutive failure). Remove the
agent-readylabel to stop it retrying.coding-agent-loop run
724680db-201d-44b1-a1fd-19bdece60544Opened a draft pull request for this issue: #9
Tests failed (
make test) — see the PR for output.Comment
implementagain if you want another attempt at this issue.coding-agent-loop run
80c2bb9b-398c-4870-98bc-cfc08a4528c3- added and removedagent-plannedManaged by coding-agent-loopManaged by coding-agent-loop
on Aug 25, 2026 - added a commit that references this issue
on Aug 25, 2026
When you're working on a task, use a different commit user name and email, so that I can differentiate between tasks you're doing and tasks created by the actual human repo author.