Skip to content

buzz-acp ignores thread_replies — new-thread instruction always forces --reply-to #4699

Description

@lazy-geeek

Summary

thread_replies from a persona pack has no effect. An agent configured with
thread_replies: false still opens a new thread for every top-level channel message,
because append_new_thread_reply_instruction() unconditionally appends an IMPORTANT:
directive telling the model to use --reply-to <event_id>.

This is the same class of gap as #2899 (mcp_servers parsed and validated but never
plumbed to the runtime).

Why this should work

PERSONA_PACK_SPEC.md §10 states the behavioral config fields are the harness's job:

The behavioral config fields in a persona's frontmatter control how the agent
participates in Buzz conversations. These are all Buzz-specific — the agent runtime
has no awareness of them.

And thread_replies is documented as:

| thread_replies | bool | true | true / false | Reply in-thread when the
triggering message is in a thread. |

Since the agent runtime is explicitly not aware of these fields, buzz-acp is the only
component that can honour them.

Actual behaviour

thread_replies does not appear anywhere in crates/buzz-acp. It exists only in
crates/buzz-persona (parse, merge, resolve, manifest) and crates/buzz-db. It is
parsed, merged and validated — buzz pack inspect even prints Thread replies: false
and then dropped.

The prompt builder in crates/buzz-acp/src/queue.rs takes the else branch for a
top-level channel message and always calls:

fn append_new_thread_reply_instruction(s: &mut String, event_id: &str) {
    s.push_str(&format!(
        "\nIMPORTANT: This is a new top-level message. For ordinary replies in \
         this turn, use `--reply-to {event_id}` on `buzz messages send` — the \
         triggering message is the thread root. Do NOT reply into any other \
         (older) thread. ..."
    ));
}

There is no configuration path that suppresses this.

Why prompting cannot fix it

We tried. Both the persona system prompt and a patched base prompt instruct the agent to
reply flat in the channel and to ignore any reply target suggested in [Context]. The
model still threads, and inconsistently so — flat at 13:03, threaded at 14:28 on the same
kind of question.

The reason is prompt position and specificity. The harness directive is marked
IMPORTANT:, carries the concrete event ID, and sits at the very end of the prompt; the
persona rule is abstract and much earlier. The concrete, closer instruction wins. Verified
in the agent's own request logs — the model received the directive and complied exactly:

buzz messages send --channel 29c37e15-… --reply-to cb710fe8… --content -

Reproduce

  1. Persona pack with thread_replies: false; confirm with buzz pack inspect.
  2. Run buzz-acp (any agent command) subscribed to a channel.
  3. Post a new top-level message in that channel — not inside a thread.
  4. The reply is threaded under the triggering message.

Observed on 3d7712cc36e8da563cb1c121fc58bfc505d38496, still present on main at time of
writing.

Suggested fix

Gate the call site on the resolved thread_replies value. The split is already in the
right place — append_reply_instruction() (replying inside an existing thread) is
correct as-is and should keep working; only the new-thread branch needs to respect the
flag.

Note that the unit tests in queue.rs assert on the current wording
("new top-level message", --reply-to <id>), so they would need a thread_replies: false counterpart.

Workaround

Patching append_new_thread_reply_instruction() at build time to emit the opposite
instruction. Works, but it means carrying a source patch against a pinned upstream commit.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions