diff --git a/core/models.py b/core/models.py index fe85d36..ba13f18 100644 --- a/core/models.py +++ b/core/models.py @@ -290,13 +290,11 @@ def has_open_children(conn: sqlite3.Connection, task_id: str, visited: set[str] return False -def _check_parent_completion(conn: sqlite3.Connection, task_id: str, visited: set[str] | None = None) -> None: - """Cascade-check upward: if parent is 'revising' and all children closed → promote to 'done'.""" - if visited is None: - visited = set() +def _do_cascade(conn: sqlite3.Connection, task_id: str, visited: set[str]) -> None: + """Recursive upward cascade without transaction management — no commits.""" if task_id in visited: return - visited = visited | {task_id} + visited.add(task_id) task = get_task(conn, task_id) if not task: return @@ -314,8 +312,19 @@ def _check_parent_completion(conn: sqlite3.Connection, task_id: str, visited: se "UPDATE tasks SET status = 'done', completed_at = ?, updated_at = ? WHERE id = ?", (now, now, parent_id), ) + _do_cascade(conn, parent_id, visited) + + +def _check_parent_completion(conn: sqlite3.Connection, task_id: str, visited: set[str] | None = None) -> None: + """Cascade-check upward: promote all ready parents to 'done' in one atomic transaction.""" + if visited is None: + visited = set() + try: + _do_cascade(conn, task_id, visited) conn.commit() - _check_parent_completion(conn, parent_id, visited) + except Exception: + conn.rollback() + raise VALID_TASK_SORT_FIELDS = frozenset({