Agent to-do list with task dependencies
Build the task tracker an AI agent uses to plan its work. The question comes in three parts. Each part builds on the one before, so keep earlier behavior working.
Part 1: tasks and statuses
A status is an IntEnum in lifecycle order:
| Status | Value |
|---|---|
BLOCKED |
1 |
READY_TO_EXECUTE |
2 |
IN_PROGRESS |
3 |
SUCCEEDED |
4 (terminal) |
FAILED |
5 (terminal) |
A Task has task_id: int, description: str and status. Implement TodoList:
-
add_task(description) -> Taskcreates a task with a new integer ID. IDs increase with each call. The task startsREADY_TO_EXECUTE. -
get_task(task_id) -> Task | Nonereturns the task, orNonefor an unknown ID. -
change_task_status(task_id, new_status) -> boolsets the status and returnsTrue. It returnsFalseand changes nothing when the ID is unknown, the task is already terminal, ornew_statusis not strictly greater than the current status. Statuses only move forward.
Part 2: dependencies
add_task(description, dependency_ids) takes a list of IDs of tasks that already exist (so the graph has no cycles).
- A new task is
READY_TO_EXECUTEwhen it has no dependencies or all of them haveSUCCEEDED, andBLOCKEDotherwise. If one of its dependencies has alreadyFAILED, it startsFAILED. - After every successful
change_task_status:- a
BLOCKEDtask whose dependencies have allSUCCEEDEDbecomesREADY_TO_EXECUTE; - a
BLOCKEDtask that depends, directly or through a chain, on a task that justFAILEDbecomesFAILED.
- a
- Do not rescan the whole list on each change. Touch only the tasks the change can affect.
Part 3: render for an LLM
render_for_llm() -> str returns the full list (including finished tasks) in a format a language model can parse reliably. Show each task's ID, status, description and dependencies. Choose and justify the format.
Example
t = TodoList()
t.add_task("download dataset") # 1, READY_TO_EXECUTE
t.add_task("clean rows", [1]) # 2, BLOCKED
t.add_task("train model", [1, 2]) # 3, BLOCKED
t.change_task_status(1, IN_PROGRESS) # True
t.change_task_status(1, READY_TO_EXECUTE) # False: backward
t.change_task_status(1, SUCCEEDED) # True; task 2 -> READY_TO_EXECUTE, task 3 stays BLOCKED
t.change_task_status(2, FAILED) # True; task 3 -> FAILED
t.add_task("write report", [3]) # 4, FAILED (a dependency already failed)
Constraints: up to 10^5 tasks and 2 * 10^5 dependency references in total.
Sign in to see the worked answer
The full answer, chapter by chapter, with what interviewers expect at each level. Free with an account: one full answer per company.
