BE improvements

This commit is contained in:
Louis Knight-Webb
2025-06-21 19:45:47 +01:00
parent 0b27d6a427
commit d7ab69e8ae
5 changed files with 18 additions and 32 deletions

View File

@@ -73,10 +73,7 @@ pub struct TaskAttempt {
#[derive(Debug, Deserialize, TS)] #[derive(Debug, Deserialize, TS)]
#[ts(export)] #[ts(export)]
pub struct CreateTaskAttempt { pub struct CreateTaskAttempt {
pub task_id: Uuid, pub executor: Option<String>, // Optional executor name (defaults to "echo")
pub worktree_path: String,
pub merge_commit: Option<String>,
pub executor: Option<String>,
} }
#[derive(Debug, Deserialize, TS)] #[derive(Debug, Deserialize, TS)]
@@ -156,9 +153,10 @@ impl TaskAttempt {
pool: &SqlitePool, pool: &SqlitePool,
data: &CreateTaskAttempt, data: &CreateTaskAttempt,
attempt_id: Uuid, attempt_id: Uuid,
task_id: Uuid,
) -> Result<Self, TaskAttemptError> { ) -> Result<Self, TaskAttemptError> {
// First, get the task to get the project_id // First, get the task to get the project_id
let task = Task::find_by_id(pool, data.task_id) let task = Task::find_by_id(pool, task_id)
.await? .await?
.ok_or(TaskAttemptError::TaskNotFound)?; .ok_or(TaskAttemptError::TaskNotFound)?;
@@ -167,9 +165,12 @@ impl TaskAttempt {
.await? .await?
.ok_or(TaskAttemptError::ProjectNotFound)?; .ok_or(TaskAttemptError::ProjectNotFound)?;
// Generate worktree path automatically
let worktree_path_str = format!("/tmp/mission-control-worktree-{}", attempt_id);
let worktree_path = Path::new(&worktree_path_str);
// Create the worktree using git2 // Create the worktree using git2
let repo = Repository::open(&project.git_repo_path)?; let repo = Repository::open(&project.git_repo_path)?;
let worktree_path = Path::new(&data.worktree_path);
// We no longer store base_commit in the database - it's retrieved live via git2 // We no longer store base_commit in the database - it's retrieved live via git2
@@ -190,9 +191,9 @@ impl TaskAttempt {
VALUES ($1, $2, $3, $4, $5) VALUES ($1, $2, $3, $4, $5)
RETURNING id as "id!: Uuid", task_id as "task_id!: Uuid", worktree_path, merge_commit, executor, created_at as "created_at!: DateTime<Utc>", updated_at as "updated_at!: DateTime<Utc>""#, RETURNING id as "id!: Uuid", task_id as "task_id!: Uuid", worktree_path, merge_commit, executor, created_at as "created_at!: DateTime<Utc>", updated_at as "updated_at!: DateTime<Utc>""#,
attempt_id, attempt_id,
data.task_id, task_id,
data.worktree_path, worktree_path_str,
data.merge_commit, Option::<String>::None, // merge_commit is always None during creation
data.executor data.executor
) )
.fetch_one(pool) .fetch_one(pool)

View File

@@ -113,7 +113,7 @@ pub async fn create_task_attempt(
Path((project_id, task_id)): Path<(Uuid, Uuid)>, Path((project_id, task_id)): Path<(Uuid, Uuid)>,
Extension(pool): Extension<SqlitePool>, Extension(pool): Extension<SqlitePool>,
Extension(app_state): Extension<crate::app_state::AppState>, Extension(app_state): Extension<crate::app_state::AppState>,
Json(mut payload): Json<CreateTaskAttempt>, Json(payload): Json<CreateTaskAttempt>,
) -> Result<ResponseJson<ApiResponse<TaskAttempt>>, StatusCode> { ) -> Result<ResponseJson<ApiResponse<TaskAttempt>>, StatusCode> {
// Verify task exists in project first // Verify task exists in project first
match Task::exists(&pool, task_id, project_id).await { match Task::exists(&pool, task_id, project_id).await {
@@ -127,10 +127,7 @@ pub async fn create_task_attempt(
let id = Uuid::new_v4(); let id = Uuid::new_v4();
// Ensure the task_id in the payload matches the path parameter match TaskAttempt::create(&pool, &payload, id, task_id).await {
payload.task_id = task_id;
match TaskAttempt::create(&pool, &payload, id).await {
Ok(attempt) => { Ok(attempt) => {
// Start execution asynchronously (don't block the response) // Start execution asynchronously (don't block the response)
let pool_clone = pool.clone(); let pool_clone = pool.clone();

View File

@@ -132,23 +132,11 @@ pub async fn create_task_and_start(
// Create task attempt // Create task attempt
let attempt_id = Uuid::new_v4(); let attempt_id = Uuid::new_v4();
let worktree_path = format!(
"/tmp/task-{}-attempt-{}",
task_id,
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_millis()
);
let attempt_payload = CreateTaskAttempt { let attempt_payload = CreateTaskAttempt {
task_id,
worktree_path,
merge_commit: None,
executor: Some("claude".to_string()), // Default executor executor: Some("claude".to_string()), // Default executor
}; };
match TaskAttempt::create(&pool, &attempt_payload, attempt_id).await { match TaskAttempt::create(&pool, &attempt_payload, attempt_id, task_id).await {
Ok(attempt) => { Ok(attempt) => {
// Start execution asynchronously (don't block the response) // Start execution asynchronously (don't block the response)
let pool_clone = pool.clone(); let pool_clone = pool.clone();

View File

@@ -355,7 +355,7 @@ export function ProjectTasks() {
</div> </div>
) : ( ) : (
<div className="px-8 overflow-x-scroll my-4"> <div className="px-8 overflow-x-scroll my-4">
<div className="min-w-[900px] max-w-[2000px] relative"> <div className="min-w-[900px] max-w-[2000px] relative py-1">
<TaskKanbanBoard <TaskKanbanBoard
tasks={tasks} tasks={tasks}
onDragEnd={handleDragEnd} onDragEnd={handleDragEnd}

View File

@@ -34,17 +34,17 @@ export type TaskWithAttemptStatus = { id: string, project_id: string, title: str
export type UpdateTask = { title: string | null, description: string | null, status: TaskStatus | null, }; export type UpdateTask = { title: string | null, description: string | null, status: TaskStatus | null, };
export type TaskAttemptStatus = "init" | "setuprunning" | "setupcomplete" | "setupfailed" | "executorrunning" | "executorcomplete" | "executorfailed" | "paused"; export type TaskAttemptStatus = "setuprunning" | "setupcomplete" | "setupfailed" | "executorrunning" | "executorcomplete" | "executorfailed";
export type TaskAttempt = { id: string, task_id: string, worktree_path: string, merge_commit: string | null, executor: string | null, created_at: string, updated_at: string, }; export type TaskAttempt = { id: string, task_id: string, worktree_path: string, merge_commit: string | null, executor: string | null, created_at: string, updated_at: string, };
export type CreateTaskAttempt = { task_id: string, worktree_path: string, merge_commit: string | null, executor: string | null, }; export type CreateTaskAttempt = { executor: string | null, };
export type UpdateTaskAttempt = Record<string, never>; export type UpdateTaskAttempt = Record<string, never>;
export type TaskAttemptActivity = { id: string, task_attempt_id: string, status: TaskAttemptStatus, note: string | null, created_at: string, }; export type TaskAttemptActivity = { id: string, execution_process_id: string, status: TaskAttemptStatus, note: string | null, created_at: string, };
export type CreateTaskAttemptActivity = { task_attempt_id: string, status: TaskAttemptStatus | null, note: string | null, }; export type CreateTaskAttemptActivity = { execution_process_id: string, status: TaskAttemptStatus | null, note: string | null, };
export type DirectoryEntry = { name: string, path: string, is_directory: boolean, is_git_repo: boolean, }; export type DirectoryEntry = { name: string, path: string, is_directory: boolean, is_git_repo: boolean, };