{"record":{"id":"d0334f23bb89e3a8","repo":"shareAI-lab/learn-claude-code","slug":"only-one-todo-can-be-in-progress-at-a-time","errorCode":null,"errorMessage":"Only one todo can be in_progress at a time","messagePattern":"Only one todo can be in_progress at a time","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"warning","filePath":"s05_todo_write/code.py","lineNumber":146,"sourceCode":"\n        validated = []\n        in_progress_count = 0\n        for index, todo in enumerate(todos):\n            if not isinstance(todo, dict):\n                raise ValueError(f\"todos[{index}] must be an object\")\n\n            content = str(todo.get(\"content\", \"\")).strip()\n            status = str(todo.get(\"status\", \"pending\")).lower()\n            if not content:\n                raise ValueError(f\"todos[{index}] requires content\")\n            if status not in (\"pending\", \"in_progress\", \"completed\"):\n                raise ValueError(f\"todos[{index}] has invalid status '{status}'\")\n            if status == \"in_progress\":\n                in_progress_count += 1\n            validated.append({\"content\": content, \"status\": status})\n\n        if in_progress_count > 1:\n            raise ValueError(\"Only one todo can be in_progress at a time\")\n\n        self.items = validated\n        return self.render()\n\n    def render(self) -> str:\n        if not self.items:\n            return \"No todos.\"\n\n        lines = []\n        for todo in self.items:\n            marker = {\n                \"pending\": \"[ ]\",\n                \"in_progress\": \"[>]\",\n                \"completed\": \"[x]\",\n            }[todo[\"status\"]]\n            lines.append(f\"{marker} {todo['content']}\")\n\n        done = sum(todo[\"status\"] == \"completed\" for todo in self.items)","sourceCodeStart":128,"sourceCodeEnd":164,"githubUrl":"https://github.com/shareAI-lab/learn-claude-code/blob/985456f4adea6f4df8fbad4112245dbd97444eae/s05_todo_write/code.py#L128-L164","documentation":"Raised by TodoManager.update in s05_todo_write/code.py when more than one element has status in_progress after per-item validation. The module enforces a single-active-item invariant so the rendered todo list has exactly one current focus. The count is tallied across the whole submitted list (which replaces, not merges with, existing items), so the error reflects the new list alone.","triggerScenarios":"Submitting [{...\"in_progress\"}, {...\"in_progress\"}] — e.g. marking a second item active without first demoting the previous one to pending/completed in the same replacement list.","commonSituations":"Agents starting a new step while forgetting to close the previous one; parallel-work plans where the model wants two active items; appending an in_progress item to a list that already contains one.","solutions":["Before marking item B in_progress, set item A to completed (or pending) in the same submitted list","Keep a helper that enforces exactly one in_progress before calling update","Treat the invariant as intentional: structure plans as a serial focus, not parallel tracks"],"exampleFix":"// before\n[{\"content\": \"a\", \"status\": \"in_progress\"}, {\"content\": \"b\", \"status\": \"in_progress\"}]\n// after\n[{\"content\": \"a\", \"status\": \"completed\"}, {\"content\": \"b\", \"status\": \"in_progress\"}]","handlingStrategy":"validation","validationCode":"def enforce_single_active(todos):\n    seen = False\n    out = []\n    for t in todos:\n        s = str(t.get('status', 'pending')).lower()\n        if s == 'in_progress':\n            s = 'completed' if seen else 'in_progress'\n            seen = True\n        out.append({**t, 'status': s})\n    return out\n\nTODOS.update(enforce_single_active(todos))","typeGuard":"def single_in_progress(todos: list) -> bool:\n    return sum(1 for t in todos if str(t.get('status', '')).lower() == 'in_progress') <= 1","tryCatchPattern":null,"preventionTips":["Demote the previous active item in the same replacement list","Check the invariant client-side before update","Model work as a serial focus, one active step at a time"],"tags":["todos","state-invariant","validation"],"backgroundTag":null,"analyzedSha":"985456f4adea6f4df8fbad4112245dbd97444eae","analyzedAt":"2026-08-14T22:02:26.028Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}