Simplify task graph to single-source blockedBy (PR #127)

Remove unused blocks field and add_blocks parameter. The LLM never
used add_blocks in practice, making it dead code that taught a
misleading bidirectional pattern. Replace with remove_blocked_by
for dependency rewiring. Single-source-of-truth with blockedBy only.
This commit is contained in:
CrazyBoyM
2026-03-29 23:56:39 +08:00
parent 5c7b873d3b
commit 5f2b0f205e
5 changed files with 38 additions and 45 deletions

View File

@@ -4,12 +4,12 @@
s07_task_system.py - Tasks
Tasks persist as JSON files in .tasks/ so they survive context compression.
Each task has a dependency graph (blockedBy/blocks).
Each task has a dependency graph (blockedBy).
.tasks/
task_1.json {"id":1, "subject":"...", "status":"completed", ...}
task_2.json {"id":2, "blockedBy":[1], "status":"pending", ...}
task_3.json {"id":3, "blockedBy":[2], "blocks":[], ...}
task_3.json {"id":3, "blockedBy":[2], ...}
Dependency resolution:
+----------+ +----------+ +----------+
@@ -67,7 +67,7 @@ class TaskManager:
def create(self, subject: str, description: str = "") -> str:
task = {
"id": self._next_id, "subject": subject, "description": description,
"status": "pending", "blockedBy": [], "blocks": [], "owner": "",
"status": "pending", "blockedBy": [], "owner": "",
}
self._save(task)
self._next_id += 1
@@ -77,37 +77,18 @@ class TaskManager:
return json.dumps(self._load(task_id), indent=2, ensure_ascii=False)
def update(self, task_id: int, status: str = None,
add_blocked_by: list = None, add_blocks: list = None) -> str:
add_blocked_by: list = None, remove_blocked_by: list = None) -> str:
task = self._load(task_id)
if status:
if status not in ("pending", "in_progress", "completed"):
raise ValueError(f"Invalid status: {status}")
task["status"] = status
# When a task is completed, remove it from all other tasks' blockedBy
if status == "completed":
self._clear_dependency(task_id)
if add_blocked_by:
task["blockedBy"] = list(set(task["blockedBy"] + add_blocked_by))
# Bidirectional: also update the blocker tasks' blocks lists
for blocker_id in add_blocked_by:
try:
blocker = self._load(blocker_id)
if task_id not in blocker["blocks"]:
blocker["blocks"].append(task_id)
self._save(blocker)
except ValueError:
pass
if add_blocks:
task["blocks"] = list(set(task["blocks"] + add_blocks))
# Bidirectional: also update the blocked tasks' blockedBy lists
for blocked_id in add_blocks:
try:
blocked = self._load(blocked_id)
if task_id not in blocked["blockedBy"]:
blocked["blockedBy"].append(task_id)
self._save(blocked)
except ValueError:
pass
if remove_blocked_by:
task["blockedBy"] = [x for x in task["blockedBy"] if x not in remove_blocked_by]
self._save(task)
return json.dumps(task, indent=2, ensure_ascii=False)
@@ -195,7 +176,7 @@ TOOL_HANDLERS = {
"write_file": lambda **kw: run_write(kw["path"], kw["content"]),
"edit_file": lambda **kw: run_edit(kw["path"], kw["old_text"], kw["new_text"]),
"task_create": lambda **kw: TASKS.create(kw["subject"], kw.get("description", "")),
"task_update": lambda **kw: TASKS.update(kw["task_id"], kw.get("status"), kw.get("addBlockedBy"), kw.get("addBlocks")),
"task_update": lambda **kw: TASKS.update(kw["task_id"], kw.get("status"), kw.get("addBlockedBy"), kw.get("removeBlockedBy")),
"task_list": lambda **kw: TASKS.list_all(),
"task_get": lambda **kw: TASKS.get(kw["task_id"]),
}
@@ -212,7 +193,7 @@ TOOLS = [
{"name": "task_create", "description": "Create a new task.",
"input_schema": {"type": "object", "properties": {"subject": {"type": "string"}, "description": {"type": "string"}}, "required": ["subject"]}},
{"name": "task_update", "description": "Update a task's status or dependencies.",
"input_schema": {"type": "object", "properties": {"task_id": {"type": "integer"}, "status": {"type": "string", "enum": ["pending", "in_progress", "completed"]}, "addBlockedBy": {"type": "array", "items": {"type": "integer"}}, "addBlocks": {"type": "array", "items": {"type": "integer"}}}, "required": ["task_id"]}},
"input_schema": {"type": "object", "properties": {"task_id": {"type": "integer"}, "status": {"type": "string", "enum": ["pending", "in_progress", "completed"]}, "addBlockedBy": {"type": "array", "items": {"type": "integer"}}, "removeBlockedBy": {"type": "array", "items": {"type": "integer"}}}, "required": ["task_id"]}},
{"name": "task_list", "description": "List all tasks with status summary.",
"input_schema": {"type": "object", "properties": {}}},
{"name": "task_get", "description": "Get full details of a task by ID.",

View File

@@ -277,7 +277,7 @@ class TaskManager:
def create(self, subject: str, description: str = "") -> str:
task = {"id": self._next_id(), "subject": subject, "description": description,
"status": "pending", "owner": None, "blockedBy": [], "blocks": []}
"status": "pending", "owner": None, "blockedBy": []}
self._save(task)
return json.dumps(task, indent=2)
@@ -285,7 +285,7 @@ class TaskManager:
return json.dumps(self._load(tid), indent=2)
def update(self, tid: int, status: str = None,
add_blocked_by: list = None, add_blocks: list = None) -> str:
add_blocked_by: list = None, remove_blocked_by: list = None) -> str:
task = self._load(tid)
if status:
task["status"] = status
@@ -300,8 +300,8 @@ class TaskManager:
return f"Task {tid} deleted"
if add_blocked_by:
task["blockedBy"] = list(set(task["blockedBy"] + add_blocked_by))
if add_blocks:
task["blocks"] = list(set(task["blocks"] + add_blocks))
if remove_blocked_by:
task["blockedBy"] = [x for x in task["blockedBy"] if x not in remove_blocked_by]
self._save(task)
return json.dumps(task, indent=2)
@@ -587,7 +587,7 @@ TOOL_HANDLERS = {
"check_background": lambda **kw: BG.check(kw.get("task_id")),
"task_create": lambda **kw: TASK_MGR.create(kw["subject"], kw.get("description", "")),
"task_get": lambda **kw: TASK_MGR.get(kw["task_id"]),
"task_update": lambda **kw: TASK_MGR.update(kw["task_id"], kw.get("status"), kw.get("add_blocked_by"), kw.get("add_blocks")),
"task_update": lambda **kw: TASK_MGR.update(kw["task_id"], kw.get("status"), kw.get("add_blocked_by"), kw.get("remove_blocked_by")),
"task_list": lambda **kw: TASK_MGR.list_all(),
"spawn_teammate": lambda **kw: TEAM.spawn(kw["name"], kw["role"], kw["prompt"]),
"list_teammates": lambda **kw: TEAM.list_all(),
@@ -626,7 +626,7 @@ TOOLS = [
{"name": "task_get", "description": "Get task details by ID.",
"input_schema": {"type": "object", "properties": {"task_id": {"type": "integer"}}, "required": ["task_id"]}},
{"name": "task_update", "description": "Update task status or dependencies.",
"input_schema": {"type": "object", "properties": {"task_id": {"type": "integer"}, "status": {"type": "string", "enum": ["pending", "in_progress", "completed", "deleted"]}, "add_blocked_by": {"type": "array", "items": {"type": "integer"}}, "add_blocks": {"type": "array", "items": {"type": "integer"}}}, "required": ["task_id"]}},
"input_schema": {"type": "object", "properties": {"task_id": {"type": "integer"}, "status": {"type": "string", "enum": ["pending", "in_progress", "completed", "deleted"]}, "add_blocked_by": {"type": "array", "items": {"type": "integer"}}, "remove_blocked_by": {"type": "array", "items": {"type": "integer"}}}, "required": ["task_id"]}},
{"name": "task_list", "description": "List all tasks.",
"input_schema": {"type": "object", "properties": {}}},
{"name": "spawn_teammate", "description": "Spawn a persistent autonomous teammate.",