use super::*;
#[test]
fn commit_carries_the_coauthor_trailer_in_the_message() {
let dir = repo_with_commit_on_task_branch();
std::fs::write(dir.path().join("c.txt"), "x\n").unwrap();
let t = tool(dir.path());
t.dispatch(
"add",
&serde_json::json!({"paths": ["c.txt"]}),
&GitCaveats::top(),
&newt_core::caveats::Caveats::top(),
)
.unwrap();
t.dispatch(
"commit",
&serde_json::json!({"message": "add c"}),
&GitCaveats::top(),
&newt_core::caveats::Caveats::top(),
)
.unwrap();
let log = git_cmd(dir.path())
.args(["log", "-1", "--pretty=%B"])
.output()
.unwrap();
let body = String::from_utf8_lossy(&log.stdout);
assert!(body.contains("add c"), "subject present: {body}");
let version = newt_core::build_info::PACKAGE_VERSION;
let build = newt_core::build_info::SOURCE_ID;
assert!(
body.contains(&format!(
"Co-authored-by: qwen3:30b (newt-agent v{version} {build}) <noreply@newt-agent.com>"
)),
"canonical model trailer present: {body}"
);
assert!(
body.contains("Harness: newt-agent v") && body.contains(" | Model: qwen3:30b | Operator: "),
"canonical provenance line present: {body}"
);
}
#[test]
fn bare_model_subject_still_gets_harness_managed_attribution() {
let dir = repo_with_commit_on_task_branch();
std::fs::write(dir.path().join("p.txt"), "x\n").unwrap();
let t = tool(dir.path());
t.dispatch(
"add",
&serde_json::json!({"paths": ["p.txt"]}),
&GitCaveats::top(),
&newt_core::caveats::Caveats::top(),
)
.unwrap();
t.dispatch(
"commit",
&serde_json::json!({"message": "fix the parser"}),
&GitCaveats::top(),
&newt_core::caveats::Caveats::top(),
)
.unwrap();
let body = head_message(dir.path());
assert!(body.contains("fix the parser"), "subject preserved: {body}");
let version = newt_core::build_info::PACKAGE_VERSION;
let build = newt_core::build_info::SOURCE_ID;
assert!(
body.contains(&format!(
"Co-authored-by: qwen3:30b (newt-agent v{version} {build}) <noreply@newt-agent.com>"
)),
"canonical model trailer added by the harness: {body}"
);
assert!(
body.contains(" | Model: qwen3:30b | Operator: "),
"canonical provenance line added by the harness: {body}"
);
}
#[test]
fn model_switch_between_commits_attributes_each_to_the_live_model() {
let dir = repo_with_commit_on_task_branch();
let p = dir.path();
let mut t = tool(p);
t.attribution = Some(newt_core::attribution::CommitAttribution::from_runtime(
"model-a",
None,
"noreply@newt-agent.com",
));
std::fs::write(p.join("c1.txt"), "x\n").unwrap();
t.dispatch(
"add",
&serde_json::json!({"paths": ["c1.txt"]}),
&GitCaveats::top(),
&newt_core::caveats::Caveats::top(),
)
.unwrap();
t.dispatch(
"commit",
&serde_json::json!({"message": "c1 under model A"}),
&GitCaveats::top(),
&newt_core::caveats::Caveats::top(),
)
.unwrap();
let body_c1 = head_message(p);
assert!(
body_c1.contains(" | Model: model-a | "),
"C1 → model A: {body_c1}"
);
assert!(!body_c1.contains("model-b"), "C1 has no model B: {body_c1}");
t.attribution = Some(newt_core::attribution::CommitAttribution::from_runtime(
"model-b",
None,
"noreply@newt-agent.com",
));
std::fs::write(p.join("c2.txt"), "x\n").unwrap();
t.dispatch(
"add",
&serde_json::json!({"paths": ["c2.txt"]}),
&GitCaveats::top(),
&newt_core::caveats::Caveats::top(),
)
.unwrap();
t.dispatch(
"commit",
&serde_json::json!({"message": "c2 under model B"}),
&GitCaveats::top(),
&newt_core::caveats::Caveats::top(),
)
.unwrap();
let body_c2 = head_message(p);
assert!(
body_c2.contains(" | Model: model-b | "),
"C2 → the LIVE model B at commit time: {body_c2}"
);
assert!(
!body_c2.contains("model-a"),
"model A does NOT survive as stale Newt attribution on C2 (#551): {body_c2}"
);
let c1_again = git_cmd(p)
.args(["log", "--pretty=%B", "--skip=1", "-1"])
.output()
.unwrap();
let body_c1_still = String::from_utf8_lossy(&c1_again.stdout).to_string();
assert!(
body_c1_still.contains(" | Model: model-a | "),
"C1 unchanged after the switch (no backward leakage): {body_c1_still}"
);
assert!(
!body_c1_still.contains("model-b"),
"C1 not corrupted with model B: {body_c1_still}"
);
t.attribution = Some(newt_core::attribution::CommitAttribution::from_runtime(
"model-a",
None,
"noreply@newt-agent.com",
));
std::fs::write(p.join("c3.txt"), "x\n").unwrap();
t.dispatch(
"add",
&serde_json::json!({"paths": ["c3.txt"]}),
&GitCaveats::top(),
&newt_core::caveats::Caveats::top(),
)
.unwrap();
t.dispatch(
"commit",
&serde_json::json!({"message": "c3 back under model A"}),
&GitCaveats::top(),
&newt_core::caveats::Caveats::top(),
)
.unwrap();
let body_c3 = head_message(p);
assert!(
body_c3.contains(" | Model: model-a | "),
"C3 → model A after switching back to a previous model: {body_c3}"
);
}
#[test]
fn amend_after_a_model_switch_resigns_with_the_live_model() {
let dir = repo_with_commit_on_task_branch();
let p = dir.path();
let mut t = tool(p);
t.attribution = Some(newt_core::attribution::CommitAttribution::from_runtime(
"model-a",
None,
"noreply@newt-agent.com",
));
std::fs::write(p.join("c1.txt"), "x\n").unwrap();
t.dispatch(
"add",
&serde_json::json!({"paths": ["c1.txt"]}),
&GitCaveats::top(),
&newt_core::caveats::Caveats::top(),
)
.unwrap();
t.dispatch(
"commit",
&serde_json::json!({"message": "orig under model A"}),
&GitCaveats::top(),
&newt_core::caveats::Caveats::top(),
)
.unwrap();
assert!(head_message(p).contains(" | Model: model-a | "));
t.attribution = Some(newt_core::attribution::CommitAttribution::from_runtime(
"model-b",
None,
"noreply@newt-agent.com",
));
std::fs::write(p.join("c2.txt"), "x\n").unwrap();
t.dispatch(
"add",
&serde_json::json!({"paths": ["c2.txt"]}),
&GitCaveats::top(),
&newt_core::caveats::Caveats::top(),
)
.unwrap();
t.dispatch(
"amend",
&serde_json::json!({"message": "reworded under model B"}),
&GitCaveats::top(),
&newt_core::caveats::Caveats::top(),
)
.unwrap();
let body = head_message(p);
assert!(
body.contains(" | Model: model-b | "),
"amended commit → the live model B: {body}"
);
assert!(
!body.contains("model-a"),
"stale model A did NOT survive the amend (#551): {body}"
);
assert!(
body.contains("reworded under model B"),
"amend subject preserved: {body}"
);
}
#[test]
fn amend_with_no_message_refreshes_attribution_after_a_model_switch() {
let dir = repo_with_commit_on_task_branch();
let p = dir.path();
let mut t = tool(p);
t.attribution = Some(newt_core::attribution::CommitAttribution::from_runtime(
"model-a",
None,
"noreply@newt-agent.com",
));
std::fs::write(p.join("c1.txt"), "x\n").unwrap();
t.dispatch(
"add",
&serde_json::json!({"paths": ["c1.txt"]}),
&GitCaveats::top(),
&newt_core::caveats::Caveats::top(),
)
.unwrap();
t.dispatch(
"commit",
&serde_json::json!({"message": "orig under model A"}),
&GitCaveats::top(),
&newt_core::caveats::Caveats::top(),
)
.unwrap();
let before = head_message(p);
assert!(
before.contains(" | Model: model-a | "),
"C1 → model A: {before}"
);
assert!(before.contains("orig under model A"));
t.attribution = Some(newt_core::attribution::CommitAttribution::from_runtime(
"model-b",
None,
"noreply@newt-agent.com",
));
std::fs::write(p.join("c2.txt"), "x\n").unwrap();
t.dispatch(
"add",
&serde_json::json!({"paths": ["c2.txt"]}),
&GitCaveats::top(),
&newt_core::caveats::Caveats::top(),
)
.unwrap();
t.dispatch(
"amend",
&serde_json::json!({}),
&GitCaveats::top(),
&newt_core::caveats::Caveats::top(),
)
.unwrap();
let body = head_message(p);
assert!(
body.contains(" | Model: model-b | "),
"amend(no message) → the LIVE model B: {body}"
);
assert!(
!body.contains("model-a"),
"stale model A did NOT survive amend(no message): {body}"
);
assert!(
body.contains("orig under model A"),
"amend(no message) preserved the user subject/body: {body}"
);
}
#[test]
fn local_git_tool_status_renders_readable_text() {
let dir = repo_with_commit();
let t = tool(dir.path());
let out = t
.dispatch(
"status",
&serde_json::json!({}),
&GitCaveats::top(),
&newt_core::caveats::Caveats::top(),
)
.unwrap();
assert!(out.contains("on branch main"), "got: {out}");
assert!(out.contains("working tree clean"), "got: {out}");
}