Skip to content

Ręczna Kontrola Zadań (Manual Task Control)

Przegląd

System umożliwia programistom ręczną kontrolę nad zadaniami poprzez komendy wysyłane z Mattermost:

  • Reopen - Ponowne otwarcie ukończonego taska z dodatkowymi instrukcjami
  • Interrupt - Przerwanie działającego taska pilną wiadomością
  • User Commands - Specjalne komendy użytkownika (deploy, test, fix)

Reopen - Ponowne Otwarcie Taska

Przypadek Użycia

Task jest ukończony (w done/), ale programista chce dodać kolejne funkcjonalności bez tworzenia nowego taska.

Sytuacja:
DEV-7315 jest ukończony i w done/

Programista na Mattermost:
"DEV-7315 dodaj jeszcze dark mode do settings"

System:
✅ Reopenuje task
✅ Dodaje instrukcje do task.md
✅ Zachowuje kontekst (branch, repos)
✅ Watchdog automatycznie podejmuje

Workflow

┌────────────────────────────────────────┐
│  1. User na Mattermost                 │
│     "DEV-7315 dodaj dark mode"         │
└────────────┬───────────────────────────┘


┌────────────────────────────────────────┐
│  2. n8n Webhook                        │
│     - Parsuje komendę                  │
│     - Ekstrahuje task_id i message     │
└────────────┬───────────────────────────┘


┌────────────────────────────────────────┐
│  3. Tworzy JSON w control_commands/    │
│     {                                  │
│       "command_type": "reopen",        │
│       "task_id": "DEV-7315",           │
│       "message": "dodaj dark mode",    │
│       "user": "jan.kowalski"           │
│     }                                  │
└────────────┬───────────────────────────┘


┌────────────────────────────────────────┐
│  4. task_control_receiver.yaml (DAGU)  │
│     Wykrywa nowy plik (co minutę)      │
└────────────┬───────────────────────────┘


┌────────────────────────────────────────┐
│  5. process_task_control_commands.sh   │
│     Rozpoznaje typ: reopen             │
└────────────┬───────────────────────────┘


┌────────────────────────────────────────┐
│  6. reopen_task.sh                     │
│     - Znajduje task w done/            │
│     - Appends instrukcje do task.md    │
│     - Aktualizuje task.json            │
│     - Przenosi: done/ → todo/          │
└────────────┬───────────────────────────┘


┌────────────────────────────────────────┐
│  7. Watchdog                           │
│     Automatycznie podejmuje task       │
└────────────┬───────────────────────────┘


┌────────────────────────────────────────┐
│  8. Webhook Confirmation → Mattermost  │
│     "✅ Task DEV-7315 reopened"        │
└────────────────────────────────────────┘

Struktura Komendy (JSON)

Lokalizacja: tasks/control_commands/cmd_{timestamp}.json

json
{
  "command_type": "reopen",
  "task_id": "DEV-7315",
  "message": "dodaj jeszcze dark mode do settings",
  "user": "jan.kowalski",
  "channel": "dev-team",
  "timestamp": "2025-01-01T14:30:00Z"
}

Co Robi reopen_task.sh

1. Znajduje Task

bash
TASK_DIR="$TASKS_DONE_DIR/$TASK_ID"

if [ ! -d "$TASK_DIR" ]; then
  echo "❌ Error: Task $TASK_ID not found in done/"
  exit 1
fi

2. Appends Instrukcje do task.md

markdown
---
## 🔄 Additional Work Requested

**Date:** 2025-01-01T14:30:00Z
**Requested by:** jan.kowalski (via Mattermost)

### Additional Instructions:
dodaj jeszcze dark mode do settings

### Context:
This task was previously completed and is being reopened.
- Previous completion: 2025-01-01T10:00:00Z
- All previous work preserved (same branch, repos)
- Continue where the task left off
---

3. Aktualizuje task.json

