Skip to content

feat(commit): let an agent author stand - #412

Merged
AbysmalBiscuit merged 5 commits into
mainfrom
402-feat-commit-let-an-agent-author-stand
Oct 18, 2026
Merged

AbysmalBiscuit merged 5 commits into
mainfrom
402-feat-commit-let-an-agent-author-stand

Conversation

@AbysmalBiscuit

@AbysmalBiscuit AbysmalBiscuit commented Oct 17, 2026 •

Copy link
Copy Markdown
Owner

Closes #402


Agent summary

A project can require a Co-authored-by trailer on each agent commit. When the git author is already the agent, that trailer repeats the author line, and devkit commit still refuses the commit.

devkit commit treats that author as meeting a coauthors requirement for agents on the unchanged built-in commit_message. The author is the one Git records. A complete GIT_AUTHOR_NAME and GIT_AUTHOR_EMAIL pair wins. One half set is not an author. Otherwise author.name wins before user.name, and author.email wins before user.email. An agent author is a strict GitHub bot address, <digits>+<name>[bot]@users.noreply.github.com, or one of two built-in harness addresses: Claude noreply@anthropic.com and Codex noreply@openai.com. No new config key. A person address, a blank half, a human caller, required = "always", a [templates].commit_message override, and a [templates.custom.commit_message] override still need --coauthor. An amend checks the author Git keeps on the existing commit. proof and done_when stay as they are.

Proof

  1. tests/commit.rs an_agent_author_meets_a_coauthor_requirement_with_no_trailer, an_agent_author_from_user_config_meets_a_coauthor_requirement, a_claude_author_meets_a_coauthor_requirement, a_codex_author_meets_a_coauthor_requirement, and the user-config Claude and Codex twins: an agent caller, coauthors required for agents, no --coauthor. The commit is fix: a with no Co-authored-by trailer. required = "always" still refuses a bot, Claude, and Codex author.
  2. tests/commit.rs an_agent_must_name_its_coauthors_when_the_project_says_so, author_email_set_to_a_person_over_a_bot_user_email_still_needs_a_coauthor, and amending_a_person_commit_with_a_bot_environment_still_needs_a_coauthor: an agent caller with a person author. Each refuses with devkit commit needs --coauthor=... (required for agents) and leaves HEAD unchanged.
  3. plugin/skills/using-devkit/references/templates.md and commit.md name the strict bot form, Claude noreply@anthropic.com, Codex noreply@openai.com, the author lookup order, and the template overrides that still need --coauthor. crates/devkit-vcs/src/lib.rs author() documents that same lookup.

🤖 Generated with Grok 4.7 via Grok Build

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-17T21:56:51.507313Z 5fb255e PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5fb255e95e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/devkit-ports/src/templates.rs Outdated
Comment thread crates/devkit-git/src/backend.rs Outdated
Comment thread crates/devkit-ports/src/templates.rs Outdated

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚡ Grok Review

Reviewed commit: 5fb255e95e

✅ No issues found.

The agent-author change matches the stated behavior, and the commit path still refuses a person author, a blank half, a human caller, and a project template.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚡ Grok Review

Reviewed commit: fe98aa7927

❌ Patch is incorrect: 1 finding.

An agent whose git author is a bot still needs --coauthor when only one GIT_AUTHOR variable is set, because the exemption treats that partial pair as no author while git fills the missing half from config and records the bot.

