mpfaffenberger / mpfaffenberger/code_puppy

`inject_interrupted_subagent_notes` appends a synthetic user message, producing consecutive user turns

Open
#798 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
814
Forks
278
Avg merge
2d 5h
Merged PRs (30d)
76

Description

Summary

inject_interrupted_subagent_notes appends each [system note] as its own ModelRequest with a UserPromptPart. The user's next message then becomes a second consecutive user turn. With N interrupted sub-agents you get N+1 consecutive user messages.

The Anthropic Messages API requires strict user/assistant alternation, so whatever sits between the client and the API has to glue them together — meaning the model sees a different conversation structure than the one code_puppy built.

Evidence

Captured at the API boundary (roles and content-block types only, no content):

merged_messages=4  model=claude-opus-5  streaming=True
[... ('assistant', ['thinking','text','tool_use','tool_use']),
     ('user',      ['tool_result','tool_result']),
     ('user',      ['text']),      <-- injected note
     ('user',      ['text']),      <-- the user's actual message
     ('assistant', ['thinking','text']), ...]

Everything else in the request alternates cleanly. The clumping is exclusively on the user side and correlates 1:1 with interrupted sub-agent runs.

Root cause

code_puppy/agents/_run_signals.py:212 (in inject_interrupted_subagent_notes, :173):

injected.append(ModelRequest(parts=[UserPromptPart(content=note)]))
...
agent._message_history = list(agent._message_history) + injected   # :218

Each note becomes a standalone user-role message in the history, and nothing merges it with the turn that follows.

The codebase already has the correct pattern

prepare_queued_steer_injection (_run_signals.py:136-170) solves the same problem properly: it returns the steer content to be used as the next user turn instead of appending a synthetic message, and its docstring states the intent explicitly — "we deliberately process ONE per loop iteration to keep turn boundaries clean for the model."

The note injection is the one place that deviates.

Suggested fix

Carry the notes forward and prepend them to the next user prompt rather than appending separate ModelRequests. That removes the duplication entirely and has a second benefit: the note arrives in the same turn as the user's question instead of appearing as its own conversational move the model may answer.

Bundling all notes into a single ModelRequest reduces 4 consecutive messages to 2 — it does not fix the problem, because the user's real message still follows.

What is not the problem (checked, so you don't have to)

  • Parallel tool results are batched correctly — all tool_result blocks land in one user message.
  • The assistant side never clumps; every assistant message is followed by tool results or ends the turn.

Reproduction

  1. Invoke a sub-agent, interrupt it before it finishes (Ctrl+C).
  2. Send another message.
  3. Inspect the outgoing request: two consecutive user messages. Interrupt several sub-agents for larger runs.

Environment

code_puppy at 8e602eb9, Anthropic provider (claude-opus-5), streaming, Windows.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in code_puppy/agents/_run_signals.py, reading inject_interrupted_subagent_notes alongside prepare_queued_steer_injection. Reproduce the issue by interrupting a sub-agent and inspecting the outgoing request. Done means interrupted notes are carried into the next user prompt and consecutive user-role messages no longer appear.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
ai, api
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.