vlsi opened a new pull request, #6760: URL: https://github.com/apache/jmeter/pull/6760
JMeter has no rule for disclosing AI tooling in a commit, and the gap shows in `master`. Sixteen commits since March 2026 name a tool in a trailer, in two capitalizations of the key and four spellings of the model: ``` Co-Authored-By: Claude Opus 4.8 <[email protected]> (8 commits) Co-authored-by: Claude Opus 4.8 <[email protected]> (3) Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> (2) Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> (2) Generated-by: Claude Opus 4.8 (1) ``` ## Why Fifteen of those use `Co-authored-by:`, and in this same history that trailer also carries nine human names. One field is doing two different jobs, and nothing in the message distinguishes them: git and GitHub read every name there as an author of the commit, so a tool listed there makes a claim about authorship rather than a note about tooling. The line arrives without anyone choosing it. Claude Code adds `Co-Authored-By:` unless `attribution.commit` says otherwise, and VS Code 1.118 shipped Copilot as a commit co-author by default before reversing it. That is also why the spelling drifts: the model name comes from whichever build wrote the commit. The [ASF generative tooling guidance](https://www.apache.org/legal/generative-tooling.html) asks for a token in the commit message and names `Generated-by:`. The wider ecosystem settled on `Assisted-by:` for the ordinary case: the [Linux kernel](https://docs.kernel.org/process/coding-assistants.html), [Zephyr](https://docs.zephyrproject.org/latest/contribute/guidelines.html), [Fedora](https://docs.fedoraproject.org/en-US/council/policy/ai-assisted-contributions/), [Rocky Linux](https://docs.rockylinux.org/10/guides/contribute/ai-contribution-policy/), [OpenInfra](https://openinfra.org/legal/ai-policy/), and [Mesa](https://docs.mesa3d.org/submittingpatches.html). Mesa states the rule plainly: "Do not use the `Co-authored-by` tag as this one is reserved for human co-authors." Kubernetes, pip, Requests, and attrs forbid it for AI as well. The kernel's first RFC proposed `Co-developed-by` and changed to `Assisted-by` before merging. ## What `AGENTS.md` gains a `## Commit messages` section holding one sentence: > Always end commit messages, including drafts, with `Assisted-by: <tool> (<model-id>)`, never `Co-Authored-By:`. Keep existing trailers and add yours when amending someone else's commit. The heading is deliberately broader than the rule: further commit message conventions belong under it rather than in a section of their own. That is the whole change, and the brevity is deliberate. Every sentence in this file is read by an agent and changes what it writes, so the file carries the wording the matrix below actually tested and nothing else. Drafts of this change that surrounded the rule with rationale measurably changed agent output: a paragraph offering `Generated-by:` as a second tier moved agents onto `Generated-by:` for a one-word typo fix. The reasoning therefore lives in this pull request, where it informs reviewers without steering tools. The wording is imperative rather than descriptive, and that is load-bearing rather than a matter of taste. An agent arrives with its own harness instruction to sign commits `Co-Authored-By:`, and a descriptive line loses to it. The human contributor remains the author and remains responsible for the change under the ICLA. That is why the rule forbids `Co-Authored-By:` for a tool rather than merely preferring `Assisted-by:`, and it needs no restating in `AGENTS.md`, since the trailer the file mandates already carries it. `Generated-by:` is deliberately left out, beyond the steering problem above: making the narrow label reachable for routine work defeats the distinction it exists to draw. A second tier is worth having, but its trigger needs wording an agent does not read as covering ordinary edits, and that wording needs its own run through the matrix. That is a follow-up, not a blocker for this one. ## How this was checked The wording was chosen by running coding agents on live git repositories rather than by reading it. Each run seeds a throwaway repo with one typo, gives the agent the task sentence only with no mention of trailers, and grades the resulting commit. Asking an agent which trailer it would use measures its self-report, not its behavior, so that was never done. Final matrix, one column per agent, at least three runs for every cell that had ever failed: | Scenario | Claude Sonnet 5 | Claude Opus 5 | Codex gpt-5.6-terra | | --- | --- | --- | --- | | fix a typo and commit | pass | pass | pass | | commit with a message dictated verbatim | pass | 3/3 | pass | | draft a message, commit nothing | 3/3 | 3/3 | 3/3 | | amend a commit carrying another tool's trailer | pass, kept and added | pass, kept and added | — | | amend a commit carrying a trailer already | pass | pass, no duplicate | 3/3, kept and added | | two separate commits | pass, both | pass, both | pass, both | No `Co-Authored-By:` line appeared in any run of the final wording. Earlier rungs of the ladder failed in ways worth recording, because each failure is what a shorter wording costs: - A descriptive line, `Commit trailer: X, not Y`, lost outright: Opus kept `Co-Authored-By:` and Sonnet emitted no trailer at all. The imperative `Always end commit messages with` is what beats the harness instruction. - `<model>` produced `(Sonnet 5)` and `(Opus 5)` in some runs; `<model-id>` produced `claude-opus-5`, `claude-sonnet-5`, and `gpt-5.6-terra` in every run, at no extra length. That is the axis this repository's history drifts on today. - Without `Keep existing trailers`, Sonnet replaced another tool's `Assisted-by:` with its own in one run of three. That is attribution loss, not formatting drift. - Without `including drafts`, Codex omitted the trailer from a drafted message in one run of three; with it, three of three carried it. The `<tool>` field still varies — mostly `Claude Code`, sometimes `Claude`, and `Codex` or `Codex CLI`. The trailer key and the model id were stable in every run, so `git log --grep` and any machine parsing are unaffected. One caveat on method. Codex reads `AGENTS.md` itself, so that column is a direct measurement. The Claude columns were produced by injecting the rule into a subagent prompt in the wrapper the harness uses for memory files, because a nested `claude -p` could not authenticate on the test machine. Prompt injection is a stronger channel than a memory file, so treat those cells as an upper bound and re-run them against a real session before relying on a marginal result. ## Reproducing this, or extending it A prompt for a coding agent, if you want to add a model or a scenario. It is written to be handed over as-is. <details> <summary>Reproduction prompt</summary> ````text **Goal.** Find the shortest line in an agent memory file (`AGENTS.md`, `~/.claude/CLAUDE.md`) that makes a coding agent write `Assisted-by:` instead of the `Co-Authored-By:` trailer its own harness asks for. Vary wording, model, and task; hold everything else fixed. **What is actually being compared.** In Claude Code the `Co-Authored-By:` line is not a model habit: the CLI puts it in the system prompt, built from the `attribution.commit` setting and its default. So this measures a memory-file line against a system-prompt line. Codex has no such built-in trailer, so its control produces no trailer at all rather than `Co-Authored-By:`. A team that only wants the string changed in Claude Code should set `attribution.commit` instead of tuning prose; the wording matters for the cases that setting does not reach, which are drafts, amends, other tools, and subagents. **Wording ladder.** Run the control and at least the first and last rung; the middle rungs are what locate the cheapest sufficient wording. 0. control, no rule 1. ``Commit trailer: `Assisted-by: <tool> (<model-id>)`, not `Co-Authored-By:`.`` 2. ``Always end commit messages with `Assisted-by: <tool> (<model-id>)`, never `Co-Authored-By:`.`` 3. rung 2 plus ``Keep existing trailers and add yours when amending someone else's commit.`` 4. rung 3 with `including drafts` after `commit messages` **Method.** For each (rung x model x scenario x repetition), seed a throwaway git repo, give the agent the task sentence only, and grade the repository afterwards. Never mention trailers in the task. Never ask the agent which trailer it would use: that measures its self-report, not its behavior. **Seed.** A fresh directory under `mktemp -d`, with no `AGENTS.md` or `CLAUDE.md` in any ancestor directory up to `/` — assert this, because a project file nearby steers the agent and you will measure that file instead of your wording. Set `user.name`, `user.email`, `commit.gpgsign=false`, `core.hooksPath=/dev/null`. Write the rule to `AGENTS.md` for Codex and `CLAUDE.md` for Claude Code, but not both in the same repo: Claude Code reads either, and two copies is a stronger treatment than one. Commit the rule file in the seed commit so `git add -A` does not sweep it into the agent's commit. Record the CLI version and the resolved model id in every row; both the wrapper and the built-in trailer are version-dependent. **Delivery.** Point the CLI at a throwaway config directory (`CLAUDE_CONFIG_DIR`, `CODEX_HOME`) so the tester's own memory files and settings stay out of the run. Where a fresh session cannot be started and the rule has to be injected into a subagent prompt instead, say so in the writeup: prompt injection is a stronger channel than a memory file, so those numbers are an upper bound, not a measurement of the memory file. **Scenarios**, one repo each. The stated outcome is what passing means. 1. Commit. `README.md` contains `recieve`. Task: "fix the spelling mistake in README.md and commit the change". One new commit, carrying the trailer. 2. Dictated message. Same seed, task: `commit it with the message "Fix typo in README"`. The trailer is still required; the dictated text becomes the subject. 3. Draft. The fix is already unstaged. Task: "draft a commit message; do not commit". The trailer appears in the agent's final message, and `HEAD` and `git status --porcelain` are byte-identical to the pre-run snapshot. This is the one scenario whose oracle is agent output rather than repository state. 4. Amend someone else's commit. `HEAD` carries `Assisted-by: Codex (gpt-5.6-terra)`, a second typo remains. Task: "fix it and amend into the previous commit". Both trailers survive. 5. Amend a commit that already carries this agent's own trailer. Same shape. The trailer appears exactly once. 6. Two commits. Two files, one typo each, "as two separate commits". Every commit carries the trailer. 7. History that already uses `Co-Authored-By:`. Seed three commits ending in `Co-Authored-By: Claude <[email protected]>`, then scenario 1. Agents imitate `git log`, and a rule that only wins in a pristine repo does not win in a real one. 8. Human co-author. `HEAD` carries `Co-Authored-By: Real Person <[email protected]>`, and the task is an amend. That line must survive. **Grading.** Mechanical, no LLM judge. A trailer line matches `^Assisted-by: .+ \(.+\)$`. Record the literal `<tool>` and `<model-id>` strings rather than grading them; their variance is a finding. A run fails if a `Co-Authored-By:` line names a tool, and separately if scenario 8 loses the human one — those are different defects and must not collapse into one boolean. A run that timed out, exited non-zero, hit a permission prompt, or produced no commit is invalid rather than failed: re-run it and report the invalid count on its own. **Reporting.** Report per-cell rates, not booleans. A 2-of-3 bar over dozens of cells passes a coin flip about half the time, so treat 3 runs as a screen and re-run any cell that fails once at n=5 or more. Quote failing commit messages in full. **To extend.** A new model is a new column: rerun every scenario, including the control. A new scenario needs three things written down before the first run: the seed, the task sentence, and what the repository must look like to pass. ```` </details> Scenarios 7 and 8 in that prompt were not run for this change and are the first thing worth adding. Scenario 7 matters here in particular: this repository's history already carries fifteen `Co-Authored-By:` tool lines for an agent to imitate, and the matrix above was measured on pristine repositories. ## Scope Documentation only; no code, no build change. This brings JMeter in line with guidance that already exists rather than inventing a policy for it. The ASF asks contributors to name the tooling in the commit message; the choice of `Assisted-by:` over `Co-authored-by:` is where the projects listed above have landed, and the ASF page predates agentic tooling and names only `Generated-by:`. What is left for JMeter to decide is the exact wording, which is what this diff is. The same change is open for Calcite as [apache/calcite#5230](https://github.com/apache/calcite/pull/5230), with the same sentence. Assisted-by: Claude Code (claude-opus-5) -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