Comment on lines +363 to +384
fn author(&self, dir: &Path) -> Result<Option<(String, String)>> {
// GIT_AUTHOR_* wins, and one half set alone does not mix with
// config. author.* overrides user.*.
let from_env = ["GIT_AUTHOR_NAME", "GIT_AUTHOR_EMAIL"]
.map(|key| std::env::var(key).ok().filter(|v| !v.trim().is_empty()));
let [name, email] = match from_env {
[Some(name), Some(email)] => [name, email],
[None, None] => ["name", "email"].map(|half| {
for key in [format!("author.{half}"), format!("user.{half}")] {
if let Ok(value) = Git::at(dir).args(["config", &key]).output() {
let value = value.trim().to_string();
if !value.is_empty() {
return value;
}
}
}
String::new()
}),
_ => return Ok(None),
};
Ok((!name.trim().is_empty() && !email.trim().is_empty()).then_some((name, email)))
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Treat a partial GIT_AUTHOR pair as git does

When an agent caller has coauthors required for agents and no --coauthor, devkit commit exempts the commit only if VersionControl::author returns a GitHub bot, Claude, or Codex address. GitBackend::author returns Ok(None) whenever exactly one of GIT_AUTHOR_NAME and GIT_AUTHOR_EMAIL is set, even when the set half is a bot email and the missing half is filled from author.* or user.*. Git itself does not do that: a set GIT_AUTHOR_EMAIL overrides the email, and the unset name comes from GIT_AUTHOR_NAME, then author.name, then user.name (and the reverse for a name-only pair). A harness that exports only GIT_AUTHOR_EMAIL=261865991+grok-by-xai[bot]@users.noreply.github.com, or only GIT_AUTHOR_NAME, while the other half lives in git config, therefore gets "devkit commit needs --coauthor=... (required for agents)" and HEAD stays put, even though the commit git would record is already that agent. The same lookup is what the skill text calls the address the commit would record. Resolve each half the way git does, and return Ok(None) only when that half is still unset or blank.

Suggested change
fn author(&self, dir: &Path) -> Result<Option<(String, String)>> {
// GIT_AUTHOR_* wins, and one half set alone does not mix with
// config. author.* overrides user.*.
let from_env = ["GIT_AUTHOR_NAME", "GIT_AUTHOR_EMAIL"]
.map(|key| std::env::var(key).ok().filter(|v| !v.trim().is_empty()));
let [name, email] = match from_env {
[Some(name), Some(email)] => [name, email],
[None, None] => ["name", "email"].map(|half| {
for key in [format!("author.{half}"), format!("user.{half}")] {
if let Ok(value) = Git::at(dir).args(["config", &key]).output() {
let value = value.trim().to_string();
if !value.is_empty() {
return value;
}
}
}
String::new()
}),
_ => return Ok(None),
};
Ok((!name.trim().is_empty() && !email.trim().is_empty()).then_some((name, email)))
}
fn author(&self, dir: &Path) -> Result<Option<(String, String)>> {
// Each half is independent, as git records it: a set GIT_AUTHOR_*
// half wins, and an unset half falls through to author.* then user.*.
// A blank half is not an author.
let halves = ["NAME", "EMAIL"].map(|half| {
let from_env = std::env::var(format!("GIT_AUTHOR_{half}"))
.ok()
.filter(|v| !v.trim().is_empty());
if let Some(value) = from_env {
return value;
}
let key = half.to_ascii_lowercase();
for config_key in [format!("author.{key}"), format!("user.{key}")] {
if let Ok(value) = Git::at(dir).args(["config", &config_key]).output() {
let value = value.trim().to_string();
if !value.is_empty() {
return value;
}
}
}
String::new()
});
let [name, email] = halves;
Ok((!name.trim().is_empty() && !email.trim().is_empty()).then_some((name, email)))
}

AbysmalBiscuit and others added 5 commits October 18, 2026 09:05
Co-authored-by: Grok 4.7 <261865991+grok-by-xai[bot]@users.noreply.github.com>
An amend keeps the existing author, and author.email overrides
user.email. The co-author exemption follows that author, and only the
unchanged built-in commit_message. A custom template still needs
--coauthor. The bot form is a numeric id, a name, and a terminal [bot].
Claude and Codex noreply addresses stand too. The supplied body is left
as written.

Co-authored-by: Grok 4.7 <261865991+grok-by-xai[bot]@users.noreply.github.com>
The agent-author exemption treated every required coauthors marking as met. required = "always" still needs --coauthor, including a GitHub bot, Claude or Codex author. The exemption now applies only when the declared requirement is agents.

Co-authored-by: Grok 4.7 <261865991+grok-by-xai[bot]@users.noreply.github.com>
author() omitted that author.name and author.email win before user.name and user.email, and that a partial GIT_AUTHOR_NAME/GIT_AUTHOR_EMAIL pair is not an author.

Co-authored-by: Grok 4.7 <261865991+grok-by-xai[bot]@users.noreply.github.com>
Passing --cleanup=verbatim changed how every devkit commit stores its message: trailing spaces and runs of blank lines were kept instead of tidied as git commit -m does. Only an agent-author spacing test relied on it, and the issue asks for no such guarantee, so devkit keeps git's default cleanup.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@AbysmalBiscuit
AbysmalBiscuit force-pushed the 402-feat-commit-let-an-agent-author-stand branch from fe98aa7 to 0cc071d Compare October 18, 2026 07:08
@AbysmalBiscuit
AbysmalBiscuit merged commit 47f6b61 into main Oct 18, 2026
18 of 19 checks passed
@AbysmalBiscuit
AbysmalBiscuit deleted the 402-feat-commit-let-an-agent-author-stand branch October 18, 2026 07:16
AbysmalBiscuit added a commit that referenced this pull request Oct 18, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.14.25](v0.14.24...v0.14.25)
(2026-10-18)


### Features

* **commit:** let an agent author stand
([#412](#412))
([47f6b61](47f6b61))
* **pr:** refuse a summary repeating template headings
([#414](#414))
([467affb](467affb))


### Bug Fixes

* **pr:** close agent bypasses of the PR gates
([#417](#417))
([0f2adf0](0f2adf0))
* report a missing home instead of panicking
([#416](#416))
([916ab68](916ab68))
* **ui:** read terminal width through console
([#413](#413))
([ee1e9f6](ee1e9f6))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(commit): let an agent author stand in for its co-author

1 participant