bash
# Aktualizuje status i metadata
jq --arg now "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
   '.status = "todo" |
    .reopened_at = $now |
    .reopened_count = (.reopened_count // 0) + 1 |
    .started_at = null |
    .completed_at = null' \
   "$TASK_DIR/task.json" > "$TASK_DIR/task.json.tmp"

mv "$TASK_DIR/task.json.tmp" "$TASK_DIR/task.json"

4. Przenosi Task

bash
mv "$TASKS_DONE_DIR/$TASK_ID" "$TASKS_TODO_DIR/$TASK_ID"

5. Wysyła Webhook

bash
send_webhook "task_reopened" \
  --task_id "$TASK_ID" \
  --additional_instructions "$MESSAGE" \
  --reopened_count "$(jq -r '.reopened_count' task.json)" \
  --status "success"

Przykład task.json Po Reopen

json
{
  "task_id": "DEV-7315",
  "title": "Settings UI Unification",
  "status": "todo",
  "reopened_count": 1,
  "reopened_at": "2025-01-01T14:30:00Z",
  "started_at": null,
  "completed_at": null,
  "repositories": [
    {
      "folder": "sembot-angular",
      "working_branch": "feature/DEV-7315"  // ← Ten sam branch!
    }
  ]
}

Kluczowe Cechy Reopen

  • Zachowuje kontekst: Ten sam branch, repos, workspace
  • Nie nadpisuje task.md: Appends na końcu
  • Śledzi reopeny: reopened_count, reopened_at
  • Automatyczny pickup: Watchdog od razu podejmuje
  • Historia zachowana: Wszystkie poprzednie artefakty w task/

Interrupt - Przerwanie Działającego Taska

Przypadek Użycia

Task jest w trakcie wykonywania (in_progress/), ale programista chce pilnie przerwać pracę z konkretną instrukcją.

Sytuacja:
DEV-7315 jest in_progress, wykonuje subtaski P1

Programista na Mattermost:
"DEV-7315 PILNE: najpierw zweryfikuj czy build działa"

System:
✅ Tworzy interrupt
✅ Orchestrator wykrywa przed next subtask
✅ Wykonuje interrupt zgodnie z priority
✅ Kontynuuje normalną pracę

Workflow

┌────────────────────────────────────────┐
│  1. User na Mattermost                 │
│     "DEV-7315 PILNE: zweryfikuj build" │
└────────────┬───────────────────────────┘


┌────────────────────────────────────────┐
│  2. n8n Webhook                        │
│     - Parsuje komendę                  │
│     - Wykrywa priority keyword         │
│       ("PILNE" → urgent)               │
└────────────┬───────────────────────────┘


┌────────────────────────────────────────┐
│  3. Tworzy JSON w control_commands/    │
│     {                                  │
│       "command_type": "interrupt",     │
│       "task_id": "DEV-7315",           │
│       "message": "zweryfikuj build",   │
│       "priority": "urgent"             │
│     }                                  │
└────────────┬───────────────────────────┘


┌────────────────────────────────────────┐
│  4. task_control_receiver.yaml (DAGU)  │
│     Wykrywa plik                       │
└────────────┬───────────────────────────┘


┌────────────────────────────────────────┐
│  5. process_task_control_commands.sh   │
│     Rozpoznaje typ: interrupt          │
└────────────┬───────────────────────────┘


┌────────────────────────────────────────┐
│  6. create_task_interrupt.sh           │
│     - Znajduje task w in_progress/     │
│     - Tworzy interrupts/interrupt.json │
└────────────┬───────────────────────────┘


┌────────────────────────────────────────┐
│  7. orchestrator_team.yaml             │
│     - Wykrywa interrupt                │
│     - Wykonuje według priority:        │
│       * urgent → stop natychmiast      │
│       * high → dokończ current         │
│       * normal → dokończ priority      │
└────────────┬───────────────────────────┘


┌────────────────────────────────────────┐
│  8. Wykonuje Interrupt Task            │
│     - Tworzy temporary subtask         │
│     - Uruchamia z interrupt message    │
│     - Po wykonaniu: status=completed   │
└────────────┬───────────────────────────┘


┌────────────────────────────────────────┐
│  9. Kontynuuje Normalne Subtaski       │
│     Resume z miejsca przerwania        │
└────────────┬───────────────────────────┘


┌────────────────────────────────────────┐
│  10. Webhook Confirmation → Mattermost │
│      "✅ Interrupt executed"           │
└────────────────────────────────────────┘

Struktura Komendy (JSON)

Lokalizacja: tasks/control_commands/cmd_{timestamp}.json

json
{
  "command_type": "interrupt",
  "task_id": "DEV-7315",
  "message": "najpierw zweryfikuj czy build działa",
  "priority": "urgent",
  "user": "jan.kowalski",
  "channel": "dev-team",
  "timestamp": "2025-01-01T14:35:00Z"
}

Priority Levels

PriorityBehaviorKiedy Użyć
urgentStop natychmiast, wykonaj przed wszystkimKrytyczny bug, build broken, security issue
highDokończ current subtask, potem interruptWażna weryfikacja przed kontynuowaniem
normalDokończ current priority level (P0/P1/P2/P3)Dodatkowa informacja, non-blocking

Interrupt File Structure

Lokalizacja: tasks/in_progress/DEV-7315/interrupts/interrupt_{timestamp}_{random}.json

json
{
  "interrupt_id": "interrupt_1735742100_12345",
  "task_id": "DEV-7315",
  "priority": "urgent",
  "message": "najpierw zweryfikuj czy build działa",
  "created_at": "2025-01-01T14:35:00Z",
  "status": "pending",
  "created_by": "user"
}

Orchestrator Detection Logic

bash
# orchestrator_team.yaml - przed każdym subtaskiem

# Sprawdź czy są pending interrupts
INTERRUPT_FILES=$(find "$TASK_DIR/interrupts" -name "interrupt_*.json" -type f 2>/dev/null)

if [ -n "$INTERRUPT_FILES" ]; then
  echo "🚨 Interrupt detected!"

  for INTERRUPT_FILE in $INTERRUPT_FILES; do
    INTERRUPT_PRIORITY=$(jq -r '.priority' "$INTERRUPT_FILE")
    INTERRUPT_MESSAGE=$(jq -r '.message' "$INTERRUPT_FILE")
    INTERRUPT_STATUS=$(jq -r '.status' "$INTERRUPT_FILE")

    if [ "$INTERRUPT_STATUS" = "pending" ]; then
      case "$INTERRUPT_PRIORITY" in
        urgent)
          echo "⚠️  URGENT interrupt - stopping immediately"
          execute_interrupt "$INTERRUPT_FILE"
          ;;
        high)
          if [ "$CURRENT_SUBTASK_RUNNING" = "true" ]; then
            echo "⏳ HIGH priority - will interrupt after current subtask"
            INTERRUPT_AFTER_CURRENT=true
          else
            execute_interrupt "$INTERRUPT_FILE"
          fi
          ;;
        normal)
          if [ "$CURRENT_PRIORITY_DONE" = "true" ]; then
            execute_interrupt "$INTERRUPT_FILE"
          else
            echo "ℹ️  NORMAL priority - will execute after current priority level"
          fi
          ;;
      esac
    fi
  done
