core / planning.py
Hierarchical task tree with dependency types, postcondition gating, alternatives, and revision tracking.
Data model
| Type | Variants / fields |
|---|---|
TaskStatus enum (line 9) | PENDING · READY · IN_PROGRESS · DONE · FAILED · BLOCKED · PAUSED · NEEDS_USER |
DependencyType enum (line 24) | ALL — every child must succeed · ANY — first success satisfies parent · BEST — run all, pick best |
TaskNode (line 32) | id, description, status, parent_id, children, dependency_type, alternatives, postconditions, estimated_cost, actual_cost, revision_count, result_summary, failure_reason, actual_tool_used, depth, position |
Methods
| Method | Effect |
|---|---|
add_task(description, parent_id, status, dependency_type, alternatives, postconditions) | Insert task, return id (line 60). |
update_status(task_id, status, result, failure_reason, actual_tool) | Mark transition; trigger postcondition check + parent propagation (line 89). |
_check_postconditions(node) | ≥60 % token-overlap heuristic between result_summary and each postcondition; optional custom checker callback (line 120). |
_check_parent_completion(parent_id) | ALL/ANY/BEST aggregation propagated upward (line 165). |
_check_parent_failure(parent_id) | Spawns alternatives, propagates BLOCKED/FAILED (line 216). |
get_active_node() | Next leaf in IN_PROGRESS or READY (line 267). |
render() | ASCII tree with emoji status icons (line 292). |
request_revision(task_id, failure_reason) | Increment revision_count, reset to PENDING if <3 (line 394). |
load_from_json(json_data) | Stateful merge (no clear) so concurrent edits survive. |
Example tree
Figure 4 — Hierarchical decomposition with dependencies, alternatives, and propagation.
Postcondition matching
Token overlap ≥ 60 % case-insensitive, with stop words filtered ({the, and, for, not, are, has}). A postcondition prefixed with HUMAN_GATE: is special-cased by project_safety to flip the task to NEEDS_USER regardless of tool output.
ProjectPlan
Same shape as TaskTree but persisted via memory.projects's SQLite store. ProjectPlan.update_status() wraps TaskTree.update_status() with a write-through to the database.
Concurrency
Synchronous tree mutation. Persistence happens via the ProjectStore RLock; in-memory TaskTree is per-request only.