Skip to main content

xchecker_engine/fixup/
phase.rs

1//! Fixup phase implementation
2//!
3//! This module implements the Fixup phase that applies changes to specification
4//! artifacts based on review feedback. The Fixup phase parses unified diffs
5//! from review output and applies them in preview or apply mode.
6
7use crate::phase::{NextStep, Phase, PhaseContext, PhaseMetadata, PhaseResult};
8use crate::status::artifact::{Artifact, ArtifactType};
9use anyhow::Result;
10
11use super::FixupMode;
12use super::parse::FixupParser;
13
14/// Implementation of Fixup phase
15///
16/// This phase applies changes to specification artifacts based on review feedback.
17/// It parses unified diff blocks from review output and applies them in
18/// preview or apply mode.
19#[derive(Debug, Clone)]
20pub struct FixupPhase {
21    mode: FixupMode,
22}
23
24impl FixupPhase {
25    /// Create a new Fixup phase instance with the specified mode
26    #[must_use]
27    pub const fn new_with_mode(mode: FixupMode) -> Self {
28        Self { mode }
29    }
30
31    /// Create a new Fixup phase instance in preview mode
32    #[must_use]
33    pub const fn new() -> Self {
34        Self {
35            mode: FixupMode::Preview,
36        }
37    }
38}
39
40impl Phase for FixupPhase {
41    fn id(&self) -> crate::types::PhaseId {
42        xchecker_utils::types::PhaseId::Fixup
43    }
44
45    fn deps(&self) -> &'static [xchecker_utils::types::PhaseId] {
46        // Fixup phase depends on Review phase
47        &[xchecker_utils::types::PhaseId::Review]
48    }
49
50    fn can_resume(&self) -> bool {
51        true
52    }
53
54    fn prompt(&self, _ctx: &PhaseContext) -> String {
55        // Fixup phase doesn't use LLM - it applies pre-parsed diffs
56        // The prompt is not used for fixup phase
57        String::new()
58    }
59
60    fn make_packet(&self, _ctx: &PhaseContext) -> Result<crate::packet::Packet> {
61        // Fixup phase doesn't build a packet - it applies pre-parsed diffs
62        // Return an empty packet for now
63        let empty_content = String::new();
64        let blake3_hash = blake3::hash(empty_content.as_bytes()).to_hex().to_string();
65
66        let evidence = crate::types::PacketEvidence {
67            files: vec![],
68            max_bytes: 65536,
69            max_lines: 1200,
70        };
71
72        let budget_used = crate::packet::BudgetUsage::new(65536, 1200);
73
74        Ok(crate::packet::Packet::new(
75            empty_content,
76            blake3_hash,
77            evidence,
78            budget_used,
79        ))
80    }
81
82    fn postprocess(&self, raw: &str, ctx: &PhaseContext) -> Result<PhaseResult> {
83        // Parse fixup diffs from review output
84        let parser = FixupParser::new(self.mode, ctx.spec_dir.clone())?;
85
86        match parser.parse_diffs(raw) {
87            Ok(diffs) => {
88                // Create fixup.md artifact with parsed diffs
89                let fixup_content = format!(
90                    "# Fixup Report\n\nMode: {:?}\n\nParsed {} diff(s) from review output.\n",
91                    self.mode,
92                    diffs.len()
93                );
94
95                let fixup_artifact = Artifact {
96                    name: "40-fixup.md".to_string(),
97                    content: fixup_content.clone(),
98                    artifact_type: ArtifactType::Markdown,
99                    blake3_hash: blake3::hash(fixup_content.as_bytes()).to_hex().to_string(),
100                };
101
102                let artifacts = vec![fixup_artifact];
103
104                // Metadata will be populated by orchestrator
105                let metadata = PhaseMetadata::default();
106
107                Ok(PhaseResult {
108                    artifacts,
109                    next_step: NextStep::Continue, // Proceed to Final phase
110                    metadata,
111                })
112            }
113            Err(e) => Err(e.into()),
114        }
115    }
116}
117
118impl Default for FixupPhase {
119    fn default() -> Self {
120        Self::new()
121    }
122}