Put the siGit Code version in the commit trailer

The co-author line now reads "siGit Code v1.5.10 <noreply@sigit.si>" rather than just the name. Which model wrote a commit changes from one session to the next and says little about the tool, while the harness version does. GitHub matches co-authors by address, so the credit still shows up. The trailer is built from CARGO_PKG_VERSION, and the system prompt spells out the same text with concat!. The existing test keeps the two matched. The safety net that amends a missing trailer still accepts any "Co-Authored-By: siGit Code" line, so a commit carrying the old, unversioned trailer doesn't get a second one. Closes #75

Seto Elkahfi committed Sep 27, 2026 at 09:17 UTC 21b18a18cb0d360fb46c957b1165b1c473db78c3
3 files changed +77 -6
CHANGELOG.md
+9
@@ -14,6 +14,15 @@
14 from the first user message — and a request may filter on `cwd` so one
15 project is never offered another's threads. Threads saved before this have no
16 sidecar and are not listed; they still reopen by id through `session/load`
17 +
18 +### Changed
19 +
20 +- **The commit trailer names the siGit Code version.** Commits siGit Code
21 + makes now end with `Co-Authored-By: siGit Code v<version> <noreply@sigit.si>`
22 + instead of the bare name. The model changes from session to session; the
23 + version tells you which build of the agent wrote the commit. GitHub still
24 + credits the co-author, since it matches on the address. A commit that
25 + already carries a siGit Code trailer from an older version is left alone.
26
27
28 ## 1.5.10
src/main.rs
+10 -3
@@ -99,7 +99,8 @@ use tracing_subscriber::{EnvFilter, fmt as tracing_fmt};
99 #[cfg(unix)]
100 use std::os::unix::io::{AsRawFd, FromRawFd};
101
102 -const SYSTEM_PROMPT: &str = "\
102 +const SYSTEM_PROMPT: &str = concat!(
103 + "\
104 Your name is siGit — lowercase 's', uppercase 'G', no spaces. \
105 Not 'SiGit', not 'Sigit'. Only say your name if the user asks who you are.
106
@@ -155,7 +156,12 @@ Git operations — always use run_command:
156 - if a clone or init fails, check the error, fix the cause (wrong path, missing \
157 directory, permissions), and retry
158 - when you create a commit, always end the commit message with a blank line and \
158 - then this trailer on its own line: Co-Authored-By: siGit Code <noreply@sigit.si> \
159 + then this trailer on its own line: ",
160 + // Same text as tools::COMMIT_CO_AUTHOR_TRAILER; concat! takes only
161 + // literals, so it is spelled out here and a test keeps the two in step.
162 + "Co-Authored-By: siGit Code v",
163 + env!("CARGO_PKG_VERSION"),
164 + " <noreply@sigit.si> \
165 — GitHub reads that exact format and credits siGit as co-author. If a commit \
166 lands without it, siGit Code amends the trailer in automatically and the tool \
167 output says so; do not amend again yourself.
@@ -228,7 +234,8 @@ force smbCloud-specific advice into the answer. When it is about smbCloud, be \
234 specific and practical.
235
236 Be direct and brief. Write clean, idiomatic code. When debugging, go for the \
231 -root cause, not the symptom. Correct beats clever.";
237 +root cause, not the symptom. Correct beats clever."
238 +);
239
240 /// shorter prompt for models without tool calling (e.g. DeepSeek Coder v1).
241 /// the full [`SYSTEM_PROMPT`] wastes context and confuses them.
src/tools.rs
+58 -3
@@ -2009,9 +2009,16 @@ fn kill_shell_tree(child: &mut std::process::Child) {
2009 /// alongside the human author; `noreply@sigit.si`
2010 /// is the noreply address for the <https://github.com/sigitc> account
2011 /// ("siGit Code"), so the co-author is rendered with that account's avatar
2012 -/// and profile link. The system prompt asks the model to add this itself;
2013 -/// [`ensure_commit_co_author`] is the safety net when it forgets.
2014 -pub const COMMIT_CO_AUTHOR_TRAILER: &str = "Co-Authored-By: siGit Code <noreply@sigit.si>";
2012 +/// and profile link. GitHub matches on the address, so the name is free to
2013 +/// carry the harness version: the model behind a commit changes from one
2014 +/// session to the next, but the version says which siGit Code made it. The
2015 +/// system prompt asks the model to add this itself; [`ensure_commit_co_author`]
2016 +/// is the safety net when it forgets.
2017 +pub const COMMIT_CO_AUTHOR_TRAILER: &str = concat!(
2018 + "Co-Authored-By: siGit Code v",
2019 + env!("CARGO_PKG_VERSION"),
2020 + " <noreply@sigit.si>"
2021 +);
2022
2023 /// Run `git <args>` in `cwd`, returning trimmed stdout on success.
2024 ///
@@ -3360,6 +3367,54 @@ mod tests {
3367 let _ = fs::remove_dir_all(&remote);
3368 }
3369
3370 + #[test]
3371 + fn co_author_trailer_names_the_harness_version() {
3372 + assert_eq!(
3373 + COMMIT_CO_AUTHOR_TRAILER,
3374 + format!(
3375 + "Co-Authored-By: siGit Code v{} <noreply@sigit.si>",
3376 + env!("CARGO_PKG_VERSION")
3377 + )
3378 + );
3379 + }
3380 +
3381 + #[test]
3382 + fn run_command_keeps_a_trailer_from_an_older_version() {
3383 + let dir = init_test_repo("coauthor_older");
3384 + fs::write(dir.join("file.txt"), "two\n").unwrap();
3385 + // Same cmd /C constraint as above: the trailer goes in with direct git
3386 + // args. Commits made before the version was added, or by another
3387 + // release, already credit siGit Code and must not get a second line.
3388 + test_git(&dir, &["add", "file.txt"]);
3389 + test_git(
3390 + &dir,
3391 + &[
3392 + "commit",
3393 + "-q",
3394 + "-m",
3395 + "Update file\n\nCo-Authored-By: siGit Code <noreply@sigit.si>",
3396 + ],
3397 + );
3398 + let args = serde_json::json!({
3399 + "command": "git commit --amend --no-edit",
3400 + "cwd": dir.display().to_string(),
3401 + })
3402 + .to_string();
3403 +
3404 + let result = exec_run_command(&args, None);
3405 + assert!(
3406 + !result.contains("[siGit Code]"),
3407 + "no amend expected: {result}"
3408 + );
3409 + let message = git_stdout(&dir, &["log", "-1", "--format=%B"]).unwrap();
3410 + assert_eq!(
3411 + message.matches("Co-Authored-By: siGit Code").count(),
3412 + 1,
3413 + "trailer must not be duplicated: {message:?}"
3414 + );
3415 + let _ = fs::remove_dir_all(&dir);
3416 + }
3417 +
3418 #[test]
3419 fn test_run_command_failure() {
3420 #[cfg(unix)]