class ToDoList:
Perplexity's "to-do list for AI" coding round, from two first-hand phone-screen reports (November 2025 and April 2026; one says the role was SWE). Both candidates were rejected the next day. Both say the problem has four parts, and both ran out of time after the second. A reply in the first thread adds another candidate who reached the third part.
The round reads like an OA: a long instruction sheet, and each part comes with its own test cases in a Python file you can read. One report says every part after the first has 6–7 test cases, and that each part includes a test that fails if earlier behaviour regresses, so change earlier code carefully. Python was required in one report. One interviewer started the problem straight away with no small talk; another, three months into the job, gave little help. One poster wondered who could finish all four parts in an hour; another candidate reached only the first two in 45 minutes.
Given — three classes
The reports name the classes TaskStatus (an Enum), Task and ToDoList, the statuses READY, BLOCKED, SUCCEEDED and FAILED, and the Part 1 methods add, get and update status. No starting code was captured, so the members and signatures below are this page's.
What the tests call (our setup — ask your interviewer)
class TaskStatus(Enum):
READY = "READY"
BLOCKED = "BLOCKED"
SUCCEEDED = "SUCCEEDED"
FAILED = "FAILED"
class Task:
id: str
status: TaskStatus
class ToDoList:
def __init__(self): ...
def add(self, task_id: str, dependencies: list[str] | None = None) -> Task: ...
def get(self, task_id: str) -> Task: ...
def update_status(self, task_id: str, new_status: TaskStatus) -> None: ...
def serialize(self) -> str: ... # Part 3
The tests read a task's id and status through get, and accept a status stored either as the enum member or as its string value. Every dependency in the tests is a task added earlier, so there are no unknown ids and no cycles.
Part 1 — Add, get, update status
Create a task, fetch it by id, and set its status. One candidate found this part not hard and advised getting through it fast. The tests never read a status before setting one, so the starting status in this part is yours to choose.
Not reported — the fourth part
No candidate in the reports reached the fourth part, so its content is unknown.
Two first-hand phone-screen reports, from November 2025 and April 2026, both rejected the next day; one says the role was SWE. Both describe four parts, each with its own test cases, and both candidates ran out of time after Part 2. The first finished Part 2 with two tests still failing. In the second, one very large test timed out the candidate's DFS cascade and stayed unfixed. A reply in the first thread adds a candidate who reached Part 3, printed the tasks in topological order, and ran out of time before the fourth part. One commenter felt the team was trying out a new question, so the interviewer could not help much.
Our practical read: Part 1 is quick and Part 2 is where the time goes. Decide both edge directions before you write add, make the cascade visit each task once, and re-run the earlier tests after every change.
Perplexity's "to-do list for AI" coding round, from two first-hand phone-screen reports (November 2025 and April 2026; one says the role was SWE). Both candidates were rejected the next day. Both say the problem has four parts, and both ran out of time after the second. A reply in the first thread adds another candidate who reached the third part.
The round reads like an OA: a long instruction sheet, and each part comes with its own test cases in a Python file you can read. One report says every part after the first has 6–7 test cases, and that each part includes a test that fails if earlier behaviour regresses, so change earlier code carefully. Python was required in one report. One interviewer started the problem straight away with no small talk; another, three months into the job, gave little help. One poster wondered who could finish all four parts in an hour; another candidate reached only the first two in 45 minutes.
Given — three classes
The reports name the classes TaskStatus (an Enum), Task and ToDoList, the statuses READY, BLOCKED, SUCCEEDED and FAILED, and the Part 1 methods add, get and update status. No starting code was captured, so the members and signatures below are this page's.
What the tests call (our setup — ask your interviewer)
class TaskStatus(Enum):
READY = "READY"
BLOCKED = "BLOCKED"
SUCCEEDED = "SUCCEEDED"
FAILED = "FAILED"
class Task:
id: str
status: TaskStatus
class ToDoList:
def __init__(self): ...
def add(self, task_id: str, dependencies: list[str] | None = None) -> Task: ...
def get(self, task_id: str) -> Task: ...
def update_status(self, task_id: str, new_status: TaskStatus) -> None: ...
def serialize(self) -> str: ... # Part 3
The tests read a task's id and status through get, and accept a status stored either as the enum member or as its string value. Every dependency in the tests is a task added earlier, so there are no unknown ids and no cycles.
Part 1 — Add, get, update status
Create a task, fetch it by id, and set its status. One candidate found this part not hard and advised getting through it fast. The tests never read a status before setting one, so the starting status in this part is yours to choose.
Part 2 — Dependencies and cascade
Each task now has dependencies: a list of the ids of tasks it depends on. Update add and update_status:
So a task has to track both directions: its parents (the tasks it depends on) and its children (the tasks that depend on it). One candidate tracked only children at first and had to go back and change it. One report words both update rules with "dependencies"; the other says the failure reaches every task that depends on the failed one, and the tests grade that.
The reported wall is one large test. One candidate's DFS cascade passed every test but one very large one, which timed out and was still unfixed when the interview ended. They could not work out how that test was built and guessed a few hundred interdependent tasks. Our reading, not reported: a DFS with no visited guard walks a shared task once for every path that reaches it, which grows exponentially with depth when many tasks share dependencies. Failing each task once, and skipping tasks already FAILED, touches every task and edge at most once.
Our large test (our setup) builds layered graphs, each layer's tasks depending on every task in the layer before, from 10 to 40 layers plus a 200-task chain, and fails the first task. Each size (build, fail, read back) must finish within 1 second. The test stops at the first size over that, so a slow cascade fails in a few seconds instead of freezing the editor.
Part 3 — Serialize for an LLM
Put every task into one string. Any format is fine; the aim is that an LLM can understand it. One candidate wrote a topological sort and printed each task in that order; the report does not say how it was graded. Here serialize() returns the string, and printing it instead also passes (our setup). The tests check that every task id appears in the output and that serializing changes nothing: statuses stay the same, and later successes and failures still spread as before.
Not reported — the fourth part
No candidate in the reports reached the fourth part, so its content is unknown.
Interviewer hints: One interviewer started the problem straight away, with no small talk. Another, three months into the job, gave little help; one commenter felt the team was trying out a new question, so the interviewer could not help much.
What candidates did: Both counted candidates were rejected the next day. One finished Part 2 with two tests still failing. The other's DFS cascade passed every test but one very large one, which timed out. Another candidate, in a reply, printed the tasks in topological order for Part 3 and ran out of time before the fourth part; the report does not say how it was graded.
Common mistakes: tracking only one edge direction (one candidate tracked only children and had to go back); a cascade that revisits shared tasks, which is our reading of the reported timeout (its cause was not reported); promoting a task to READY when only some of its dependencies have succeeded (our analysis). Fix (ours): fail each task once and skip tasks already FAILED, so the cascade is O(V+E).