From d089d1a9ccf68ce4571d3476b379d02c23dfaadd Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Tue, 7 Apr 2026 14:51:12 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20b5-retry-backoff=20=E2=80=94=20batch=20?= =?UTF-8?q?5=20upstream=20parity?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- rust/crates/runtime/src/conversation.rs | 1 + rust/crates/runtime/src/prompt.rs | 109 ++++++++++++++++++++++++ 2 files changed, 110 insertions(+) diff --git a/rust/crates/runtime/src/conversation.rs b/rust/crates/runtime/src/conversation.rs index 1010bd3..c3e3c03 100644 --- a/rust/crates/runtime/src/conversation.rs +++ b/rust/crates/runtime/src/conversation.rs @@ -890,6 +890,7 @@ mod tests { current_date: "2026-03-31".to_string(), git_status: None, git_diff: None, + git_recent_commits: None, instruction_files: Vec::new(), }) .with_os("linux", "6.8") diff --git a/rust/crates/runtime/src/prompt.rs b/rust/crates/runtime/src/prompt.rs index 307a64b..85b7e38 100644 --- a/rust/crates/runtime/src/prompt.rs +++ b/rust/crates/runtime/src/prompt.rs @@ -56,6 +56,7 @@ pub struct ProjectContext { pub current_date: String, pub git_status: Option, pub git_diff: Option, + pub git_recent_commits: Option, pub instruction_files: Vec, } @@ -71,6 +72,7 @@ impl ProjectContext { current_date: current_date.into(), git_status: None, git_diff: None, + git_recent_commits: None, instruction_files, }) } @@ -82,6 +84,7 @@ impl ProjectContext { let mut context = Self::discover(cwd, current_date)?; context.git_status = read_git_status(&context.cwd); context.git_diff = read_git_diff(&context.cwd); + context.git_recent_commits = read_git_recent_commits(&context.cwd); Ok(context) } } @@ -249,6 +252,31 @@ fn read_git_status(cwd: &Path) -> Option { } } +fn read_git_recent_commits(cwd: &Path) -> Option { + let output = Command::new("git") + .args([ + "--no-optional-locks", + "log", + "--oneline", + "--no-decorate", + "-n", + "5", + ]) + .current_dir(cwd) + .output() + .ok()?; + if !output.status.success() { + return None; + } + let stdout = String::from_utf8(output.stdout).ok()?; + let trimmed = stdout.trim(); + if trimmed.is_empty() { + None + } else { + Some(trimmed.to_string()) + } +} + fn read_git_diff(cwd: &Path) -> Option { let mut sections = Vec::new(); @@ -299,6 +327,11 @@ fn render_project_context(project_context: &ProjectContext) -> String { lines.push("Git status snapshot:".to_string()); lines.push(status.clone()); } + if let Some(commits) = &project_context.git_recent_commits { + lines.push(String::new()); + lines.push("Recent commits (last 5):".to_string()); + lines.push(commits.clone()); + } if let Some(diff) = &project_context.git_diff { lines.push(String::new()); lines.push("Git diff snapshot:".to_string()); @@ -639,6 +672,82 @@ mod tests { fs::remove_dir_all(root).expect("cleanup temp dir"); } + #[test] + fn discover_with_git_includes_recent_commits_and_renders_them() { + // given: a git repo with three commits and a current branch + let _guard = env_lock(); + ensure_valid_cwd(); + let root = temp_dir(); + fs::create_dir_all(&root).expect("root dir"); + std::process::Command::new("git") + .args(["init", "--quiet", "-b", "main"]) + .current_dir(&root) + .status() + .expect("git init should run"); + std::process::Command::new("git") + .args(["config", "user.email", "tests@example.com"]) + .current_dir(&root) + .status() + .expect("git config email should run"); + std::process::Command::new("git") + .args(["config", "user.name", "Runtime Prompt Tests"]) + .current_dir(&root) + .status() + .expect("git config name should run"); + for (file, message) in [ + ("a.txt", "first commit"), + ("b.txt", "second commit"), + ("c.txt", "third commit"), + ] { + fs::write(root.join(file), "x\n").expect("write commit file"); + std::process::Command::new("git") + .args(["add", file]) + .current_dir(&root) + .status() + .expect("git add should run"); + std::process::Command::new("git") + .args(["commit", "-m", message, "--quiet"]) + .current_dir(&root) + .status() + .expect("git commit should run"); + } + fs::write(root.join("d.txt"), "staged\n").expect("write staged file"); + std::process::Command::new("git") + .args(["add", "d.txt"]) + .current_dir(&root) + .status() + .expect("git add staged should run"); + + // when: discovering project context with git auto-include + let context = + ProjectContext::discover_with_git(&root, "2026-03-31").expect("context should load"); + let rendered = SystemPromptBuilder::new() + .with_os("linux", "6.8") + .with_project_context(context.clone()) + .render(); + + // then: branch, recent commits and staged files are present in context + let commits = context + .git_recent_commits + .as_deref() + .expect("recent commits should be present"); + assert!(commits.contains("first commit")); + assert!(commits.contains("second commit")); + assert!(commits.contains("third commit")); + assert_eq!(commits.lines().count(), 3); + + let status = context.git_status.as_deref().expect("status snapshot"); + assert!(status.contains("## main")); + assert!(status.contains("A d.txt")); + + assert!(rendered.contains("Recent commits (last 5):")); + assert!(rendered.contains("first commit")); + assert!(rendered.contains("Git status snapshot:")); + assert!(rendered.contains("## main")); + + fs::remove_dir_all(root).expect("cleanup temp dir"); + } + #[test] fn discover_with_git_includes_diff_snapshot_for_tracked_changes() { let _guard = env_lock();