diff --git a/src/Conclave.App/Claude/ClaudeService.cs b/src/Conclave.App/Claude/ClaudeService.cs index c9fdfa6..b17596f 100644 --- a/src/Conclave.App/Claude/ClaudeService.cs +++ b/src/Conclave.App/Claude/ClaudeService.cs @@ -364,6 +364,16 @@ private void AppendAssistantMessage( break; case ToolUseContent tu: hasToolUse = true; + // TodoWrite is mirrored into the right-hand plan panel — rendering a + // pill in the transcript too is just noise, so update the plan and skip. + if (tu.Name == "TodoWrite") + { + UpdatePlanFromTodoWrite(session, tu.InputJson); + break; + } + // Task (subagent) calls don't carry useful per-pill state — the agent's + // summary lands in the next assistant message anyway. Skip the pill. + if (tu.Name == "Task") break; var vm = new ToolCallVm { Tokens = session.Tokens, @@ -380,7 +390,6 @@ private void AppendAssistantMessage( // permission_prompt for this tool_use_id, the handler knows which // pill to flip to PendingApproval. permHandler?.NoteToolUse(tu.Id, vm); - if (tu.Name == "TodoWrite") UpdatePlanFromTodoWrite(session, tu.InputJson); // AskUserQuestion + ExitPlanMode are interactive — claude is waiting on // the user. EnterPlanMode is just claude announcing it switched modes, // not a question, so it stays out of this branch. Fire both a native diff --git a/src/Conclave.App/Sessions/SessionManager.cs b/src/Conclave.App/Sessions/SessionManager.cs index fdd1204..a95e11f 100644 --- a/src/Conclave.App/Sessions/SessionManager.cs +++ b/src/Conclave.App/Sessions/SessionManager.cs @@ -406,6 +406,9 @@ private IEnumerable DeserializeTools(string json) ?? Array.Empty(); foreach (var r in rows) { + // Older sessions persisted TodoWrite/Task pills before we started hiding them — + // drop them on load so historical transcripts match the current rendering rules. + if (r.Kind is "TODO" or "TASK") continue; yield return new ToolCallVm { Tokens = _tokens,