summaryrefslogtreecommitdiff
path: root/makima/src/llm/phase_guidance.rs
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-05-17 21:23:20 +0100
committerGitHub <noreply@github.com>2026-05-17 21:23:20 +0100
commit0d996cf7590e3e52f424859c7d6f0e68640f119e (patch)
tree0f3898d9e2e2a3c312358dbf70c44f4ab1cf3648 /makima/src/llm/phase_guidance.rs
parentce29ae801bcc5a0ba76d5a8d1565242ab267a47d (diff)
downloadsoryu-0d996cf7590e3e52f424859c7d6f0e68640f119e.tar.gz
soryu-0d996cf7590e3e52f424859c7d6f0e68640f119e.zip
chore: remove LLM module + all dependent surfaces (#135)
Wholesale removal of the LLM integration layer. ~14,200 LOC deleted across backend and frontend. All chat-driven UIs go with it. ## Backend - Delete `src/llm/` (7,400 LOC): claude/groq clients, contract_tools, contract_evaluator, discuss_tools, mesh_tools, phase_guidance, task_output, templates, markdown round-trip, tools, transcript_analyzer. - Delete handlers wholly dependent on LLM: - `chat.rs` (file-level LLM chat at /files/{id}/chat) - `mesh_chat.rs` (mesh & task LLM chat + history) - `templates.rs` (/contract-types listing) - Strip LLM uses from `mesh_daemon.rs`: - `compute_action_directive` (used phase_guidance::check_deliverables_met to nudge supervisors with "all tasks done" messages). The auto-PR path below still fires when all tasks finish, so no behaviour lost. - `crate::llm::markdown_to_body` → inline 1-line replacement that wraps markdown content in a single BodyElement::Markdown. The editor re-parses on display, so round-trip is preserved. - Drop routes: /files/{id}/chat, /mesh/chat, /mesh/chat/history, /mesh/tasks/{id}/chat, /contract-types. - Drop the matching openapi registrations. ## Frontend - Delete components that were LLM-only: - `mesh/UnifiedMeshChatInput.tsx` - `listen/DiscussContractModal.tsx` - `listen/TranscriptAnalysisPanel.tsx` - `listen/ContractPickerModal.tsx` - `files/CliInput.tsx` - Delete the entire /listen page (its primary value-add was voice → LLM analysis → contract creation; without LLM the page is just a transcript display with no obvious user purpose). - Delete `hooks/useMeshChatHistory.ts` and `lib/listenApi.ts` (transcript-analysis API client to the already-Phase-5-removed listen handlers). - Strip api.ts of LLM exports: LlmModel, ChatMessage/Request/Response, UserQuestion/Answer, chatWithFile, MeshChat* types & functions, getMeshChatHistory, clearMeshChatHistory, chatWithMeshContext, ContractTypeTemplate, listContractTypes, chatWithContract, getContractChatHistory, clearContractChatHistory, discussContract, PhaseDefinition, DeliverableDefinition. - mesh.tsx: drop UnifiedMeshChatInput render + the chatContext memo + handleTaskUpdatedFromCli (only consumer was the input). - files.tsx: drop CliInput render + handleGenerateFromElement + handleBodyUpdate + handleClearFocus + suggestedPrompt state (all CliInput-only). - NavStrip: drop the /listen link. - main.tsx: drop the /listen route. ## Net diff: 37 files changed, 58 insertions, 14,281 deletions. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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);
- }
-}