fix(buzz): preserve stable thread roots

This commit is contained in:
yuvalfis
2026-08-06 09:49:30 +03:00
committed by Teknium
parent 367c13ad5c
commit 09cbce43e0
2 changed files with 89 additions and 3 deletions

View File

@@ -782,7 +782,7 @@ class BuzzAdapter(BasePlatformAdapter):
if not content:
return SendResult(success=False, error="Empty message")
args = ["messages", "send", "--channel", str(chat_id), "--content", "-"]
reply_target = reply_to or (metadata or {}).get("thread_id")
reply_target = (metadata or {}).get("thread_id") or reply_to
if reply_target:
args += ["--reply-to", str(reply_target)]
code, out, err = await self._run_cli(args, input_text=content)
@@ -929,8 +929,9 @@ class BuzzAdapter(BasePlatformAdapter):
"--file", str(local),
"--content", "-",
]
if reply_to:
args += ["--reply-to", str(reply_to)]
reply_target = (metadata or {}).get("thread_id") or reply_to
if reply_target:
args += ["--reply-to", str(reply_target)]
code, out, err = await self._run_cli(args, input_text=caption or "")
if code != 0:
return SendResult(success=False, error=_cli_error_message(err, code), retryable=code == 2)
@@ -1310,6 +1311,23 @@ class BuzzAdapter(BasePlatformAdapter):
# open with "@Chip" even though no mention is required there, so the
# strip applies to both chat types.
dispatch_text = self._strip_mention(content)
tags = event.get("tags")
thread_id = (
next(
(
str(tag[1])
for tag in tags
if isinstance(tag, (list, tuple))
and len(tag) > 3
and tag[0] == "e"
and tag[1]
and tag[3] == "root"
),
None,
)
if isinstance(tags, list)
else None
)
await self._dispatch_message(
text=dispatch_text,
@@ -1319,6 +1337,7 @@ class BuzzAdapter(BasePlatformAdapter):
user_name=await self._resolve_user_name(pubkey),
message_id=event_id,
created_at=created_at,
thread_id=thread_id,
)
# ── DM classification (issue #68871) ──────────────────────────────────
@@ -1482,6 +1501,7 @@ class BuzzAdapter(BasePlatformAdapter):
user_name: str,
message_id: str,
created_at: int,
thread_id: Optional[str] = None,
) -> None:
"""Build a MessageEvent and hand it to the base class handler."""
if not self._message_handler:
@@ -1493,6 +1513,7 @@ class BuzzAdapter(BasePlatformAdapter):
chat_type=chat_type,
user_id=user_id,
user_name=user_name,
thread_id=thread_id,
)
event = MessageEvent(

View File

@@ -828,6 +828,34 @@ class TestDmClassification:
assert a._may_reclassify_as_dm(CHANNEL) is False
class TestThreadRoots:
@pytest.mark.asyncio
async def test_inbound_root_e_tag_propagates_to_session_source(self):
adapter = _make_adapter()
adapter._channel_state[CHANNEL] = {
"chat_type": "group",
"last_ts": 0,
"seen": {},
}
dispatched = []
async def capture(event):
dispatched.append(event)
adapter._message_handler = AsyncMock()
adapter.handle_message = capture
event = _tagged_event("latest-child", CHANNEL, content="@Chip follow-up")
event["tags"] += [
["e", "stable-root", "", "root"],
["e", "latest-parent", "", "reply"],
]
await adapter._handle_event(CHANNEL, adapter._channel_state[CHANNEL], event)
assert dispatched[0].source.thread_id == "stable-root"
# ── Sending ───────────────────────────────────────────────────────────────
@@ -871,6 +899,24 @@ class TestBuzzAdapterSend:
args, _stdin = cli.calls[0]
assert args[args.index("--reply-to") + 1] == "buzz-event-123"
@pytest.mark.asyncio
async def test_send_prefers_stable_thread_root_over_latest_reply(self):
adapter = _make_adapter()
cli = _ScriptedCli()
cli.script("messages", "send", {"accepted": True, "event_id": "evt124"})
adapter._run_cli = cli
await adapter.send(
CHANNEL,
"threaded reply",
reply_to="latest-child",
metadata={"thread_id": "stable-root"},
)
args, _stdin = cli.calls[0]
assert args[args.index("--reply-to") + 1] == "stable-root"
@pytest.mark.asyncio
async def test_send_image_local_file_uses_file_flag(self, tmp_path):
img = tmp_path / "shot.png"
@@ -884,6 +930,25 @@ class TestBuzzAdapterSend:
args, _stdin = cli.calls[0]
assert args[args.index("--file") + 1] == str(img)
@pytest.mark.asyncio
async def test_send_image_local_file_prefers_stable_thread_root(self, tmp_path):
img = tmp_path / "shot.png"
img.write_bytes(b"\x89PNG fake")
adapter = _make_adapter()
cli = _ScriptedCli()
cli.script("messages", "send", {"accepted": True, "event_id": "evt127"})
adapter._run_cli = cli
await adapter.send_image(
CHANNEL,
str(img),
reply_to="latest-child",
metadata={"thread_id": "stable-root"},
)
args, _stdin = cli.calls[0]
assert args[args.index("--reply-to") + 1] == "stable-root"
# ── Lifecycle ─────────────────────────────────────────────────────────────