fi

Execute Interrupt Logic

bash
execute_interrupt() {
  local interrupt_file=$1
  local interrupt_id=$(jq -r '.interrupt_id' "$interrupt_file")
  local interrupt_message=$(jq -r '.message' "$interrupt_file")

  echo "🔄 Executing interrupt: $interrupt_id"

  # Tworzy temporary subtask
  TEMP_SUBTASK_DIR="$SUBTASKS_DIR/INTERRUPT/in_progress/interrupt_$interrupt_id"
  mkdir -p "$TEMP_SUBTASK_DIR"

  # task.md dla interrupt
  cat > "$TEMP_SUBTASK_DIR/task.md" <<EOF
# Interrupt Request from User

## Message
$interrupt_message

## Instructions
This is a user interrupt. Please address the request and continue with the task.
EOF

  # task.json dla interrupt
  cat > "$TEMP_SUBTASK_DIR/task.json" <<EOF
{
  "task_id": "interrupt_$interrupt_id",
  "priority": "INTERRUPT",
  "title": "User Interrupt Request",
  "ai": {
    "provider": "${AI_PROVIDER}",
    "model": "${AI_MODEL}",
    "start_command": "/plugin-codegen:execute /task"
  },
  "created_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
}
EOF

  # Wykonaj interrupt task (w Docker worker)
  docker exec "$WORKER_CONTAINER" \
    bash -c "cd /task && ${AI_PROVIDER}.sh --command '/plugin-codegen:execute /task'"

  # Oznacz jako completed
  jq '.status = "completed"' "$interrupt_file" > "$interrupt_file.tmp"
  mv "$interrupt_file.tmp" "$interrupt_file"

  # Przenieś subtask do done
  mv "$TEMP_SUBTASK_DIR" "$SUBTASKS_DIR/INTERRUPT/done/interrupt_$interrupt_id"

  echo "✅ Interrupt completed"
  send_webhook "task_interrupted_completed" \
    --task_id "$TASK_ID" \
    --interrupt_id "$interrupt_id"
}

Struktura Katalogów

tasks/in_progress/DEV-7315/
├── task.json
├── task.md
├── subtasks/
│   ├── P0_critic_in_progress/
│   ├── P1_frontend_component_todo/
│   └── P1_frontend_service_in_progress/  ← currently running
└── interrupts/                            ← nowy katalog
    ├── interrupt_1735742100_12345.json    ← pending
    └── interrupt_1735742200_67890.json    ← completed

Przykład Użycia

Urgent Interrupt (Stop Natychmiast)

User: "DEV-7315 PILNE: backend API zwraca 500, trzeba naprawić"

System:
1. Zatrzymuje current subtask (P1_frontend_service)
2. Tworzy interrupt task
3. AI debuguje API issue
4. Po naprawie kontynuuje P1_frontend_service

High Priority (Dokończ Current)

User: "DEV-7315 sprawdź czy testy przechodzą zanim pójdziesz dalej"

System:
1. Dokańcza P1_frontend_service (current)
2. Tworzy interrupt task
3. AI uruchamia testy
4. Kontynuuje z P1_backend_api

Normal Priority

User: "DEV-7315 pamiętaj żeby dodać komentarze do kodu"

