[PATCH] scripts/checkpatch: add Assisted-by: tag validation

From: Harry Wentland

Date: Thu Mar 26 2026 - 17:06:32 EST


The coding-assistants.rst documentation defines the Assisted-by: tag
format for AI-assisted contributions as:

Assisted-by: AGENT_NAME:MODEL_VERSION [TOOL1] [TOOL2]

This format does not use an email address, so checkpatch currently
reports a false positive about an invalid email when encountering this
tag.

Add Assisted-by: to the recognized signature tags and standard signature
list. When an Assisted-by: tag is found, validate it instead of checking
for an email address. The validation accepts AGENT_NAME with an optional
:MODEL_VERSION, followed by optional tool names which may themselves
carry a colon-separated version. MODEL_VERSION containing dots triggers
a warning suggesting hyphens per convention.

Cc: Jonathan Corbet <corbet@xxxxxxx>
Cc: Jani Nikula <jani.nikula@xxxxxxxxxxxxxxx>
Assisted-by: Claude:claude-opus-4-6
Signed-off-by: Harry Wentland <harry.wentland@xxxxxxx>
---
scripts/checkpatch.pl | 146 +++++++++++++++++++++++-------------------
1 file changed, 79 insertions(+), 67 deletions(-)

diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index e56374662ff7..3d0688f50b7c 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -641,6 +641,7 @@ our $signature_tags = qr{(?xi:
Reviewed-by:|
Reported-by:|
Suggested-by:|
+ Assisted-by:|
To:|
Cc:
)};
@@ -737,7 +738,7 @@ sub find_standard_signature {
my ($sign_off) = @_;
my @standard_signature_tags = (
'Signed-off-by:', 'Co-developed-by:', 'Acked-by:', 'Tested-by:',
- 'Reviewed-by:', 'Reported-by:', 'Suggested-by:'
+ 'Reviewed-by:', 'Reported-by:', 'Suggested-by:', 'Assisted-by:'
);
foreach my $signature (@standard_signature_tags) {
return $signature if (get_edit_distance($sign_off, $signature) <= 2);
@@ -3105,88 +3106,99 @@ sub process {
}
}

- my ($email_name, $name_comment, $email_address, $comment) = parse_email($email);
- my $suggested_email = format_email(($email_name, $name_comment, $email_address, $comment));
- if ($suggested_email eq "") {
- ERROR("BAD_SIGN_OFF",
- "Unrecognized email address: '$email'\n" . $herecurr);
+# Assisted-by: uses format AGENT_NAME[:MODEL_VERSION] [TOOL1[:VER]] [TOOL2[:VER]] instead of email
+ if ($sign_off =~ /^assisted-by:$/i) {
+ if ($email !~ /^\S+(\s+\S+)*$/) {
+ WARN("BAD_ASSISTED_BY",
+ "Assisted-by: should use format: 'Assisted-by: AGENT_NAME[:MODEL_VERSION] [TOOL1[:VER]] [TOOL2[:VER]]'\n" . $herecurr);
+ } elsif ($email =~ /^[^:\s]+:(\S+)/ && $1 =~ /\./) {
+ WARN("BAD_ASSISTED_BY",
+ "Assisted-by: MODEL_VERSION should use hyphens instead of dots: '$email'\n" . $herecurr);
+ }
} else {
- my $dequoted = $suggested_email;
- $dequoted =~ s/^"//;
- $dequoted =~ s/" </ </;
- # Don't force email to have quotes
- # Allow just an angle bracketed address
- if (!same_email_addresses($email, $suggested_email)) {
- if (WARN("BAD_SIGN_OFF",
- "email address '$email' might be better as '$suggested_email'\n" . $herecurr) &&
- $fix) {
- $fixed[$fixlinenr] =~ s/\Q$email\E/$suggested_email/;
+ my ($email_name, $name_comment, $email_address, $comment) = parse_email($email);
+ my $suggested_email = format_email(($email_name, $name_comment, $email_address, $comment));
+ if ($suggested_email eq "") {
+ ERROR("BAD_SIGN_OFF",
+ "Unrecognized email address: '$email'\n" . $herecurr);
+ } else {
+ my $dequoted = $suggested_email;
+ $dequoted =~ s/^"//;
+ $dequoted =~ s/" </ </;
+ # Don't force email to have quotes
+ # Allow just an angle bracketed address
+ if (!same_email_addresses($email, $suggested_email)) {
+ if (WARN("BAD_SIGN_OFF",
+ "email address '$email' might be better as '$suggested_email'\n" . $herecurr) &&
+ $fix) {
+ $fixed[$fixlinenr] =~ s/\Q$email\E/$suggested_email/;
+ }
}
- }

- # Address part shouldn't have comments
- my $stripped_address = $email_address;
- $stripped_address =~ s/\([^\(\)]*\)//g;
- if ($email_address ne $stripped_address) {
- if (WARN("BAD_SIGN_OFF",
- "address part of email should not have comments: '$email_address'\n" . $herecurr) &&
- $fix) {
- $fixed[$fixlinenr] =~ s/\Q$email_address\E/$stripped_address/;
+ # Address part shouldn't have comments
+ my $stripped_address = $email_address;
+ $stripped_address =~ s/\([^\(\)]*\)//g;
+ if ($email_address ne $stripped_address) {
+ if (WARN("BAD_SIGN_OFF",
+ "address part of email should not have comments: '$email_address'\n" . $herecurr) &&
+ $fix) {
+ $fixed[$fixlinenr] =~ s/\Q$email_address\E/$stripped_address/;
+ }
}
- }

- # Only one name comment should be allowed
- my $comment_count = () = $name_comment =~ /\([^\)]+\)/g;
- if ($comment_count > 1) {
- WARN("BAD_SIGN_OFF",
- "Use a single name comment in email: '$email'\n" . $herecurr);
- }
+ # Only one name comment should be allowed
+ my $comment_count = () = $name_comment =~ /\([^\)]+\)/g;
+ if ($comment_count > 1) {
+ WARN("BAD_SIGN_OFF",
+ "Use a single name comment in email: '$email'\n" . $herecurr);
+ }


- # stable@xxxxxxxxxxxxxxx or stable@xxxxxxxxxx shouldn't
- # have an email name. In addition comments should strictly
- # begin with a #
- if ($email =~ /^.*stable\@(?:vger\.)?kernel\.org/i) {
- if (($comment ne "" && $comment !~ /^#.+/) ||
- ($email_name ne "")) {
- my $cur_name = $email_name;
+ # stable@xxxxxxxxxxxxxxx or stable@xxxxxxxxxx shouldn't
+ # have an email name. In addition comments should strictly
+ # begin with a #
+ if ($email =~ /^.*stable\@(?:vger\.)?kernel\.org/i) {
+ if (($comment ne "" && $comment !~ /^#.+/) ||
+ ($email_name ne "")) {
+ my $cur_name = $email_name;
+ my $new_comment = $comment;
+ $cur_name =~ s/[a-zA-Z\s\-\"]+//g;
+
+ # Remove brackets enclosing comment text
+ # and # from start of comments to get comment text
+ $new_comment =~ s/^\((.*)\)$/$1/;
+ $new_comment =~ s/^\[(.*)\]$/$1/;
+ $new_comment =~ s/^[\s\#]+|\s+$//g;
+
+ $new_comment = trim("$new_comment $cur_name") if ($cur_name ne $new_comment);
+ $new_comment = " # $new_comment" if ($new_comment ne "");
+ my $new_email = "$email_address$new_comment";
+
+ if (WARN("BAD_STABLE_ADDRESS_STYLE",
+ "Invalid email format for stable: '$email', prefer '$new_email'\n" . $herecurr) &&
+ $fix) {
+ $fixed[$fixlinenr] =~ s/\Q$email\E/$new_email/;
+ }
+ }
+ } elsif ($comment ne "" && $comment !~ /^(?:#.+|\(.+\))$/) {
my $new_comment = $comment;
- $cur_name =~ s/[a-zA-Z\s\-\"]+//g;

- # Remove brackets enclosing comment text
- # and # from start of comments to get comment text
- $new_comment =~ s/^\((.*)\)$/$1/;
+ # Extract comment text from within brackets or
+ # c89 style /*...*/ comments
$new_comment =~ s/^\[(.*)\]$/$1/;
- $new_comment =~ s/^[\s\#]+|\s+$//g;
+ $new_comment =~ s/^\/\*(.*)\*\/$/$1/;

- $new_comment = trim("$new_comment $cur_name") if ($cur_name ne $new_comment);
- $new_comment = " # $new_comment" if ($new_comment ne "");
- my $new_email = "$email_address$new_comment";
+ $new_comment = trim($new_comment);
+ $new_comment =~ s/^[^\w]$//; # Single lettered comment with non word character is usually a typo
+ $new_comment = "($new_comment)" if ($new_comment ne "");
+ my $new_email = format_email($email_name, $name_comment, $email_address, $new_comment);

- if (WARN("BAD_STABLE_ADDRESS_STYLE",
- "Invalid email format for stable: '$email', prefer '$new_email'\n" . $herecurr) &&
+ if (WARN("BAD_SIGN_OFF",
+ "Unexpected content after email: '$email', should be: '$new_email'\n" . $herecurr) &&
$fix) {
$fixed[$fixlinenr] =~ s/\Q$email\E/$new_email/;
}
}
- } elsif ($comment ne "" && $comment !~ /^(?:#.+|\(.+\))$/) {
- my $new_comment = $comment;
-
- # Extract comment text from within brackets or
- # c89 style /*...*/ comments
- $new_comment =~ s/^\[(.*)\]$/$1/;
- $new_comment =~ s/^\/\*(.*)\*\/$/$1/;
-
- $new_comment = trim($new_comment);
- $new_comment =~ s/^[^\w]$//; # Single lettered comment with non word character is usually a typo
- $new_comment = "($new_comment)" if ($new_comment ne "");
- my $new_email = format_email($email_name, $name_comment, $email_address, $new_comment);
-
- if (WARN("BAD_SIGN_OFF",
- "Unexpected content after email: '$email', should be: '$new_email'\n" . $herecurr) &&
- $fix) {
- $fixed[$fixlinenr] =~ s/\Q$email\E/$new_email/;
- }
}
}

--
2.53.0