use std::sync::mpsc::Sender; use anyhow::Result; use serde_json::{json, Value}; use crate::app::AppEvent; use crate::model::{ExecutionResponse, Plan, PlanStep, SessionSource, TaskConfig}; use crate::process; use crate::prompt; use crate::storage::toon; pub fn implement( repo_root: &std::path::Path, config: &TaskConfig, plan: &Plan, step: &PlanStep, event_tx: &Sender, ) -> Result { let goal_md = toon::read_markdown(&config.goal_file)?; let standards_md = toon::read_markdown(&config.standards_file)?; let context = build_execution_context(plan, step); let prompt = format!( concat!( "You are the autonomous execution worker for a Rust TUI-first controller.\n", "Execution mode only. Do not ask the user questions.\n", "Complete the active step with the smallest correct change set.\n\n", "Efficiency rules:\n", "- Inspect only files likely relevant to this step.\n", "- Avoid repository-wide searches unless the focused path is exhausted.\n", "- Prefer targeted verification and targeted tests over broad full-suite runs.\n", "- Keep output terse. Use short summaries and short notes.\n", "- If the requested change is already present, return done.\n", "- If the goal is genuinely ambiguous, set needs_goal_clarification=true.\n\n", "Return empty arrays for verification_commands, test_commands, or notes when not needed.\n\n", "Goal summary:\n{goal}\n\n", "Standards summary:\n{standards}\n\n", "Execution context:\n{context}\n" ), goal = prompt::compact_markdown(&goal_md, 8, 1200), standards = prompt::compact_markdown(&standards_md, 10, 1200), context = serde_json::to_string_pretty(&context)?, ); let schema = json!({ "type": "object", "additionalProperties": false, "required": ["status", "summary", "verification_commands", "test_commands", "notes", "needs_goal_clarification"], "properties": { "status": { "type": "string", "enum": ["done", "blocked", "needs-replan"] }, "summary": { "type": "string" }, "verification_commands": { "type": "array", "items": { "type": "string" } }, "test_commands": { "type": "array", "items": { "type": "string" } }, "notes": { "type": "array", "items": { "type": "string" } }, "needs_goal_clarification": { "type": "boolean" } } }); let raw = process::run_codex_with_schema( repo_root, &prompt, &schema, event_tx, SessionSource::Executor, Some(step.id.clone()), )?; Ok(serde_json::from_str(&raw)?) } fn build_execution_context(plan: &Plan, step: &PlanStep) -> Value { let dependency_steps = step .dependencies .iter() .filter_map(|dependency| { plan.steps .iter() .find(|candidate| &candidate.id == dependency) .map(|candidate| { json!({ "id": candidate.id, "title": prompt::truncate_text(&candidate.title, 100), "status": candidate.status, }) }) }) .collect::>(); let next_steps = plan .steps .iter() .filter(|candidate| candidate.id != step.id) .filter(|candidate| !candidate.status.is_done()) .take(3) .map(|candidate| { json!({ "id": candidate.id, "title": prompt::truncate_text(&candidate.title, 100), "dependencies": prompt::compact_string_vec(&candidate.dependencies, 4, 60), "status": candidate.status, }) }) .collect::>(); json!({ "goal_summary": prompt::truncate_text(&plan.goal_summary, 200), "active_step": prompt::compact_step(step), "dependency_steps": dependency_steps, "next_pending_steps": next_steps, }) }