buzz-acp ignores `thread_replies` — new-thread instruction always forces `--reply-to`
- Dominant language
- Rust
- Stars
- 32.7k
- Forks
- 4.3k
- Avg merge
- 1d 13h
- Merged PRs (30d)
- 253
Description
## 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 `.
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:
```rust
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 `), 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.
Contributor guide
Research direction
Start in crates/buzz-acp/src/queue.rs, especially the top-level-message branch that calls append_new_thread_reply_instruction(), and trace how the resolved thread_replies value is available from the persona configuration. Run the queue.rs unit tests, adding coverage for thread_replies: false. Done means false suppresses the new-thread --reply-to directive while true preserves it and existing thread replies remain unchanged.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 76/100