diff --git a/.agents/scripts/supervisor-helper.sh b/.agents/scripts/supervisor-helper.sh index ec2a5ef4af..a9d25b5cba 100755 --- a/.agents/scripts/supervisor-helper.sh +++ b/.agents/scripts/supervisor-helper.sh @@ -12502,6 +12502,146 @@ cmd_notify() { return 0 } +####################################### +# Dispatch a decomposition worker for a #plan task (t274) +# Reads PLANS.md section and generates subtasks in TODO.md +# with #auto-dispatch tags for autonomous execution. +# +# This is a special worker that IS allowed to edit TODO.md +# because it's generating subtasks for orchestration. +# +# Arguments: +# $1 - task_id (e.g., t199) +# $2 - plan_anchor (e.g., 2026-02-09-content-creation-agent-architecture) +# $3 - repo path +####################################### +dispatch_decomposition_worker() { + local task_id="$1" + local plan_anchor="$2" + local repo="$3" + + if [[ -z "$task_id" || -z "$plan_anchor" || -z "$repo" ]]; then + log_error "dispatch_decomposition_worker: missing required arguments" + return 1 + fi + + local plans_file="$repo/todo/PLANS.md" + if [[ ! -f "$plans_file" ]]; then + log_error " $task_id: PLANS.md not found at $plans_file" + return 1 + fi + + # Build decomposition prompt with explicit TODO.md edit permission + local decomposition_prompt + read -r -d '' decomposition_prompt </dev/null; then + ( + cd "$repo" || exit 1 + echo "$decomposition_prompt" | Claude > "$worker_log" 2>&1 + local exit_code=$? + echo "Worker exit code: $exit_code" >> "$worker_log" + exit "$exit_code" + ) & + local worker_pid=$! + log_success " Decomposition worker dispatched (PID: $worker_pid)" + + # Update task metadata with worker PID + local escaped_id + escaped_id=$(sql_escape "$task_id") + db "$SUPERVISOR_DB" "UPDATE tasks SET metadata = metadata || ',decomposition_worker_pid=$worker_pid' WHERE id = '$escaped_id';" 2>/dev/null || true + else + log_error " Claude CLI not found — cannot dispatch decomposition worker" + return 1 + fi + + return 0 +} + ####################################### # Scan TODO.md for tasks tagged #auto-dispatch or in a # "Dispatch Queue" section. Auto-adds them to supervisor @@ -12628,6 +12768,65 @@ cmd_auto_pickup() { done <<< "$section_tasks" fi + # Strategy 3: Find #plan tasks with PLANS.md references but no subtasks (t274) + # Matches: - [ ] tXXX description #plan ... → [todo/PLANS.md#anchor] + # Dispatches decomposition worker to generate subtasks with #auto-dispatch + local plan_tasks + plan_tasks=$(grep -E '^[[:space:]]*- \[ \] (t[0-9]+) .*#plan.*→ \[todo/PLANS\.md#' "$todo_file" 2>/dev/null || true) + + if [[ -n "$plan_tasks" ]]; then + while IFS= read -r line; do + local task_id + task_id=$(echo "$line" | grep -oE 't[0-9]+' | head -1) + if [[ -z "$task_id" ]]; then + continue + fi + + # Check if task already has subtasks (e.g., t001.1, t001.2) + local has_subtasks + has_subtasks=$(grep -E "^[[:space:]]+-[[:space:]]\[[[:space:]xX]\][[:space:]]${task_id}\.[0-9]+" "$todo_file" 2>/dev/null || true) + if [[ -n "$has_subtasks" ]]; then + log_info " $task_id: already has subtasks — skipping auto-decomposition" + continue + fi + + # Check if already in supervisor + local existing + existing=$(db "$SUPERVISOR_DB" "SELECT status FROM tasks WHERE id = '$(sql_escape "$task_id")';" 2>/dev/null || true) + if [[ -n "$existing" ]]; then + if [[ "$existing" == "complete" || "$existing" == "cancelled" ]]; then + continue + fi + log_info " $task_id: already tracked (status: $existing)" + continue + fi + + # Pre-pickup check: skip tasks with merged PRs (t224). + if check_task_already_done "$task_id" "$repo"; then + log_info " $task_id: already completed (merged PR) — skipping auto-pickup" + continue + fi + + # Extract PLANS.md anchor from the line + local plan_anchor + plan_anchor=$(echo "$line" | grep -oE 'todo/PLANS\.md#[^]]+' | sed 's/todo\/PLANS\.md#//' || true) + if [[ -z "$plan_anchor" ]]; then + log_warn " $task_id: #plan tag found but no PLANS.md anchor — skipping" + continue + fi + + # Add to supervisor with special metadata for decomposition + if cmd_add "$task_id" --repo "$repo" --metadata "plan_anchor=$plan_anchor,needs_decomposition=true" 2>/dev/null; then + picked_up=$((picked_up + 1)) + log_success " Auto-picked: $task_id (#plan task for decomposition)" + + # Dispatch decomposition worker immediately + log_info " Dispatching decomposition worker for $task_id..." + dispatch_decomposition_worker "$task_id" "$plan_anchor" "$repo" + fi + done <<< "$plan_tasks" + fi + if [[ "$picked_up" -eq 0 ]]; then log_info "No new tasks to pick up" else