Add task IDs (T-1, T-2, ...) for tracking and debugging

Every PrimaryTask and SubTask gets a unique auto-incrementing ID.
All log messages now include the task ID:

  T-1 NEW: Following GavRocket (stack depth: 1)
  T-2 INTERRUPT: Fighting spider
  T-2 COMPLETE: Fighting spider
  T-1 RESUMED: Following GavRocket
  T-3 NEW: Checking inventory (stack depth: 2)
  T-1 PAUSED: Following GavRocket
  T-3 COMPLETE: Checking inventory
  T-1 RESUMED: Following GavRocket

Makes it easy to track which task is which and debug the stack.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
roberts 2026-03-30 20:49:20 -05:00
parent 7363e8589f
commit 1872d0b89a
2 changed files with 16 additions and 9 deletions

View file

@ -416,7 +416,8 @@ class DougBrain(QObject):
if subtask.description: if subtask.description:
current = self._tasks.current_task current = self._tasks.current_task
priority_name = current.priority.name if current else "?" priority_name = current.priority.name if current else "?"
log.info(f"[{priority_name}] {subtask.description}") tid = current.task_id if current else 0
log.info(f"T-{tid} [{priority_name}] {subtask.description}")
self._action_sent_time = time.time() self._action_sent_time = time.time()

View file

@ -15,11 +15,15 @@ import time
from dataclasses import dataclass, field from dataclasses import dataclass, field
from enum import IntEnum from enum import IntEnum
from typing import Any, Optional from typing import Any, Optional
import itertools
from dougbot.utils.logging import get_logger from dougbot.utils.logging import get_logger
log = get_logger("core.tasks") log = get_logger("core.tasks")
# Global task ID counter
_task_counter = itertools.count(1)
class Priority(IntEnum): class Priority(IntEnum):
"""Task priority levels. Higher = more urgent.""" """Task priority levels. Higher = more urgent."""
@ -51,6 +55,7 @@ class SubTask:
status: TaskStatus = TaskStatus.PENDING status: TaskStatus = TaskStatus.PENDING
timeout: float = 30.0 timeout: float = 30.0
started_at: float = 0.0 started_at: float = 0.0
task_id: int = field(default_factory=lambda: next(_task_counter))
@property @property
def is_expired(self) -> bool: def is_expired(self) -> bool:
@ -67,6 +72,7 @@ class PrimaryTask:
""" """
name: str name: str
priority: Priority priority: Priority
task_id: int = field(default_factory=lambda: next(_task_counter))
description: str = "" description: str = ""
source: str = "self" # "player" or "self" source: str = "self" # "player" or "self"
source_player: str = "" # Who gave the command source_player: str = "" # Who gave the command
@ -141,7 +147,7 @@ class PrimaryTask:
def __str__(self): def __str__(self):
st = self.current_subtask() st = self.current_subtask()
step_info = f"{st.description}" if st else "" step_info = f"{st.description}" if st else ""
return f"[{self.priority.name}] {self.description}{step_info}" return f"T-{self.task_id} [{self.priority.name}] {self.description}{step_info}"
class TaskStack: class TaskStack:
@ -204,7 +210,7 @@ class TaskStack:
# Pause the current top task # Pause the current top task
if self._stack and self._stack[-1].status == TaskStatus.ACTIVE: if self._stack and self._stack[-1].status == TaskStatus.ACTIVE:
self._stack[-1].status = TaskStatus.PAUSED self._stack[-1].status = TaskStatus.PAUSED
log.info(f"Pausing: {self._stack[-1].description}") log.info(f"T-{self._stack[-1].task_id} PAUSED: {self._stack[-1].description}")
# Push new task # Push new task
task.status = TaskStatus.ACTIVE task.status = TaskStatus.ACTIVE
@ -215,7 +221,7 @@ class TaskStack:
while len(self._stack) > self.MAX_DEPTH: while len(self._stack) > self.MAX_DEPTH:
self._stack.pop(0) self._stack.pop(0)
log.info(f"New task: {task.description} (stack depth: {len(self._stack)})") log.info(f"T-{task.task_id} NEW: {task.description} (stack depth: {len(self._stack)})")
return True return True
def interrupt(self, task: PrimaryTask): def interrupt(self, task: PrimaryTask):
@ -226,7 +232,7 @@ class TaskStack:
self._interruption = task self._interruption = task
task.status = TaskStatus.ACTIVE task.status = TaskStatus.ACTIVE
task.started_at = time.time() task.started_at = time.time()
log.info(f"Interrupt: {task.description}") log.info(f"T-{task.task_id} INTERRUPT: {task.description}")
def complete_interruption(self): def complete_interruption(self):
"""Clear the current interruption, resume stack.""" """Clear the current interruption, resume stack."""
@ -258,12 +264,12 @@ class TaskStack:
self._completed_names.append(top.name) self._completed_names.append(top.name)
if len(self._completed_names) > 20: if len(self._completed_names) > 20:
self._completed_names.pop(0) self._completed_names.pop(0)
log.info(f"Task complete: {top.description}") log.info(f"T-{top.task_id} COMPLETE: {top.description}")
# Resume the task below if any # Resume the task below if any
if self._stack: if self._stack:
self._stack[-1].status = TaskStatus.ACTIVE self._stack[-1].status = TaskStatus.ACTIVE
log.info(f"Resuming: {self._stack[-1].description}") log.info(f"T-{self._stack[-1].task_id} RESUMED: {self._stack[-1].description}")
def fail_current(self): def fail_current(self):
"""Fail current subtask but don't kill the primary task.""" """Fail current subtask but don't kill the primary task."""
@ -296,10 +302,10 @@ class TaskStack:
if self._stack: if self._stack:
removed = self._stack.pop() removed = self._stack.pop()
log.info(f"Task cancelled: {removed.description}") log.info(f"T-{removed.task_id} CANCELLED: {removed.description}")
if self._stack: if self._stack:
self._stack[-1].status = TaskStatus.ACTIVE self._stack[-1].status = TaskStatus.ACTIVE
log.info(f"Resuming: {self._stack[-1].description}") log.info(f"T-{self._stack[-1].task_id} RESUMED: {self._stack[-1].description}")
def cancel_all(self): def cancel_all(self):
"""Clear everything.""" """Clear everything."""