summaryrefslogtreecommitdiff
path: root/makima/src/llm/phase_guidance.rs
diff options
context:
space:
mode:
Diffstat (limited to 'makima/src/llm/phase_guidance.rs')
-rw-r--r--makima/src/llm/phase_guidance.rs1032
1 files changed, 0 insertions, 1032 deletions
diff --git a/makima/src/llm/phase_guidance.rs b/makima/src/llm/phase_guidance.rs
deleted file mode 100644
index 712e8bb..0000000
--- a/makima/src/llm/phase_guidance.rs
+++ /dev/null
@@ -1,1032 +0,0 @@
-//! Phase guidance and deliverables tracking for contract management.
-//!
-//! This module provides structured guidance for each contract phase, tracking
-//! expected deliverables and completion criteria.
-//!
-//! ## Contract Types
-//!
-//! ### Simple
-//! - **Plan phase**: One required deliverable: "Plan"
-//! - **Execute phase**: One required deliverable: "PR"
-//!
-//! ### Specification
-//! - **Research phase**: One required deliverable: "Research Notes"
-//! - **Specify phase**: One required deliverable: "Requirements Document"
-//! - **Plan phase**: One required deliverable: "Plan"
-//! - **Execute phase**: One required deliverable: "PR"
-//! - **Review phase**: One required deliverable: "Release Notes"
-//!
-//! ### Execute
-//! - **Execute phase only**: No deliverables at all
-
-use serde::{Deserialize, Serialize};
-use utoipa::ToSchema;
-
-/// Priority level for deliverables
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
-#[serde(rename_all = "lowercase")]
-pub enum DeliverablePriority {
- /// Must be completed before advancing phase
- Required,
- /// Strongly suggested for phase completion
- Recommended,
- /// Nice to have, not blocking
- Optional,
-}
-
-/// A deliverable for a phase
-#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
-pub struct Deliverable {
- /// Unique identifier for the deliverable
- pub id: String,
- /// Display name
- pub name: String,
- /// Priority level
- pub priority: DeliverablePriority,
- /// Brief description of purpose
- pub description: String,
-}
-
-/// Expected deliverables for a phase
-#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
-pub struct PhaseDeliverables {
- /// Phase name
- pub phase: String,
- /// Deliverables for this phase
- pub deliverables: Vec<Deliverable>,
- /// Whether a repository is required for this phase
- pub requires_repository: bool,
- /// Whether tasks should be completed in this phase
- pub requires_tasks: bool,
- /// Guidance text for this phase
- pub guidance: String,
-}
-
-/// Status of a deliverable
-#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
-pub struct DeliverableStatus {
- /// Deliverable ID
- pub id: String,
- /// Display name
- pub name: String,
- /// Priority
- pub priority: DeliverablePriority,
- /// Whether it has been completed
- pub completed: bool,
-}
-
-/// Checklist for phase completion
-#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
-pub struct PhaseChecklist {
- /// Current phase
- pub phase: String,
- /// Deliverable status list
- pub deliverables: Vec<DeliverableStatus>,
- /// Whether repository is configured
- pub has_repository: bool,
- /// Whether repository was required
- pub repository_required: bool,
- /// Task statistics (for execute phase)
- pub task_stats: Option<TaskStats>,
- /// Overall completion percentage (0-100)
- pub completion_percentage: u8,
- /// Summary message
- pub summary: String,
- /// Suggestions for next actions
- pub suggestions: Vec<String>,
-}
-
-/// Task statistics for execute phase
-#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
-pub struct TaskStats {
- pub total: usize,
- pub pending: usize,
- pub running: usize,
- pub done: usize,
- pub failed: usize,
-}
-
-/// Minimal task info for checklist building
-pub struct TaskInfo {
- pub name: String,
- pub status: String,
-}
-
-use crate::db::models::PhaseConfig;
-
-/// Get phase deliverables configuration (legacy, defaults to "simple" contract type)
-pub fn get_phase_deliverables(phase: &str) -> PhaseDeliverables {
- get_phase_deliverables_for_type(phase, "simple")
-}
-
-/// Get phase deliverables configuration for a specific contract type
-pub fn get_phase_deliverables_for_type(phase: &str, contract_type: &str) -> PhaseDeliverables {
- match contract_type {
- "execute" => get_execute_type_deliverables(phase),
- "specification" => get_specification_type_deliverables(phase),
- "simple" | _ => get_simple_type_deliverables(phase),
- }
-}
-
-/// Get phase deliverables from a custom PhaseConfig
-/// This is used for contracts with custom templates
-pub fn get_phase_deliverables_from_config(phase: &str, config: &PhaseConfig) -> PhaseDeliverables {
- // Check if this phase exists in the config
- let phase_exists = config.phases.iter().any(|p| p.id == phase);
- if !phase_exists {
- return PhaseDeliverables {
- phase: phase.to_string(),
- deliverables: vec![],
- requires_repository: false,
- requires_tasks: false,
- guidance: format!("Phase '{}' is not defined in this contract template", phase),
- };
- }
-
- // Get deliverables for this phase from the config
- let deliverables: Vec<Deliverable> = config
- .deliverables
- .get(phase)
- .map(|defs| {
- defs.iter()
- .map(|d| Deliverable {
- id: d.id.clone(),
- name: d.name.clone(),
- priority: match d.priority.as_str() {
- "recommended" => DeliverablePriority::Recommended,
- "optional" => DeliverablePriority::Optional,
- _ => DeliverablePriority::Required,
- },
- description: format!("{} deliverable", d.name),
- })
- .collect()
- })
- .unwrap_or_default();
-
- // Determine if repository is required (typically for execute-like phases)
- let requires_repository = phase == "execute" || phase == "plan";
-
- // Determine if tasks are required (typically for execute phase)
- let requires_tasks = phase == "execute";
-
- // Find the phase name for better guidance
- let phase_name = config
- .phases
- .iter()
- .find(|p| p.id == phase)
- .map(|p| p.name.clone())
- .unwrap_or_else(|| phase.to_string());
-
- let guidance = if deliverables.is_empty() {
- format!("Complete the {} phase. No specific deliverables are required.", phase_name)
- } else {
- let deliverable_names: Vec<_> = deliverables.iter().map(|d| d.name.clone()).collect();
- format!(
- "Complete the {} phase by producing the following deliverables: {}",
- phase_name,
- deliverable_names.join(", ")
- )
- };
-
- PhaseDeliverables {
- phase: phase.to_string(),
- deliverables,
- requires_repository,
- requires_tasks,
- guidance,
- }
-}
-
-/// Get phase deliverables, checking custom config first, then falling back to built-in types
-pub fn get_phase_deliverables_with_config(
- phase: &str,
- contract_type: &str,
- phase_config: Option<&PhaseConfig>,
-) -> PhaseDeliverables {
- // If we have a custom phase config, use it
- if let Some(config) = phase_config {
- return get_phase_deliverables_from_config(phase, config);
- }
-
- // Otherwise, fall back to built-in contract types
- get_phase_deliverables_for_type(phase, contract_type)
-}
-
-/// Get deliverables for 'simple' contract type
-fn get_simple_type_deliverables(phase: &str) -> PhaseDeliverables {
- match phase {
- "plan" => PhaseDeliverables {
- phase: "plan".to_string(),
- deliverables: vec![Deliverable {
- id: "plan-document".to_string(),
- name: "Plan".to_string(),
- priority: DeliverablePriority::Required,
- description: "Implementation plan detailing the approach and tasks".to_string(),
- }],
- requires_repository: true,
- requires_tasks: false,
- guidance: "Create a plan document that outlines the implementation approach. A repository must be configured before moving to Execute phase.".to_string(),
- },
- "execute" => PhaseDeliverables {
- phase: "execute".to_string(),
- deliverables: vec![Deliverable {
- id: "pull-request".to_string(),
- name: "Pull Request".to_string(),
- priority: DeliverablePriority::Required,
- description: "Pull request with the implemented changes".to_string(),
- }],
- requires_repository: true,
- requires_tasks: true,
- guidance: "Execute the plan and create a PR with the implemented changes. Complete all tasks to finish the contract.".to_string(),
- },
- _ => PhaseDeliverables {
- phase: phase.to_string(),
- deliverables: vec![],
- requires_repository: false,
- requires_tasks: false,
- guidance: "Unknown phase for simple contract type".to_string(),
- },
- }
-}
-
-/// Get deliverables for 'specification' contract type
-fn get_specification_type_deliverables(phase: &str) -> PhaseDeliverables {
- match phase {
- "research" => PhaseDeliverables {
- phase: "research".to_string(),
- deliverables: vec![Deliverable {
- id: "research-notes".to_string(),
- name: "Research Notes".to_string(),
- priority: DeliverablePriority::Required,
- description: "Document findings and insights during research".to_string(),
- }],
- requires_repository: false,
- requires_tasks: false,
- guidance: "Focus on understanding the problem space and document your findings in the Research Notes before moving to Specify phase.".to_string(),
- },
- "specify" => PhaseDeliverables {
- phase: "specify".to_string(),
- deliverables: vec![Deliverable {
- id: "requirements-document".to_string(),
- name: "Requirements Document".to_string(),
- priority: DeliverablePriority::Required,
- description: "Define functional and non-functional requirements".to_string(),
- }],
- requires_repository: false,
- requires_tasks: false,
- guidance: "Define what needs to be built with clear requirements in the Requirements Document. Ensure specifications are detailed enough for planning.".to_string(),
- },
- "plan" => PhaseDeliverables {
- phase: "plan".to_string(),
- deliverables: vec![Deliverable {
- id: "plan-document".to_string(),
- name: "Plan".to_string(),
- priority: DeliverablePriority::Required,
- description: "Implementation plan detailing the approach and tasks".to_string(),
- }],
- requires_repository: true,
- requires_tasks: false,
- guidance: "Create a plan document that outlines the implementation approach. A repository must be configured before moving to Execute phase.".to_string(),
- },
- "execute" => PhaseDeliverables {
- phase: "execute".to_string(),
- deliverables: vec![Deliverable {
- id: "pull-request".to_string(),
- name: "Pull Request".to_string(),
- priority: DeliverablePriority::Required,
- description: "Pull request with the implemented changes".to_string(),
- }],
- requires_repository: true,
- requires_tasks: true,
- guidance: "Execute the plan and create a PR with the implemented changes. Complete all tasks before moving to Review phase.".to_string(),
- },
- "review" => PhaseDeliverables {
- phase: "review".to_string(),
- deliverables: vec![Deliverable {
- id: "release-notes".to_string(),
- name: "Release Notes".to_string(),
- priority: DeliverablePriority::Required,
- description: "Document changes for release communication".to_string(),
- }],
- requires_repository: false,
- requires_tasks: false,
- guidance: "Review completed work and document the release in the Release Notes. The contract can be completed after review.".to_string(),
- },
- _ => PhaseDeliverables {
- phase: phase.to_string(),
- deliverables: vec![],
- requires_repository: false,
- requires_tasks: false,
- guidance: "Unknown phase for specification contract type".to_string(),
- },
- }
-}
-
-/// Get deliverables for 'execute' contract type
-fn get_execute_type_deliverables(phase: &str) -> PhaseDeliverables {
- match phase {
- "execute" => PhaseDeliverables {
- phase: "execute".to_string(),
- deliverables: vec![], // No deliverables for execute-only contract type
- requires_repository: true,
- requires_tasks: true,
- guidance: "Execute the tasks directly. No deliverable documents are required for this contract type.".to_string(),
- },
- _ => PhaseDeliverables {
- phase: phase.to_string(),
- deliverables: vec![],
- requires_repository: false,
- requires_tasks: false,
- guidance: "The 'execute' contract type only supports the 'execute' phase.".to_string(),
- },
- }
-}
-
-/// Build a phase checklist comparing expected vs actual deliverables
-pub fn get_phase_checklist_for_type(
- phase: &str,
- completed_deliverables: &[String],
- tasks: &[TaskInfo],
- has_repository: bool,
- contract_type: &str,
-) -> PhaseChecklist {
- let phase_config = get_phase_deliverables_for_type(phase, contract_type);
-
- // Build deliverable status list
- let deliverables: Vec<DeliverableStatus> = phase_config
- .deliverables
- .iter()
- .map(|d| DeliverableStatus {
- id: d.id.clone(),
- name: d.name.clone(),
- priority: d.priority,
- completed: completed_deliverables.contains(&d.id),
- })
- .collect();
-
- // Calculate task stats for execute phase
- let task_stats = if phase == "execute" {
- let total = tasks.len();
- let pending = tasks.iter().filter(|t| t.status == "pending").count();
- let running = tasks.iter().filter(|t| t.status == "running").count();
- let done = tasks.iter().filter(|t| t.status == "done").count();
- let failed = tasks
- .iter()
- .filter(|t| t.status == "failed" || t.status == "error")
- .count();
-
- Some(TaskStats {
- total,
- pending,
- running,
- done,
- failed,
- })
- } else {
- None
- };
-
- // Calculate completion percentage
- let mut completed_items = 0;
- let mut total_items = 0;
-
- // Count required and recommended deliverables (not optional)
- for status in &deliverables {
- if status.priority != DeliverablePriority::Optional {
- total_items += 1;
- if status.completed {
- completed_items += 1;
- }
- }
- }
-
- // Count repository if required
- if phase_config.requires_repository {
- total_items += 1;
- if has_repository {
- completed_items += 1;
- }
- }
-
- // Count tasks if in execute phase
- if let Some(ref stats) = task_stats {
- if stats.total > 0 {
- total_items += 1;
- if stats.done == stats.total && stats.total > 0 {
- completed_items += 1;
- }
- }
- }
-
- let completion_percentage = if total_items > 0 {
- ((completed_items as f64 / total_items as f64) * 100.0) as u8
- } else {
- 100 // No requirements means complete
- };
-
- // Generate suggestions
- let mut suggestions = Vec::new();
-
- // Suggest missing deliverables
- for status in &deliverables {
- if !status.completed {
- match status.priority {
- DeliverablePriority::Required => {
- suggestions.push(format!(
- "Mark '{}' as complete using mark_deliverable_complete (required)",
- status.name
- ));
- }
- DeliverablePriority::Recommended => {
- suggestions.push(format!(
- "Consider completing '{}' (recommended)",
- status.name
- ));
- }
- DeliverablePriority::Optional => {}
- }
- }
- }
-
- // Suggest repository if needed
- if phase_config.requires_repository && !has_repository {
- suggestions.push("Configure a repository for task execution".to_string());
- }
-
- // Suggest task actions for execute phase
- if let Some(ref stats) = task_stats {
- if stats.total == 0 {
- suggestions.push("Create tasks to implement the plan".to_string());
- } else if stats.pending > 0 {
- suggestions.push(format!("Run {} pending task(s)", stats.pending));
- } else if stats.running > 0 {
- suggestions.push(format!(
- "Wait for {} running task(s) to complete",
- stats.running
- ));
- } else if stats.failed > 0 {
- suggestions.push(format!("Address {} failed task(s)", stats.failed));
- } else if stats.done == stats.total && stats.total > 0 {
- suggestions.push("All tasks complete. Mark deliverables and advance phase.".to_string());
- }
- }
-
- // Generate summary
- let summary = generate_phase_summary(
- phase,
- &deliverables,
- has_repository,
- &task_stats,
- completion_percentage,
- );
-
- PhaseChecklist {
- phase: phase.to_string(),
- deliverables,
- has_repository,
- repository_required: phase_config.requires_repository,
- task_stats,
- completion_percentage,
- summary,
- suggestions,
- }
-}
-
-fn generate_phase_summary(
- phase: &str,
- deliverables: &[DeliverableStatus],
- has_repository: bool,
- task_stats: &Option<TaskStats>,
- completion_percentage: u8,
-) -> String {
- let completed_count = deliverables.iter().filter(|d| d.completed).count();
- let total_count = deliverables.len();
-
- match phase {
- "research" => {
- if completed_count == 0 {
- "Research phase needs documentation. Mark deliverables complete when ready."
- .to_string()
- } else {
- format!(
- "{}/{} deliverables complete. Ready to transition to Specify phase.",
- completed_count, total_count
- )
- }
- }
- "specify" => {
- let has_required = deliverables
- .iter()
- .filter(|d| d.priority == DeliverablePriority::Required)
- .all(|d| d.completed);
-
- if !has_required {
- "Specify phase requires completing the Requirements Document deliverable."
- .to_string()
- } else {
- "Specifications ready. Consider transitioning to Plan phase.".to_string()
- }
- }
- "plan" => {
- let has_required = deliverables
- .iter()
- .filter(|d| d.priority == DeliverablePriority::Required)
- .all(|d| d.completed);
-
- if !has_required {
- "Plan phase requires completing the Plan deliverable.".to_string()
- } else if !has_repository {
- "Repository not configured. Configure a repository before Execute phase."
- .to_string()
- } else {
- "Planning complete. Ready to transition to Execute phase.".to_string()
- }
- }
- "execute" => {
- if let Some(stats) = task_stats {
- if stats.total == 0 {
- "No tasks created. Create tasks to implement the plan.".to_string()
- } else if stats.done == stats.total {
- "All tasks complete! Mark deliverables and advance to Review phase (or complete contract).".to_string()
- } else {
- format!(
- "{}/{} tasks completed ({}% done)",
- stats.done,
- stats.total,
- if stats.total > 0 {
- (stats.done * 100) / stats.total
- } else {
- 0
- }
- )
- }
- } else {
- "Execute phase in progress.".to_string()
- }
- }
- "review" => {
- let has_required = deliverables
- .iter()
- .filter(|d| d.priority == DeliverablePriority::Required)
- .all(|d| d.completed);
-
- if !has_required {
- "Review phase requires completing the Release Notes deliverable.".to_string()
- } else {
- "Review documentation complete. Contract can be marked as done.".to_string()
- }
- }
- _ => format!("Phase {} - {}% complete", phase, completion_percentage),
- }
-}
-
-/// Result of checking if deliverables are met for the current phase
-#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
-pub struct DeliverableCheckResult {
- /// Whether all required deliverables are met
- pub deliverables_met: bool,
- /// Whether the phase is ready to advance (includes all readiness checks)
- pub ready_to_advance: bool,
- /// Current phase
- pub phase: String,
- /// Next phase (if available)
- pub next_phase: Option<String>,
- /// List of required deliverables and their status
- pub required_deliverables: Vec<DeliverableItem>,
- /// List of what's missing (if any)
- pub missing: Vec<String>,
- /// Human-readable summary
- pub summary: String,
- /// Whether auto-progress is recommended
- pub auto_progress_recommended: bool,
-}
-
-/// A single deliverable item status
-#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
-pub struct DeliverableItem {
- /// ID of the deliverable
- pub id: String,
- /// Name of the deliverable
- pub name: String,
- /// Type: "deliverable", "repository", "tasks"
- pub deliverable_type: String,
- /// Whether it's met
- pub met: bool,
- /// Additional details
- pub details: Option<String>,
-}
-
-/// Check if all required deliverables for the current phase are met
-pub fn check_deliverables_met(
- phase: &str,
- contract_type: &str,
- completed_deliverables: &[String],
- tasks: &[TaskInfo],
- has_repository: bool,
-) -> DeliverableCheckResult {
- let mut required_items = Vec::new();
- let mut missing = Vec::new();
-
- // Get the deliverables for this contract type and phase
- let phase_config = get_phase_deliverables_for_type(phase, contract_type);
-
- // Check required deliverables for this phase
- for deliverable in &phase_config.deliverables {
- if deliverable.priority == DeliverablePriority::Required {
- let is_complete = completed_deliverables.contains(&deliverable.id);
-
- required_items.push(DeliverableItem {
- id: deliverable.id.clone(),
- name: deliverable.name.clone(),
- deliverable_type: "deliverable".to_string(),
- met: is_complete,
- details: if is_complete {
- Some("Marked complete".to_string())
- } else {
- None
- },
- });
-
- if !is_complete {
- missing.push(format!(
- "Mark '{}' as complete (required)",
- deliverable.name
- ));
- }
- }
- }
-
- // Check repository for phases that require it
- if phase_config.requires_repository {
- required_items.push(DeliverableItem {
- id: "repository".to_string(),
- name: "Repository".to_string(),
- deliverable_type: "repository".to_string(),
- met: has_repository,
- details: if has_repository {
- Some("Repository configured".to_string())
- } else {
- None
- },
- });
-
- if !has_repository {
- missing.push("Configure a repository".to_string());
- }
- }
-
- // Check tasks for execute phase
- if phase_config.requires_tasks {
- let total_tasks = tasks.len();
- let done_tasks = tasks.iter().filter(|t| t.status == "done").count();
- let tasks_complete = total_tasks > 0 && done_tasks == total_tasks;
-
- required_items.push(DeliverableItem {
- id: "tasks".to_string(),
- name: "Tasks Completed".to_string(),
- deliverable_type: "tasks".to_string(),
- met: tasks_complete,
- details: Some(format!("{}/{} tasks done", done_tasks, total_tasks)),
- });
-
- if !tasks_complete {
- if total_tasks == 0 {
- missing.push("Create and complete tasks".to_string());
- } else {
- missing.push(format!(
- "Complete remaining {} task(s)",
- total_tasks - done_tasks
- ));
- }
- }
- }
-
- let deliverables_met = required_items.iter().all(|d| d.met);
- let next_phase = get_next_phase_for_contract(contract_type, phase);
- let ready_to_advance = deliverables_met && next_phase.is_some();
-
- let summary = if deliverables_met {
- if let Some(ref next) = next_phase {
- format!(
- "All deliverables met for {} phase. Ready to advance to {} phase.",
- phase, next
- )
- } else {
- format!(
- "All deliverables met for {} phase. This is the final phase.",
- phase
- )
- }
- } else {
- format!(
- "{} deliverable(s) still needed for {} phase.",
- missing.len(),
- phase
- )
- };
-
- DeliverableCheckResult {
- deliverables_met,
- ready_to_advance,
- phase: phase.to_string(),
- next_phase,
- required_deliverables: required_items,
- missing,
- summary,
- auto_progress_recommended: deliverables_met && ready_to_advance,
- }
-}
-
-/// Get the next phase based on contract type
-pub fn get_next_phase_for_contract(contract_type: &str, current_phase: &str) -> Option<String> {
- match contract_type {
- "simple" => match current_phase {
- "plan" => Some("execute".to_string()),
- "execute" => None, // Terminal phase for simple contracts
- _ => None,
- },
- "execute" => None, // Execute-only contracts don't have phase transitions
- "specification" | _ => match current_phase {
- "research" => Some("specify".to_string()),
- "specify" => Some("plan".to_string()),
- "plan" => Some("execute".to_string()),
- "execute" => Some("review".to_string()),
- "review" => None, // Final phase
- _ => None,
- },
- }
-}
-
-/// Determine if the contract should auto-progress to the next phase
-pub fn should_auto_progress(
- phase: &str,
- contract_type: &str,
- completed_deliverables: &[String],
- tasks: &[TaskInfo],
- has_repository: bool,
- autonomous_loop: bool,
-) -> AutoProgressDecision {
- let check = check_deliverables_met(
- phase,
- contract_type,
- completed_deliverables,
- tasks,
- has_repository,
- );
-
- if !check.deliverables_met {
- return AutoProgressDecision {
- should_progress: false,
- next_phase: None,
- reason: format!("Deliverables not met: {}", check.missing.join(", ")),
- action: AutoProgressAction::WaitForDeliverables,
- };
- }
-
- if check.next_phase.is_none() {
- return AutoProgressDecision {
- should_progress: false,
- next_phase: None,
- reason: "This is the terminal phase. Contract can be completed.".to_string(),
- action: AutoProgressAction::CompleteContract,
- };
- }
-
- if autonomous_loop {
- AutoProgressDecision {
- should_progress: true,
- next_phase: check.next_phase,
- reason: "All deliverables met and autonomous_loop is enabled.".to_string(),
- action: AutoProgressAction::AdvancePhase,
- }
- } else {
- AutoProgressDecision {
- should_progress: false,
- next_phase: check.next_phase,
- reason: "All deliverables met. Suggest advancing to next phase.".to_string(),
- action: AutoProgressAction::SuggestAdvance,
- }
- }
-}
-
-/// Result of auto-progress decision
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct AutoProgressDecision {
- /// Whether to automatically progress
- pub should_progress: bool,
- /// The next phase to progress to
- pub next_phase: Option<String>,
- /// Reason for the decision
- pub reason: String,
- /// Recommended action
- pub action: AutoProgressAction,
-}
-
-/// Actions that can be taken based on auto-progress decision
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub enum AutoProgressAction {
- /// Wait for required deliverables
- WaitForDeliverables,
- /// Automatically advance to next phase
- AdvancePhase,
- /// Suggest user to advance (when not autonomous)
- SuggestAdvance,
- /// Contract is complete, mark as done
- CompleteContract,
-}
-
-/// Generate enhanced prompt guidance for deliverable checking
-pub fn generate_deliverable_prompt_guidance(
- phase: &str,
- contract_type: &str,
- check_result: &DeliverableCheckResult,
-) -> String {
- let mut guidance = String::new();
-
- guidance.push_str("\n## Phase Deliverables Status\n\n");
- guidance.push_str(&format!(
- "**Current Phase**: {} | **Contract Type**: {}\n\n",
- capitalize(phase),
- contract_type
- ));
-
- // Show required deliverables checklist
- guidance.push_str("### Required Deliverables Checklist\n");
- for item in &check_result.required_deliverables {
- let status = if item.met { "[x]" } else { "[ ]" };
- let details = item
- .details
- .as_ref()
- .map(|d| format!(" - {}", d))
- .unwrap_or_default();
- guidance.push_str(&format!(
- "{} **{}** ({}){}\n",
- status, item.name, item.deliverable_type, details
- ));
- }
-
- // Show status and next actions
- guidance.push_str("\n### Status\n");
- if check_result.deliverables_met {
- guidance.push_str("**All deliverables are met.**\n\n");
- if let Some(ref next) = check_result.next_phase {
- guidance.push_str(&format!("Ready to advance to **{}** phase.\n", next));
- if check_result.auto_progress_recommended {
- guidance.push_str(&format!("\n**ACTION REQUIRED**: Since all deliverables are met, you should call `advance_phase` with `new_phase=\"{}\"` to progress the contract.\n", next));
- }
- } else {
- guidance.push_str(
- "This is the terminal phase. The contract can be marked as completed.\n",
- );
- }
- } else {
- guidance.push_str("**Deliverables not yet met.**\n\n");
- guidance.push_str("Missing:\n");
- for item in &check_result.missing {
- guidance.push_str(&format!("- {}\n", item));
- }
- guidance.push_str(
- "\nUse `mark_deliverable_complete` to mark deliverables as complete when ready.\n",
- );
- }
-
- guidance
-}
-
-/// Format checklist as markdown for LLM context
-pub fn format_checklist_markdown(checklist: &PhaseChecklist) -> String {
- let mut md = format!(
- "## Phase Progress ({} Phase)\n\n",
- capitalize(&checklist.phase)
- );
-
- // Deliverables
- md.push_str("### Deliverables\n");
- for status in &checklist.deliverables {
- let check = if status.completed { "+" } else { "-" };
- let priority_label = match status.priority {
- DeliverablePriority::Required => " (required)",
- DeliverablePriority::Recommended => " (recommended)",
- DeliverablePriority::Optional => " (optional)",
- };
-
- if status.completed {
- md.push_str(&format!("[{}] {} - completed\n", check, status.name));
- } else {
- md.push_str(&format!("[{}] {}{}\n", check, status.name, priority_label));
- }
- }
-
- // Repository status
- if checklist.repository_required {
- let check = if checklist.has_repository { "+" } else { "-" };
- md.push_str(&format!("[{}] Repository configured (required)\n", check));
- }
-
- // Task stats for execute phase
- if let Some(ref stats) = checklist.task_stats {
- md.push_str("\n### Task Progress\n");
- md.push_str(&format!("- Total: {}\n", stats.total));
- md.push_str(&format!("- Done: {}\n", stats.done));
- if stats.pending > 0 {
- md.push_str(&format!("- Pending: {}\n", stats.pending));
- }
- if stats.running > 0 {
- md.push_str(&format!("- Running: {}\n", stats.running));
- }
- if stats.failed > 0 {
- md.push_str(&format!("- Failed: {}\n", stats.failed));
- }
- }
-
- // Summary
- md.push_str(&format!(
- "\n**Status**: {} ({}% complete)\n",
- checklist.summary, checklist.completion_percentage
- ));
-
- // Suggestions
- if !checklist.suggestions.is_empty() {
- md.push_str("\n**Next Steps**:\n");
- for suggestion in &checklist.suggestions {
- md.push_str(&format!("- {}\n", suggestion));
- }
- }
-
- md
-}
-
-fn capitalize(s: &str) -> String {
- let mut chars = s.chars();
- match chars.next() {
- None => String::new(),
- Some(first) => first.to_uppercase().collect::<String>() + chars.as_str(),
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn test_get_phase_deliverables_simple() {
- let plan = get_phase_deliverables_for_type("plan", "simple");
- assert_eq!(plan.phase, "plan");
- assert!(plan.requires_repository);
- assert_eq!(plan.deliverables.len(), 1);
- assert_eq!(plan.deliverables[0].id, "plan-document");
- assert_eq!(plan.deliverables[0].priority, DeliverablePriority::Required);
-
- let execute = get_phase_deliverables_for_type("execute", "simple");
- assert_eq!(execute.phase, "execute");
- assert!(execute.requires_repository);
- assert!(execute.requires_tasks);
- assert_eq!(execute.deliverables.len(), 1);
- assert_eq!(execute.deliverables[0].id, "pull-request");
- }
-
- #[test]
- fn test_get_phase_deliverables_specification() {
- let research = get_phase_deliverables_for_type("research", "specification");
- assert_eq!(research.deliverables.len(), 1);
- assert_eq!(research.deliverables[0].id, "research-notes");
-
- let specify = get_phase_deliverables_for_type("specify", "specification");
- assert_eq!(specify.deliverables.len(), 1);
- assert_eq!(specify.deliverables[0].id, "requirements-document");
-
- let review = get_phase_deliverables_for_type("review", "specification");
- assert_eq!(review.deliverables.len(), 1);
- assert_eq!(review.deliverables[0].id, "release-notes");
- }
-
- #[test]
- fn test_get_phase_deliverables_execute_type() {
- let execute = get_phase_deliverables_for_type("execute", "execute");
- assert!(execute.deliverables.is_empty());
- assert!(execute.requires_repository);
- assert!(execute.requires_tasks);
- }
-
- #[test]
- fn test_check_deliverables_met() {
- // No deliverables marked complete
- let result = check_deliverables_met("plan", "simple", &[], &[], true);
- assert!(!result.deliverables_met);
- assert!(!result.missing.is_empty());
-
- // Deliverable marked complete
- let completed = vec!["plan-document".to_string()];
- let result = check_deliverables_met("plan", "simple", &completed, &[], true);
- assert!(result.deliverables_met);
- assert!(result.ready_to_advance);
- }
-
- #[test]
- fn test_phase_checklist() {
- let completed = vec!["plan-document".to_string()];
- let checklist = get_phase_checklist_for_type("plan", &completed, &[], true, "simple");
- assert_eq!(checklist.completion_percentage, 100);
- assert!(checklist.deliverables[0].completed);
- }
-}