From 0516f2febc86014f8e09aa2571c06111e25ecba9 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 26 Feb 2026 20:58:23 +0900 Subject: [PATCH] fix(todo-continuation): exclude blocked todos from incomplete count to prevent infinite loops Closes #2025 --- .../todo-continuation-enforcer.test.ts | 26 ++++++++++++++++++- src/hooks/todo-continuation-enforcer/todo.ts | 8 +++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/hooks/todo-continuation-enforcer/todo-continuation-enforcer.test.ts b/src/hooks/todo-continuation-enforcer/todo-continuation-enforcer.test.ts index 19d2222f..f8e7be07 100644 --- a/src/hooks/todo-continuation-enforcer/todo-continuation-enforcer.test.ts +++ b/src/hooks/todo-continuation-enforcer/todo-continuation-enforcer.test.ts @@ -297,6 +297,31 @@ describe("todo-continuation-enforcer", () => { expect(promptCalls).toHaveLength(0) }) + test("should not inject when remaining todos are blocked or deleted", async () => { + // given - session where non-completed todos are only blocked/deleted + const sessionID = "main-blocked-deleted" + setMainSession(sessionID) + + const mockInput = createMockPluginInput() + mockInput.client.session.todo = async () => ({ data: [ + { id: "1", content: "Blocked task", status: "blocked", priority: "high" }, + { id: "2", content: "Deleted task", status: "deleted", priority: "medium" }, + { id: "3", content: "Done task", status: "completed", priority: "low" }, + ]}) + + const hook = createTodoContinuationEnforcer(mockInput, {}) + + // when - session goes idle + await hook.handler({ + event: { type: "session.idle", properties: { sessionID } }, + }) + + await fakeTimers.advanceBy(3000) + + // then - no continuation injected + expect(promptCalls).toHaveLength(0) + }) + test("should not inject when background tasks are running", async () => { // given - session with running background tasks const sessionID = "main-789" @@ -1663,7 +1688,6 @@ describe("todo-continuation-enforcer", () => { test("should cancel all countdowns via cancelAllCountdowns", async () => { // given - multiple sessions with running countdowns const session1 = "main-cancel-all-1" - const session2 = "main-cancel-all-2" setMainSession(session1) const hook = createTodoContinuationEnforcer(createMockPluginInput(), {}) diff --git a/src/hooks/todo-continuation-enforcer/todo.ts b/src/hooks/todo-continuation-enforcer/todo.ts index dbc6f5b6..1847cb52 100644 --- a/src/hooks/todo-continuation-enforcer/todo.ts +++ b/src/hooks/todo-continuation-enforcer/todo.ts @@ -1,5 +1,11 @@ import type { Todo } from "./types" export function getIncompleteCount(todos: Todo[]): number { - return todos.filter((todo) => todo.status !== "completed" && todo.status !== "cancelled").length + return todos.filter( + (todo) => + todo.status !== "completed" + && todo.status !== "cancelled" + && todo.status !== "blocked" + && todo.status !== "deleted", + ).length }