studio sdlc: merge worktree on completion, move state to .g3/

- Add merge step before worktree cleanup when pipeline completes
- On success with commits: merge to main, then cleanup
- On failure: preserve worktree for debugging, print path
- On merge conflict: preserve worktree, print resolution instructions
- Move pipeline.json from analysis/sdlc/ to .g3/sdlc/ (gitignored)
This commit is contained in:
Dhanji R. Prasanna
2026-02-05 13:03:54 +11:00
parent 0e64f13a8a
commit bc2860dd3a
2 changed files with 33 additions and 6 deletions

View File

@@ -664,9 +664,35 @@ fn cmd_sdlc_run(commits_per_run: u32, from_commit: Option<String>) -> Result<()>
sdlc::display_pipeline(&state);
}
// Cleanup worktree
// Handle completion: merge if successful, otherwise preserve for debugging
if state.is_complete() {
// Check if there are commits to merge
let branch_name = sdlc_session.branch_name();
if has_commits_on_branch(&worktree_path, &branch_name)? {
// Merge to main before cleanup
print!("\x1b[1;32msdlc:\x1b[0m merging changes to main ... ");
std::io::Write::flush(&mut std::io::stdout()).ok();
match worktree.merge_to_main(&branch_name) {
Ok(()) => {
println!("[\x1b[1;32mmerged\x1b[0m]");
}
Err(e) => {
println!("[\x1b[1;31mfailed\x1b[0m]");
println!("\x1b[1;31msdlc:\x1b[0m merge failed: {}", e);
println!(" Worktree preserved at: {}", worktree_path.display());
println!(" Resolve conflicts manually, then run 'studio accept {}'", sdlc_session.id);
state.save(&repo_root)?;
return Ok(());
}
}
}
// Cleanup worktree after successful merge (or if no commits)
worktree.remove(&sdlc_session)?;
sdlc_session.delete(&repo_root)?;
}
// If not complete (failures), preserve worktree for debugging/retry
// Generate and display summary
if state.is_complete() {
@@ -682,6 +708,7 @@ fn cmd_sdlc_run(commits_per_run: u32, from_commit: Option<String>) -> Result<()>
println!("\x1b[1;32msdlc:\x1b[0m pipeline complete!");
} else if state.has_failures() {
println!();
println!("\x1b[1;32msdlc:\x1b[0m worktree preserved at: {}", worktree_path.display());
println!("\x1b[1;33msdlc:\x1b[0m pipeline paused due to failures");
println!(" Run 'studio sdlc run' to retry failed stages");
}

View File

@@ -158,12 +158,12 @@ impl PipelineState {
/// Get the path to the pipeline state file
pub fn state_path(repo_root: &Path) -> PathBuf {
repo_root.join("analysis").join("sdlc").join("pipeline.json")
repo_root.join(".g3").join("sdlc").join("pipeline.json")
}
/// Get the path to the SDLC directory
pub fn sdlc_dir(repo_root: &Path) -> PathBuf {
repo_root.join("analysis").join("sdlc")
repo_root.join(".g3").join("sdlc")
}
/// Load pipeline state from disk, or return None if not found