use crate::agents::AgentDrain;
use crate::reducer::effect::Effect;
use crate::reducer::event::CheckpointTrigger;
use crate::reducer::state::{CommitState, PipelineState, PromptMode};
pub const REQUIRED_FILES: &[&str] = &[".agent/tmp/commit_message.xml"];
pub(super) fn determine_commit_effect(state: &PipelineState) -> Effect {
if state.agent_chain.agents.is_empty() || state.agent_chain.current_drain != AgentDrain::Commit
{
return Effect::InitializeAgentChain {
drain: AgentDrain::Commit,
};
}
match state.commit {
CommitState::NotStarted | CommitState::Generating { .. } => {
let current_attempt = match state.commit {
CommitState::Generating { attempt, .. } => attempt,
_ => 1,
};
if let Some(outcome) = state.commit_validated_outcome.as_ref() {
if outcome.attempt == current_attempt && state.commit_xml_extracted {
return Effect::ApplyCommitMessageOutcome;
}
}
if state.commit_prompt_prepared {
if current_attempt == 1 && !state.commit_required_files_cleaned {
return Effect::CleanupRequiredFiles {
files: REQUIRED_FILES.iter().map(ToString::to_string).collect(),
};
}
if !state.commit_agent_invoked {
return Effect::InvokeCommitAgent;
}
if !state.commit_xml_extracted {
return Effect::ExtractCommitXml;
}
return Effect::ValidateCommitXml;
}
if !state.commit_diff_prepared {
return Effect::CheckCommitDiff;
}
if state.commit_diff_empty {
return Effect::SkipCommit {
reason: "No changes to commit (empty diff)".to_string(),
};
}
if state.commit_diff_content_id_sha256.is_none() {
return Effect::CheckCommitDiff;
}
let current_attempt = match state.commit {
CommitState::Generating { attempt, .. } => attempt,
_ => 1,
};
let consumer_signature_sha256 = state.agent_chain.consumer_signature_sha256();
let diff_content_id_sha256 = state.commit_diff_content_id_sha256.as_deref();
if !state.commit_prompt_prepared {
let commit_inputs_materialized_for_attempt =
state.prompt_inputs.commit.as_ref().is_some_and(|c| {
c.attempt == current_attempt
&& c.diff.consumer_signature_sha256 == consumer_signature_sha256
&& diff_content_id_sha256
.is_some_and(|id| id == c.diff.content_id_sha256)
});
if !commit_inputs_materialized_for_attempt {
return Effect::MaterializeCommitInputs {
attempt: current_attempt,
};
}
let prompt_mode = if state.continuation.same_agent_retry_pending
&& !state.continuation.same_agent_retries_exhausted()
{
PromptMode::SameAgentRetry
} else if state.continuation.xsd_retry_pending
&& !state.continuation.xsd_retries_exhausted()
{
PromptMode::XsdRetry
} else {
PromptMode::Normal
};
return Effect::PrepareCommitPrompt { prompt_mode };
}
Effect::ValidateCommitXml
}
CommitState::Generated { ref message } => {
if state.commit_xml_archived {
Effect::CreateCommit {
message: message.clone(),
files: state.commit_selected_files.clone(),
excluded_files: state.commit_excluded_files.clone(),
}
} else {
Effect::ArchiveCommitXml
}
}
CommitState::Skipped => Effect::SaveCheckpoint {
trigger: CheckpointTrigger::PhaseTransition,
},
CommitState::Committed { .. } => {
let is_selective = !state.commit_selected_files.is_empty();
let retry_pass = state.commit_residual_retry_pass;
if is_selective || retry_pass > 0 {
let pass = if retry_pass > 0 { retry_pass } else { 1 };
Effect::CheckResidualFiles { pass }
} else {
Effect::SaveCheckpoint {
trigger: CheckpointTrigger::PhaseTransition,
}
}
}
}
}