summaryrefslogtreecommitdiff
path: root/makima/src/server
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-01-24 15:50:43 +0000
committersoryu <soryu@soryu.co>2026-01-24 16:17:59 +0000
commit595548db950eca303a7d73ca09f31895d291534f (patch)
treead6317979526e6a683fa6987bc9979ec3ac055f3 /makima/src/server
parentabc5fbed331ea527ccaac0cd4120c4a0650f8bc0 (diff)
downloadsoryu-595548db950eca303a7d73ca09f31895d291534f.tar.gz
soryu-595548db950eca303a7d73ca09f31895d291534f.zip
feat(frontend): Add Templates page for managing contract templates
- Add NavStrip component for top navigation between pages - Create Templates page with template card grid layout - Create TemplateEditor component for editing phase deliverables - Add navigation state management in stores - Implement three built-in templates: - Simple: Plan phase (Plan deliverable), Execute phase (PR deliverable) - Specification: Research, Specify, Plan, Execute, Review phases with deliverables - Execute: Single execute phase with no deliverables - Support for creating custom templates with configurable phases/deliverables - Templates are persisted to localStorage - Add comprehensive CSS styling matching existing design patterns Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> [WIP] Heartbeat checkpoint - 2026-01-24 16:13:00 UTC [WIP] Heartbeat checkpoint - 2026-01-24 16:14:29 UTC
Diffstat (limited to 'makima/src/server')
-rw-r--r--makima/src/server/handlers/contract_chat.rs164
1 files changed, 162 insertions, 2 deletions
diff --git a/makima/src/server/handlers/contract_chat.rs b/makima/src/server/handlers/contract_chat.rs
index 0b6bad1..a0708da 100644
--- a/makima/src/server/handlers/contract_chat.rs
+++ b/makima/src/server/handlers/contract_chat.rs
@@ -433,8 +433,8 @@ When a new contract is created or the user seems unsure:
fn build_contract_context(contract: &crate::db::models::ContractWithRelations) -> String {
let c = &contract.contract;
let mut context = format!(
- "Name: {}\nID: {}\nPhase: {}\nStatus: {}\n",
- c.name, c.id, c.phase, c.status
+ "Name: {}\nID: {}\nPhase: {}\nStatus: {}\nContract Type: {}\nAutonomous Loop: {}\n",
+ c.name, c.id, c.phase, c.status, c.contract_type, c.autonomous_loop
);
if let Some(ref desc) = c.description {
@@ -461,6 +461,25 @@ fn build_contract_context(contract: &crate::db::models::ContractWithRelations) -
context.push_str("\n");
context.push_str(&format_checklist_markdown(&phase_checklist));
+ // Add deliverable check result for phase transition readiness
+ // Note: pr_url is not available in TaskSummary, so we pass None here
+ // Full PR checking should be done via the check_deliverables_met tool
+ let deliverable_check = crate::llm::check_deliverables_met(
+ &c.phase,
+ &c.contract_type,
+ &file_infos,
+ &task_infos,
+ has_repository,
+ None, // pr_url not available in TaskSummary
+ );
+
+ // Add deliverable prompt guidance
+ context.push_str(&crate::llm::generate_deliverable_prompt_guidance(
+ &c.phase,
+ &c.contract_type,
+ &deliverable_check,
+ ));
+
// Files summary
context.push_str(&format!("\n### Files ({} total)\n", contract.files.len()));
if !contract.files.is_empty() {
@@ -1732,6 +1751,68 @@ async fn handle_contract_request(
};
}
+<<<<<<< HEAD
+=======
+ // Check if deliverables are met before allowing transition
+ let cwr = match get_contract_with_relations(pool, contract_id, owner_id).await {
+ Ok(Some(c)) => c,
+ Ok(None) | Err(_) => {
+ // Fall through - we'll just skip the deliverables check
+ return ContractRequestResult {
+ success: false,
+ message: "Failed to load contract for deliverables check".to_string(),
+ data: None,
+ };
+ }
+ };
+
+ let file_infos: Vec<FileInfo> = cwr.files.iter().map(|f| FileInfo {
+ id: f.id,
+ name: f.name.clone(),
+ contract_phase: f.contract_phase.clone(),
+ }).collect();
+
+ let task_infos: Vec<TaskInfo> = cwr.tasks.iter().map(|t| TaskInfo {
+ id: t.id,
+ name: t.name.clone(),
+ status: t.status.clone(),
+ }).collect();
+
+ let has_repository = !cwr.repositories.is_empty();
+ // Note: pr_url is not available in TaskSummary, so we skip PR checking here
+ // For simple contracts, the PR deliverable check will need to be done
+ // by fetching full task details if needed
+
+ let check_result = crate::llm::check_deliverables_met(
+ current_phase,
+ &contract.contract_type,
+ &file_infos,
+ &task_infos,
+ has_repository,
+ None, // pr_url not available in TaskSummary
+ );
+
+ // Block transition if deliverables are not met
+ if !check_result.deliverables_met {
+ return ContractRequestResult {
+ success: false,
+ message: format!(
+ "Cannot advance to '{}' phase: deliverables not met. {}",
+ new_phase, check_result.summary
+ ),
+ data: Some(json!({
+ "status": "deliverables_not_met",
+ "currentPhase": current_phase,
+ "requestedPhase": new_phase,
+ "deliverablesMet": false,
+ "requiredDeliverables": check_result.required_deliverables,
+ "missing": check_result.missing,
+ "action": "Complete the missing deliverables before advancing to the next phase"
+ })),
+ };
+ }
+
+>>>>>>> c6507b4 (feat: Add deliverables checking and auto-progress for contract phases)
// Check if phase_guard is enabled
if contract.phase_guard {
// If user provided feedback, return it for the task to address
@@ -1993,6 +2074,85 @@ async fn handle_contract_request(
}
}
+<<<<<<< HEAD
+=======
+ ContractToolRequest::CheckDeliverablesMet => {
+ match get_contract_with_relations(pool, contract_id, owner_id).await {
+ Ok(Some(cwr)) => {
+ let file_infos: Vec<FileInfo> = cwr.files.iter().map(|f| FileInfo {
+ id: f.id,
+ name: f.name.clone(),
+ contract_phase: f.contract_phase.clone(),
+ }).collect();
+
+ let task_infos: Vec<TaskInfo> = cwr.tasks.iter().map(|t| TaskInfo {
+ id: t.id,
+ name: t.name.clone(),
+ status: t.status.clone(),
+ }).collect();
+
+ let has_repository = !cwr.repositories.is_empty();
+
+ // Note: pr_url is not available in TaskSummary
+ // For simple contracts needing PR checking, full task details would need to be fetched
+ // For now, we pass None and the LLM can guide the user to ensure a PR exists
+
+ let check_result = crate::llm::check_deliverables_met(
+ &cwr.contract.phase,
+ &cwr.contract.contract_type,
+ &file_infos,
+ &task_infos,
+ has_repository,
+ None, // pr_url not available in TaskSummary
+ );
+
+ // Check if we should auto-progress
+ let auto_progress = crate::llm::should_auto_progress(
+ &cwr.contract.phase,
+ &cwr.contract.contract_type,
+ &file_infos,
+ &task_infos,
+ has_repository,
+ None, // pr_url not available in TaskSummary
+ cwr.contract.autonomous_loop,
+ );
+
+ ContractRequestResult {
+ success: true,
+ message: check_result.summary.clone(),
+ data: Some(json!({
+ "deliverablesMet": check_result.deliverables_met,
+ "readyToAdvance": check_result.ready_to_advance,
+ "phase": check_result.phase,
+ "nextPhase": check_result.next_phase,
+ "requiredDeliverables": check_result.required_deliverables,
+ "missing": check_result.missing,
+ "summary": check_result.summary,
+ "autoProgressRecommended": check_result.auto_progress_recommended,
+ "autoProgress": {
+ "shouldProgress": auto_progress.should_progress,
+ "nextPhase": auto_progress.next_phase,
+ "reason": auto_progress.reason,
+ "action": format!("{:?}", auto_progress.action),
+ },
+ "autonomousLoop": cwr.contract.autonomous_loop,
+ })),
+ }
+ }
+ Ok(None) => ContractRequestResult {
+ success: false,
+ message: "Contract not found".to_string(),
+ data: None,
+ },
+ Err(e) => ContractRequestResult {
+ success: false,
+ message: format!("Database error: {}", e),
+ data: None,
+ },
+ }
+ }
+
+>>>>>>> c6507b4 (feat: Add deliverables checking and auto-progress for contract phases)
// =============================================================================
// Task Derivation Handlers
// =============================================================================