System:
1. Dokańcza wszystkie P1 subtaski
2. Przed przejściem do P2 wykonuje interrupt
3. AI dodaje komentarze
4. Kontynuuje z P2

User Commands (Special Commands)

Przypadek Użycia

Użytkownik chce wykonać specjalne operacje (deploy, run script, test and fix) bez tworzenia pełnego taska.

Typy User Commands

1. test_and_fix

Uruchom testy i napraw błędy automatycznie.

json
{
  "command_id": "user_cmd_1234567890",
  "task_id": "DEV-7315",
  "priority": "P1",
  "command_type": "test_and_fix",
  "description": "Uruchom wszystkie testy i napraw błędy",
  "params": {
    "test_command": "npm test",
    "auto_fix": true,
    "max_iterations": 5
  },
  "created_at": "2025-12-21T10:30:00Z"
}

2. deploy

Wydaj aplikację na serwer.

json
{
  "command_type": "deploy",
  "description": "Deploy na dev6",
  "params": {
    "environment": "dev6",
    "branch": "feature/DEV-7315"
  }
}

3. run_script

Wykonaj dowolny skrypt shell.

json
{
  "command_type": "run_script",
  "description": "Uruchom migracje",
  "params": {
    "script": "php artisan migrate --force"
  }
}

4. custom

Dowolna komenda AI.

json
{
  "command_type": "custom",
  "description": "Dodaj logging do wszystkich kontrolerów",
  "params": {
    "instructions": "Add PSR-3 logging to all controller methods"
  }
}

Lokalizacja

User commands trafiają do:

tasks/in_progress/{TASK_ID}/user_commands/cmd_{timestamp}.json

Processing

Watchdog lub dedykowany workflow skanuje user_commands/ co 5 minut i automatycznie tworzy subtaski.


Error Handling

Reopen Errors

bash
# Task nie istnieje w done/
 Error: Task DEV-7315 not found in done/
 Plik: cmd_123.json.error
 Webhook: {"event_type": "task_reopened", "status": "error", "error": "not found"}

Interrupt Errors

bash
# Task nie istnieje w in_progress/
 Error: Task DEV-7315 not found in in_progress/
   Task must be currently running to interrupt
 Plik: cmd_456.json.error
 Webhook: {"event_type": "task_interrupted", "status": "error"}

Invalid JSON

bash
 Invalid JSON, skipping
 Plik przeniesiony do .error

Missing Fields

bash
 Missing required fields (command_type, task_id, message)
 Plik przeniesiony do .error

DAGU Workflow Configuration

yaml
# task_control_receiver.yaml
name: "task_control_receiver"
description: "Processes task control commands (reopen, interrupt)"
schedule: "* * * * *"  # Co minutę

steps:
  - name: process_control_commands
    command: |
      export TASKS_DIR TASKS_TODO_DIR TASKS_IN_PROGRESS_DIR TASKS_DONE_DIR
      export N8N_WEBHOOK_URL

      $SCRIPTS_DIR/process_task_control_commands.sh tasks/control_commands

Monitoring

Sprawdzanie Pending Commands

bash
# Lista pending control commands
ls tasks/control_commands/

# Sprawdź konkretny command
cat tasks/control_commands/cmd_1234567890.json | jq '.'

Sprawdzanie Pending Interrupts

bash
# Dla konkretnego taska
ls tasks/in_progress/DEV-7315/interrupts/*.json

# Status interrupt
cat tasks/in_progress/DEV-7315/interrupts/interrupt_xyz.json | jq '.status'

Logi

bash
# Logi DAGU task_control_receiver
cat ~/.dagu/logs/task_control_receiver/*/task_control_receiver.log

# Error files
ls tasks/control_commands/*.error
cat tasks/control_commands/cmd_123.json.error

Best Practices

1. Używaj Reopen dla iteracji

Dobrze: Task ukończony, ale trzeba dodać feature

"DEV-7315 dodaj dark mode"

Źle: Tworzenie nowego taska dla małej zmiany

Nowy task: DEV-7316 "Add Dark Mode to DEV-7315"

2. Używaj Interrupt dla pilnych zmian

Dobrze: Coś się zepsuło, trzeba naprawić natychmiast

"DEV-7315 PILNE: API zwraca 500, napraw"

Źle: Interrupt dla non-urgent requests

"DEV-7315 pamiętaj żeby dodać komentarze" (to powinno być normal priority)

3. Określaj Priority w Interrupt

PILNE / URGENT / CRITICAL → urgent
WAŻNE / HIGH / IMPORTANT → high
NORMAL / INFO → normal

Changelog

v2.0 - Manual Control

  • ✅ Reopen functionality
  • ✅ Interrupt with priority levels
  • ✅ User commands support

v1.0 - Basic Workflow

  • ✅ Automatyczny workflow bez manual control