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 podejmujeWorkflow
┌────────────────────────────────────────┐
│ 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
{
"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
TASK_DIR="$TASKS_DONE_DIR/$TASK_ID"
if [ ! -d "$TASK_DIR" ]; then
echo "❌ Error: Task $TASK_ID not found in done/"
exit 1
fi2. Appends Instrukcje do task.md
---
## 🔄 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
# 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
mv "$TASKS_DONE_DIR/$TASK_ID" "$TASKS_TODO_DIR/$TASK_ID"5. Wysyła Webhook
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
{
"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
{
"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
| Priority | Behavior | Kiedy Użyć |
|---|---|---|
| urgent | Stop natychmiast, wykonaj przed wszystkim | Krytyczny bug, build broken, security issue |
| high | Dokończ current subtask, potem interrupt | Ważna weryfikacja przed kontynuowaniem |
| normal | Dokoń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
{
"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
# 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
fiExecute Interrupt Logic
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 ← completedPrzykł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_serviceHigh 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_apiNormal 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 P2User 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.
{
"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.
{
"command_type": "deploy",
"description": "Deploy na dev6",
"params": {
"environment": "dev6",
"branch": "feature/DEV-7315"
}
}3. run_script
Wykonaj dowolny skrypt shell.
{
"command_type": "run_script",
"description": "Uruchom migracje",
"params": {
"script": "php artisan migrate --force"
}
}4. custom
Dowolna komenda AI.
{
"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}.jsonProcessing
Watchdog lub dedykowany workflow skanuje user_commands/ co 5 minut i automatycznie tworzy subtaski.
Error Handling
Reopen Errors
# 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
# 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
❌ Invalid JSON, skipping
→ Plik przeniesiony do .errorMissing Fields
❌ Missing required fields (command_type, task_id, message)
→ Plik przeniesiony do .errorDAGU Workflow Configuration
# 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_commandsMonitoring
Sprawdzanie Pending Commands
# Lista pending control commands
ls tasks/control_commands/
# Sprawdź konkretny command
cat tasks/control_commands/cmd_1234567890.json | jq '.'Sprawdzanie Pending Interrupts
# 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
# 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.errorBest 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 → normalChangelog
v2.0 - Manual Control
- ✅ Reopen functionality
- ✅ Interrupt with priority levels
- ✅ User commands support
v1.0 - Basic Workflow
- ✅ Automatyczny workflow bez manual control