summaryrefslogtreecommitdiff
path: root/makima/src/llm
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-01-24 15:18:21 +0000
committersoryu <soryu@soryu.co>2026-01-24 16:11:39 +0000
commitabc5fbed331ea527ccaac0cd4120c4a0650f8bc0 (patch)
treeebfaf1e5c76361abcc0423edb90697725d7839a4 /makima/src/llm
parent579c983d3efb8f1414ffb45b9e031f741cce5f76 (diff)
downloadsoryu-abc5fbed331ea527ccaac0cd4120c4a0650f8bc0.tar.gz
soryu-abc5fbed331ea527ccaac0cd4120c4a0650f8bc0.zip
feat: Simplify phase deliverables and add 'execute' contract type
## Changes ### Phase Deliverables Simplified - **Simple contract type**: - Plan phase: Only 'Plan' deliverable (required) - Execute phase: Only 'PR' deliverable (required) - **Specification contract type**: - Research phase: Only 'Research Notes' deliverable (required) - Specify phase: Only 'Requirements Document' deliverable (required) - Plan phase: Only 'Plan' deliverable (required) - Execute phase: Only 'PR' deliverable (required) - Review phase: Only 'Release Notes' deliverable (required) ### New 'execute' Contract Type - Only has 'execute' phase (no plan or review phases) - NO deliverables at all - executes tasks directly - Added to ContractType enum with proper Display/FromStr implementations - Added helper methods: `initial_phase()`, `terminal_phase()` ### API Updates - Added `get_phase_deliverables_for_type()` for contract-type-aware deliverables - Added `get_phase_checklist_for_type()` for contract-type-aware checklists - Added `check_phase_completion_for_type()` for contract-type-aware completion checks - Legacy functions remain for backward compatibility (default to 'simple' type) ### Files Modified - makima/src/llm/phase_guidance.rs - Core deliverable definitions - makima/src/db/models.rs - ContractType enum and Contract methods - makima/src/llm/mod.rs - Export new functions - makima/src/server/handlers/contract_daemon.rs - Use type-aware functions - makima/src/server/handlers/contract_chat.rs - Use type-aware functions Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat (limited to 'makima/src/llm')
-rw-r--r--makima/src/llm/mod.rs3
-rw-r--r--makima/src/llm/phase_guidance.rs347
2 files changed, 259 insertions, 91 deletions
diff --git a/makima/src/llm/mod.rs b/makima/src/llm/mod.rs
index c4f8e50..6167a42 100644
--- a/makima/src/llm/mod.rs
+++ b/makima/src/llm/mod.rs
@@ -19,7 +19,8 @@ pub use contract_tools::{
pub use groq::GroqClient;
pub use mesh_tools::{parse_mesh_tool_call, MeshToolExecutionResult, MeshToolRequest, MESH_TOOLS};
pub use phase_guidance::{
- check_phase_completion, format_checklist_markdown, get_phase_checklist, get_phase_deliverables,
+ check_phase_completion, check_phase_completion_for_type, format_checklist_markdown,
+ get_phase_checklist, get_phase_checklist_for_type, get_phase_deliverables, get_phase_deliverables_for_type,
DeliverableStatus, FileInfo, FilePriority, PhaseChecklist, PhaseDeliverables, RecommendedFile,
TaskInfo, TaskStats,
};
diff --git a/makima/src/llm/phase_guidance.rs b/makima/src/llm/phase_guidance.rs
index 0d4bb3d..df7bd24 100644
--- a/makima/src/llm/phase_guidance.rs
+++ b/makima/src/llm/phase_guidance.rs
@@ -2,6 +2,22 @@
//!
//! 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;
@@ -109,8 +125,86 @@ pub struct TaskInfo {
pub status: String,
}
-/// Get phase deliverables configuration
+/// 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
+///
+/// ## Contract Types
+///
+/// ### Simple
+/// - Plan: Only "Plan" deliverable (required)
+/// - Execute: Only "PR" deliverable (required)
+///
+/// ### Specification
+/// - Research: Only "Research Notes" deliverable (required)
+/// - Specify: Only "Requirements Document" deliverable (required)
+/// - Plan: Only "Plan" deliverable (required)
+/// - Execute: Only "PR" deliverable (required)
+/// - Review: Only "Release Notes" deliverable (required)
+///
+/// ### Execute
+/// - Execute: No deliverables at all
+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 deliverables for 'simple' contract type
+/// - Plan phase: Only "Plan" deliverable (required)
+/// - Execute phase: Only "PR" deliverable (required)
+fn get_simple_type_deliverables(phase: &str) -> PhaseDeliverables {
+ match phase {
+ "plan" => PhaseDeliverables {
+ phase: "plan".to_string(),
+ recommended_files: vec![
+ RecommendedFile {
+ template_id: "plan".to_string(),
+ name_suggestion: "Plan".to_string(),
+ priority: FilePriority::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(),
+ recommended_files: vec![
+ RecommendedFile {
+ template_id: "pr".to_string(),
+ name_suggestion: "PR".to_string(),
+ priority: FilePriority::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(),
+ recommended_files: vec![],
+ requires_repository: false,
+ requires_tasks: false,
+ guidance: "Unknown phase for simple contract type".to_string(),
+ },
+ }
+}
+
+/// Get deliverables for 'specification' contract type
+/// - Research: Only "Research Notes" deliverable (required)
+/// - Specify: Only "Requirements Document" deliverable (required)
+/// - Plan: Only "Plan" deliverable (required)
+/// - Execute: Only "PR" deliverable (required)
+/// - Review: Only "Release Notes" deliverable (required)
+fn get_specification_type_deliverables(phase: &str) -> PhaseDeliverables {
match phase {
"research" => PhaseDeliverables {
phase: "research".to_string(),
@@ -118,25 +212,13 @@ pub fn get_phase_deliverables(phase: &str) -> PhaseDeliverables {
RecommendedFile {
template_id: "research-notes".to_string(),
name_suggestion: "Research Notes".to_string(),
- priority: FilePriority::Recommended,
+ priority: FilePriority::Required,
description: "Document findings and insights during research".to_string(),
},
- RecommendedFile {
- template_id: "competitor-analysis".to_string(),
- name_suggestion: "Competitor Analysis".to_string(),
- priority: FilePriority::Recommended,
- description: "Analyze competitors and market positioning".to_string(),
- },
- RecommendedFile {
- template_id: "user-research".to_string(),
- name_suggestion: "User Research".to_string(),
- priority: FilePriority::Optional,
- description: "Document user interviews and persona insights".to_string(),
- },
],
requires_repository: false,
requires_tasks: false,
- guidance: "Focus on understanding the problem space, gathering information, and documenting findings. Create at least one research document before moving to Specify phase.".to_string(),
+ 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(),
@@ -147,74 +229,38 @@ pub fn get_phase_deliverables(phase: &str) -> PhaseDeliverables {
priority: FilePriority::Required,
description: "Define functional and non-functional requirements".to_string(),
},
- RecommendedFile {
- template_id: "user-stories".to_string(),
- name_suggestion: "User Stories".to_string(),
- priority: FilePriority::Recommended,
- description: "Define features from the user's perspective".to_string(),
- },
- RecommendedFile {
- template_id: "acceptance-criteria".to_string(),
- name_suggestion: "Acceptance Criteria".to_string(),
- priority: FilePriority::Recommended,
- description: "Define testable conditions for completion".to_string(),
- },
],
requires_repository: false,
requires_tasks: false,
- guidance: "Define what needs to be built with clear requirements and acceptance criteria. Ensure specifications are detailed enough for planning.".to_string(),
+ 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(),
recommended_files: vec![
RecommendedFile {
- template_id: "architecture".to_string(),
- name_suggestion: "Architecture Document".to_string(),
- priority: FilePriority::Recommended,
- description: "Document system architecture and design decisions".to_string(),
- },
- RecommendedFile {
- template_id: "task-breakdown".to_string(),
- name_suggestion: "Task Breakdown".to_string(),
+ template_id: "plan".to_string(),
+ name_suggestion: "Plan".to_string(),
priority: FilePriority::Required,
- description: "Break down work into implementable tasks".to_string(),
- },
- RecommendedFile {
- template_id: "technical-design".to_string(),
- name_suggestion: "Technical Design".to_string(),
- priority: FilePriority::Optional,
- description: "Detailed technical specification".to_string(),
+ description: "Implementation plan detailing the approach and tasks".to_string(),
},
],
requires_repository: true,
requires_tasks: false,
- guidance: "Design the solution and break down work into tasks. A repository must be configured before moving to Execute phase.".to_string(),
+ 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(),
recommended_files: vec![
RecommendedFile {
- template_id: "dev-notes".to_string(),
- name_suggestion: "Development Notes".to_string(),
- priority: FilePriority::Recommended,
- description: "Track implementation details and decisions".to_string(),
- },
- RecommendedFile {
- template_id: "test-plan".to_string(),
- name_suggestion: "Test Plan".to_string(),
- priority: FilePriority::Optional,
- description: "Document testing strategy and test cases".to_string(),
- },
- RecommendedFile {
- template_id: "implementation-log".to_string(),
- name_suggestion: "Implementation Log".to_string(),
- priority: FilePriority::Optional,
- description: "Chronological log of implementation progress".to_string(),
+ template_id: "pr".to_string(),
+ name_suggestion: "PR".to_string(),
+ priority: FilePriority::Required,
+ description: "Pull request with the implemented changes".to_string(),
},
],
requires_repository: true,
requires_tasks: true,
- guidance: "Execute the planned tasks, implement features, and track progress. Complete all tasks before moving to Review phase.".to_string(),
+ 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(),
@@ -225,41 +271,61 @@ pub fn get_phase_deliverables(phase: &str) -> PhaseDeliverables {
priority: FilePriority::Required,
description: "Document changes for release communication".to_string(),
},
- RecommendedFile {
- template_id: "review-checklist".to_string(),
- name_suggestion: "Review Checklist".to_string(),
- priority: FilePriority::Recommended,
- description: "Comprehensive checklist for code and feature review".to_string(),
- },
- RecommendedFile {
- template_id: "retrospective".to_string(),
- name_suggestion: "Retrospective".to_string(),
- priority: FilePriority::Optional,
- description: "Reflect on the project and capture learnings".to_string(),
- },
],
requires_repository: false,
requires_tasks: false,
- guidance: "Review completed work, document the release, and conduct a retrospective. The contract can be completed after review.".to_string(),
+ 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(),
recommended_files: vec![],
requires_repository: false,
requires_tasks: false,
- guidance: "Unknown phase".to_string(),
+ guidance: "Unknown phase for specification contract type".to_string(),
},
}
}
-/// Build a phase checklist comparing expected vs actual deliverables
+/// Get deliverables for 'execute' contract type
+/// - Execute phase only: No deliverables at all
+fn get_execute_type_deliverables(phase: &str) -> PhaseDeliverables {
+ match phase {
+ "execute" => PhaseDeliverables {
+ phase: "execute".to_string(),
+ recommended_files: 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(),
+ recommended_files: 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 (legacy, defaults to "simple")
pub fn get_phase_checklist(
phase: &str,
files: &[FileInfo],
tasks: &[TaskInfo],
has_repository: bool,
) -> PhaseChecklist {
- let deliverables = get_phase_deliverables(phase);
+ get_phase_checklist_for_type(phase, files, tasks, has_repository, "simple")
+}
+
+/// Build a phase checklist comparing expected vs actual deliverables for a specific contract type
+pub fn get_phase_checklist_for_type(
+ phase: &str,
+ files: &[FileInfo],
+ tasks: &[TaskInfo],
+ has_repository: bool,
+ contract_type: &str,
+) -> PhaseChecklist {
+ let deliverables = get_phase_deliverables_for_type(phase, contract_type);
// Match files to expected deliverables
let file_deliverables: Vec<DeliverableStatus> = deliverables
@@ -475,14 +541,25 @@ fn generate_phase_summary(
}
}
-/// Check if phase targets are met for transition
+/// Check if phase targets are met for transition (legacy, defaults to "simple")
pub fn check_phase_completion(
phase: &str,
files: &[FileInfo],
tasks: &[TaskInfo],
has_repository: bool,
) -> bool {
- let checklist = get_phase_checklist(phase, files, tasks, has_repository);
+ check_phase_completion_for_type(phase, files, tasks, has_repository, "simple")
+}
+
+/// Check if phase targets are met for transition for a specific contract type
+pub fn check_phase_completion_for_type(
+ phase: &str,
+ files: &[FileInfo],
+ tasks: &[TaskInfo],
+ has_repository: bool,
+ contract_type: &str,
+) -> bool {
+ let checklist = get_phase_checklist_for_type(phase, files, tasks, has_repository, contract_type);
// Check required files are complete
let required_files_complete = checklist.file_deliverables.iter()
@@ -572,26 +649,93 @@ mod tests {
use super::*;
#[test]
- fn test_get_phase_deliverables() {
- let research = get_phase_deliverables("research");
+ fn test_get_phase_deliverables_simple() {
+ // Simple contract type: Plan phase has only "Plan" deliverable
+ let plan = get_phase_deliverables_for_type("plan", "simple");
+ assert_eq!(plan.phase, "plan");
+ assert!(plan.requires_repository);
+ assert_eq!(plan.recommended_files.len(), 1);
+ assert_eq!(plan.recommended_files[0].template_id, "plan");
+ assert_eq!(plan.recommended_files[0].priority, FilePriority::Required);
+
+ // Simple contract type: Execute phase has only "PR" deliverable
+ 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.recommended_files.len(), 1);
+ assert_eq!(execute.recommended_files[0].template_id, "pr");
+ assert_eq!(execute.recommended_files[0].priority, FilePriority::Required);
+ }
+
+ #[test]
+ fn test_get_phase_deliverables_specification() {
+ // Specification: Research phase has only "Research Notes" deliverable
+ let research = get_phase_deliverables_for_type("research", "specification");
assert_eq!(research.phase, "research");
assert!(!research.requires_repository);
- assert_eq!(research.recommended_files.len(), 3);
+ assert_eq!(research.recommended_files.len(), 1);
+ assert_eq!(research.recommended_files[0].template_id, "research-notes");
+ assert_eq!(research.recommended_files[0].priority, FilePriority::Required);
+
+ // Specification: Specify phase has only "Requirements Document" deliverable
+ let specify = get_phase_deliverables_for_type("specify", "specification");
+ assert_eq!(specify.phase, "specify");
+ assert_eq!(specify.recommended_files.len(), 1);
+ assert_eq!(specify.recommended_files[0].template_id, "requirements");
+ assert_eq!(specify.recommended_files[0].priority, FilePriority::Required);
+
+ // Specification: Plan phase has only "Plan" deliverable
+ let plan = get_phase_deliverables_for_type("plan", "specification");
+ assert_eq!(plan.phase, "plan");
+ assert_eq!(plan.recommended_files.len(), 1);
+ assert_eq!(plan.recommended_files[0].template_id, "plan");
+
+ // Specification: Execute phase has only "PR" deliverable
+ let execute = get_phase_deliverables_for_type("execute", "specification");
+ assert_eq!(execute.phase, "execute");
+ assert_eq!(execute.recommended_files.len(), 1);
+ assert_eq!(execute.recommended_files[0].template_id, "pr");
+
+ // Specification: Review phase has only "Release Notes" deliverable
+ let review = get_phase_deliverables_for_type("review", "specification");
+ assert_eq!(review.phase, "review");
+ assert_eq!(review.recommended_files.len(), 1);
+ assert_eq!(review.recommended_files[0].template_id, "release-notes");
+ assert_eq!(review.recommended_files[0].priority, FilePriority::Required);
+ }
- let plan = get_phase_deliverables("plan");
- assert!(plan.requires_repository);
- assert!(plan.recommended_files.iter().any(|f| f.template_id == "task-breakdown"));
+ #[test]
+ fn test_get_phase_deliverables_execute_type() {
+ // Execute contract type: Only execute phase, NO deliverables
+ let execute = get_phase_deliverables_for_type("execute", "execute");
+ assert_eq!(execute.phase, "execute");
+ assert!(execute.requires_repository);
+ assert!(execute.requires_tasks);
+ assert!(execute.recommended_files.is_empty()); // NO deliverables
+
+ // Execute contract type: Other phases should return empty deliverables
+ let plan = get_phase_deliverables_for_type("plan", "execute");
+ assert!(plan.recommended_files.is_empty());
}
#[test]
- fn test_phase_checklist_empty() {
- let checklist = get_phase_checklist("research", &[], &[], false);
+ fn test_phase_checklist_empty_simple() {
+ let checklist = get_phase_checklist_for_type("plan", &[], &[], false, "simple");
assert_eq!(checklist.completion_percentage, 0);
assert!(!checklist.suggestions.is_empty());
}
#[test]
- fn test_check_phase_completion() {
+ fn test_phase_checklist_execute_type_no_deliverables() {
+ // Execute contract type with no file deliverables
+ let checklist = get_phase_checklist_for_type("execute", &[], &[], true, "execute");
+ // Should have no file deliverables
+ assert!(checklist.file_deliverables.is_empty());
+ }
+
+ #[test]
+ fn test_check_phase_completion_specification() {
let files = vec![
FileInfo {
id: Uuid::new_v4(),
@@ -600,8 +744,31 @@ mod tests {
},
];
- // Specify phase has required file
- let complete = check_phase_completion("specify", &files, &[], false);
+ // Specify phase has required file for specification contract type
+ let complete = check_phase_completion_for_type("specify", &files, &[], false, "specification");
assert!(complete);
}
+
+ #[test]
+ fn test_check_phase_completion_simple() {
+ let files = vec![
+ FileInfo {
+ id: Uuid::new_v4(),
+ name: "Plan".to_string(),
+ contract_phase: Some("plan".to_string()),
+ },
+ ];
+
+ // Plan phase has required "Plan" file for simple contract type
+ let complete = check_phase_completion_for_type("plan", &files, &[], true, "simple");
+ assert!(complete);
+ }
+
+ #[test]
+ fn test_legacy_functions_default_to_simple() {
+ // Legacy get_phase_deliverables defaults to simple
+ let plan = get_phase_deliverables("plan");
+ assert_eq!(plan.recommended_files.len(), 1);
+ assert_eq!(plan.recommended_files[0].template_id, "plan");
+ }
}