Skip to content

Background <task-notification> entries are rendered as User messages #99

@willirath

Description

@willirath

When Claude Code runs an agent or shell command in the background (run_in_background=true), the eventual completion arrives in the JSONL as a type: user entry whose message.content is a string beginning with <task-notification> — not the usual list of tool_result content blocks. The transcript renderer treats these as ordinary user prompts (blue "User" panel), which makes long sessions look like the human kept typing system-generated XML at random intervals.

In a recent session of mine this affected 7 entries: 1 background Agent completion, 3 Monitor events, and 3 background Bash completions.

Minimal repro JSONL (4 lines — last entry is the bug):

{"type":"assistant","timestamp":"2026-01-01T10:00:00.000Z","message":{"role":"assistant","content":[{"type":"tool_use","id":"toolu_AAA","name":"Bash","input":{"command":"sleep 30","run_in_background":true,"description":"Long task"}}]}}
{"type":"user","timestamp":"2026-01-01T10:00:00.500Z","message":{"role":"user","content":[{"type":"tool_result","tool_use_id":"toolu_AAA","content":"Async bash launched. id=bash_AAA"}]}}
{"type":"assistant","timestamp":"2026-01-01T10:00:01.000Z","message":{"role":"assistant","content":[{"type":"text","text":"Started; continuing while it runs."}]}}
{"type":"user","timestamp":"2026-01-01T10:00:31.000Z","message":{"role":"user","content":"<task-notification>\n<task-id>bash_AAA</task-id>\n<tool-use-id>toolu_AAA</tool-use-id>\n<status>completed</status>\n<summary>Background command \"Long task\" completed (exit code 0)</summary>\n</task-notification>"}}

Save as repro.jsonl and run:

claude-code-transcripts json repro.jsonl -o /tmp/out

Observed: the 4th entry renders as

<div class="message user" id="msg-..."><span class="role-label">User</span> ...
  <p><task-notification> ... </task-notification></p>

i.e. a normal blue user-prompt bubble.

Expected: classified as a system / tool-reply panel (e.g. orange tool-reply), since the human did not author it. Ideally the renderer would parse out <status> and <summary> (and <result> when present) for a compact display.

Suggested detection: any type: "user" entry where message.content is a string starting with <task-notification>. (These are also distinguishable by the absence of a tool_result content block and the presence of an <output-file> or <task-id> element inside.)

Environment: claude-code-transcripts (current uvx install), Claude Code generating the JSONL.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions