Skip to content

fix(core): report broadcast delivery failures in send_message - #10081

Merged
yiliang114 merged 1 commit into
QwenLM:mainfrom
yiliang114:fix/issue-10072
Aug 26, 2026
Merged

fix(core): report broadcast delivery failures in send_message#10081
yiliang114 merged 1 commit into
QwenLM:mainfrom
yiliang114:fix/issue-10072

Conversation

@yiliang114

Copy link
Copy Markdown
Collaborator

What this PR does

The send_message(to: "*") broadcast path no longer claims complete success when one or more deliveries were rejected. TeamManager.broadcast() now returns the delivery outcome it already computed — the number of recipients attempted and the names of the recipients whose delivery was rejected — instead of Promise<void>. The broadcast branch of send_message uses it to distinguish three outcomes:

  • Complete success: unchanged Message broadcast to all teammates.
  • Partial failure: reports how many recipients were reached and lists the recipients whose delivery failed, stating explicitly that they did not receive the message.
  • Total failure: returned as a tool error listing every unreachable recipient.

Why it's needed

Fixes the misleading tool result reported in #10072: TeamManager.broadcast() waits with Promise.allSettled() and logs the rejected deliveries, but always resolves normally, and the tool then unconditionally returned Message broadcast to all teammates. The leader model could therefore believe every teammate received a message that never reached a terminated/rejected recipient (e.g. a recipient whose queue was dropped between the member snapshot and the send), with no way to retry or escalate.

Reviewer Test Plan

How to verify

Reproduced before the fix with an invocation-level fault-injection test using the real TeamManager (via TeamCoordinationHarness): create a two-teammate team, terminate one teammate so its message queue is dropped, run send_message(to: "*"), and observe Message broadcast to all teammates.

cd packages/core
npx vitest run src/tools/send-message.broadcast.test.ts src/tools/send-message.test.ts src/agents/team/test-utils/coordination-harness.test.ts

Before the fix the two fault-injection tests are red (the tool claims complete success; error is undefined for total failure). After the fix all 112 tests across the three files are green, including regression tests that complete success still returns the original message byte-for-byte, that existing delivery semantics (sender skip, leader inbox delivery) are unchanged, and that the debug.warn log line is preserved.

Evidence (Before & After)

N/A for UI — this is an agent-visible tool result, not TUI output. Test evidence:

Before (red repro):

× does not claim complete success when a delivery is rejected
  → expected 'Message broadcast to all teammates.' not to be 'Message broadcast to all teammates.'
× reports failure when no delivery lands
  → expected undefined to be defined

After:

✓ src/tools/send-message.test.ts (21 tests)
✓ src/tools/send-message.broadcast.test.ts (3 tests)
✓ src/agents/team/test-utils/coordination-harness.test.ts (88 tests)
Test Files  3 passed (3)
     Tests  112 passed (112)

Tested on

OS Status
🍏 macOS ⚠️ not tested
🪟 Windows ⚠️ not tested
🐧 Linux ✅ tested

Environment (optional)

Unit/integration tests only (vitest) — no runtime environment needed.

Risk & Scope

  • Main risk or tradeoff: partial/total-failure results introduce new result wording that the leader model sees; the complete-success wording is kept byte-for-byte identical so happy-path behavior is unchanged. TeamManager.broadcast()'s return type changes from Promise<void> to Promise<BroadcastResult>; its only production caller is send_message (verified by grep).
  • Not validated / out of scope: a live end-to-end multi-agent session; the change is covered by harness-based integration tests with the real TeamManager.
  • Breaking changes / migration notes: none for users; internal API TeamManager.broadcast() return type changed.

Linked Issues

Fixes #10072

中文说明

这个 PR 做了什么

send_message(to: "*") 广播路径在部分投递被 reject 时不再声称全部成功。TeamManager.broadcast() 不再返回 Promise<void>,而是返回它本来就计算好的投递结果:尝试投递的收件人数量和投递被 reject 的收件人名单。send_message 的广播分支据此区分三种结果:

  • 完整成功:保持原文案 Message broadcast to all teammates. 不变。
  • 部分失败:报告成功送达的收件人数量,并列出投递失败的收件人,明确说明这些收件人没有收到消息。
  • 全部失败:作为工具错误(tool error)返回,并列出所有未送达的收件人。

为什么需要

修复 #10072 报告的误导性工具结果:TeamManager.broadcast()Promise.allSettled() 等待并记录被 reject 的投递,但总是正常 resolve,工具随后无条件返回 Message broadcast to all teammates.。这会导致 leader 模型以为所有 teammate 都收到了消息,而实际上消息并未送达已终止/被 reject 的收件人(例如成员快照与发送之间队列被清理的收件人),且无法重试或升级处理。

审阅者测试计划

如何验证

修复前已用真实 TeamManager(通过 TeamCoordinationHarness)的调用级故障注入测试复现:创建两个 teammate,终止其中一个使其消息队列被清理,运行 send_message(to: "*"),观察到结果仍为 Message broadcast to all teammates.

运行上面「How to verify」中的命令:修复前两个故障注入测试为红(工具声称全部成功;全部失败时 error 为 undefined);修复后三个文件共 112 个测试全绿,包括「完整成功仍返回原文案」的回归测试、既有投递语义(跳过发送者、leader 收件箱投递)不变的验证,以及 debug.warn 日志保持原样的确认。

前后证据

UI 层面 N/A —— 这是 agent 可见的工具结果,不是 TUI 输出。测试证据见上方英文版。

测试环境

macOS 未测试,Windows 未测试,Linux 已测试。

环境(可选)

仅单元/集成测试(vitest),无需运行时环境。

风险与范围

  • 主要风险或权衡:部分/全部失败引入了新的结果文案;完整成功文案保持逐字节不变,快乐路径行为不受影响。TeamManager.broadcast() 返回类型从 Promise<void> 变为 Promise<BroadcastResult>,唯一的生产调用方是 send_message(已用 grep 确认)。
  • 未验证 / 超出范围:真实端到端多 agent 会话;改动由基于真实 TeamManager 的 harness 集成测试覆盖。
  • 破坏性变更 / 迁移说明:对用户无;内部 API TeamManager.broadcast() 返回类型变化。

关联 Issue

Fixes #10072

send_message(to: "*") unconditionally returned "Message broadcast to
all teammates." even when TeamManager.broadcast() had rejected
deliveries: broadcast() collected the per-recipient failures but
returned Promise<void>, discarding them.

Make broadcast() return a BroadcastResult (attempted total + failed
recipient names) derived from the failures it already computes, and
let the send_message broadcast branch distinguish complete success,
partial failure (naming the unreachable recipients), and total
failure (returned as a tool error).

Co-authored-by: Qwen-Coder <qwen-coder @alibabacloud.com>
@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

Qwen Triage finishedview run. See the stage comments in this thread for the result.

Qwen Triage 已完成 —— 查看运行。结果见本线程中的各阶段评论。

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Thanks for the PR!

Template looks good ✓

Problem: observed, not theoretical. #10072 was filed from static inspection, but the rejection path is verifiably reachable in production: TeamManager.sendMessage throws when a recipient's per-agent queue was dropped on terminal status (the documented "recipient terminated between snapshot and send" race), on backpressure, and on leader-inbox write errors — while broadcast() swallowed all of that via Promise.allSettled and the tool unconditionally reported complete success. The issue is maintainer-triaged (type/bug, priority/P2, welcome-pr), and this PR ships a red-to-green fault-injection reproduction against the real TeamManager.

Direction: aligned — this fixes a misleading tool result in the agent-team messaging path, where the leader model can currently act on a false "everyone got it" signal. No direct CHANGELOG reference for broadcast failure reporting, but the area is relevant and actively maintained (recent upstream entries keep fixing agent-team delivery edge cases).

Size: core paths touched (packages/core/src/**) — 58 production lines (TeamManager.ts 24+9, send-message.ts 23+2) vs. 164 test lines. Well under any threshold.

Approach: scope feels right. broadcast() already computed the delivery outcome and threw it away, so returning it is the minimal fix; the tool then splits into complete-success (wording preserved byte-for-byte), partial-failure, and total-failure (tool error). The obvious alternative — reject the whole broadcast on any failure — is exactly what the existing allSettled comment argues against, so that path was correctly not taken. No unrelated edits in the diff.

Risk: no elevated risk signals — none of the changed files match the revert-correlated paths from the Stage 1e analysis.

Moving on to code review. 🔍

中文说明

感谢贡献!

模板完整 ✓

问题:是已观测到的问题,不是理论性的。#10072 虽然源自静态代码检查,但 reject 路径在生产中真实可达:TeamManager.sendMessage 在收件人的消息队列因终止状态被清理时(即文档中"成员快照与发送之间收件人终止"的竞争)、背压时、以及 leader 收件箱写入失败时都会抛错——而 broadcast()Promise.allSettled 吞掉了所有这些失败,工具无条件报告全部成功。该 issue 已由维护者分诊(type/bugpriority/P2welcome-pr),本 PR 还提供了针对真实 TeamManager 的由红转绿的故障注入复现。

方向:对齐——修复 agent 团队消息路径中的误导性工具结果:leader 模型目前可能基于"所有人都收到了"的假信号行动。CHANGELOG 没有广播失败上报的直接条目,但该领域相关且持续维护中(上游最近多个条目都在修 agent 团队投递的边界情况)。

规模:触及核心路径(packages/core/src/**)——58 行生产代码(TeamManager.ts 24+9,send-message.ts 23+2),测试 164 行,远低于任何阈值。

方案:范围合理。broadcast() 本来就计算了投递结果却直接丢弃,把它返回就是最小修复;工具随后区分完整成功(文案逐字节保持不变)、部分失败、全部失败(工具错误)。另一个显而易见的路径——任一失败就整体 reject——正是现有 allSettled 注释明确反对的,因此正确地没有采用。diff 中没有无关改动。

风险:无升级风险信号——改动文件均未命中 Stage 1e 的易回滚路径分析。

进入代码审查 🔍

Qwen Code · qwen3.8-max

Reviewed at 24d7fc55d41d74afd70a8131aaa454499c408cfe · re-run with @qwen-code /triage

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Code review — no blockers found.

I formed my independent proposal before reading the diff (return the delivery outcome broadcast() already computes; split the tool result into complete/partial/total failure; keep the success wording byte-identical; fault-injection tests against the real TeamManager) — the PR lands on the same shape, and I didn't find a simpler path it missed. Verified against the base code:

  • The rejection path is real: sendMessage throws when the recipient's per-agent queue was dropped on terminal status, on backpressure, on leader-inbox write errors, and for unknown recipients. Promise.allSettled result order matches recipients order, so the failedRecipients zip is correct.
  • send_message is the only production consumer of broadcast() (grepped packages/); the existing debug.warn line and sender-skip / leader-inbox semantics are preserved, and error: { message } matches this file's existing error shape.
  • Edge case: a broadcast with zero recipients still returns the original success string — byte-identical to today's behavior, so no regression there.
  • Conventions are clean: ESM imports, colocated kebab-case tests, no any; the storage mock in the new test file follows the same __setMockGlobalDir pattern as team-create.test.ts/team-lifecycle.test.ts, and it keeps importOriginal so the rest of the Storage API stays real.

Skipped the diagram/files-table enrichments — this is a small, focused change and they'd just be noise.

CI test evidence — fetched via API for the reviewed commit (PR code not executed here, per triage policy):

Check Conclusion
Test (ubuntu-latest, Node 22.x) ✅ success
web-shell E2E Smoke (ubuntu-latest, Node 22.x) ✅ success
Desktop Shell (ubuntu-22.04) ✅ success
Desktop Shell (windows-2022) ✅ success
precheck-pr / precheck ✅ success
Secret scan (TruffleHog) ✅ success
Dependency CVE audit ✅ success
Classify PR ✅ success
Test (macos-latest, Node 22.x) ⏭️ skipped (repo-gated)
Test (windows-latest, Node 22.x) ⏭️ skipped (repo-gated)
Integration Tests (CLI, No Sandbox) ⏭️ skipped (repo-gated)
tmux-testing / verify ⏭️ skipped (repo-gated)
triage ⏳ in progress (this run)
delay-automatic-review ⏳ waiting

The load-bearing check is Test (ubuntu-latest, Node 22.x): green on this commit, and it runs the vitest suite including the three files this PR adds or touches. No red checks anywhere on the commit; the skipped jobs are repo gating (maintainer-triggered), not failures. Not verified: the suite has not run on macOS/Windows for this commit, and the author's before-red/after-green numbers are their own run, not re-executed here. By inspection the new tests do assert the post-fix contract (llmContent not claiming success, error defined on total failure), so they would be red against the base — but the A/B proof is still open.

Sandboxed verification would settle that: @qwen-code /verify — an A/B run would prove the new fault-injection tests fail against the base build and pass with the diff, and would exercise the partial/total-failure result wording beyond the Linux-only CI run.

中文说明

代码审查 —— 未发现阻塞问题。

我在看 diff 之前先形成了自己的独立方案(把 broadcast() 本来就计算好的投递结果返回;工具结果区分完整成功/部分失败/全部失败;成功文案逐字节保持不变;用真实 TeamManager 做故障注入测试)——PR 的做法与之一致,也没有找到被它遗漏的更简路径。对照基线代码核实:

  • reject 路径真实存在:sendMessage 在收件人队列因终止状态被清理、背压、leader 收件箱写入失败、收件人不存在时都会抛错。Promise.allSettled 的结果顺序与 recipients 一致,failedRecipients 的对位是正确的。
  • send_messagebroadcast() 唯一的生产调用方(已 grep packages/ 确认);原有 debug.warn 日志、跳过发送者、leader 收件箱投递语义均保留;error: { message } 与本文件既有错误形状一致。
  • 边界情况:零收件人的广播仍返回原成功文案——与现状逐字节一致,无回归。
  • 规范检查通过:ESM 导入、同目录 kebab-case 测试、无 any;新测试文件的 storage mock 沿用 team-create.test.ts/team-lifecycle.test.ts__setMockGlobalDir 模式,并通过 importOriginal 保留其余真实 Storage API。

省略了时序图/文件表——改动小而聚焦,加上去只是噪音。

CI 测试证据 —— 通过 API 获取(按分诊策略不执行 PR 代码):核心套件 Test (ubuntu-latest, Node 22.x) 在该提交上为绿,它运行的 vitest 套件包含本 PR 新增/修改的三个测试文件。该提交上没有任何红色检查;macOS/Windows 测试及集成测试任务为仓库门控(需维护者触发)跳过,并非失败。未验证:该提交未在 macOS/Windows 上跑过套件;作者"修复前红、修复后绿"的数据是其本人运行结果,未在此重新执行。从测试内容看,新测试确实断言修复后的契约(结果不再声称全部成功、全部失败时 error 有定义),对基线应当为红——但 A/B 证明仍未闭环。

沙盒验证可以补上这一环:@qwen-code /verify —— A/B 运行可证明新的故障注入测试在基线构建上失败、在本 PR 上通过,并在 Linux-only 的 CI 之外验证部分/全部失败的结果文案。

Qwen Code · qwen3.8-max

Reviewed at 24d7fc55d41d74afd70a8131aaa454499c408cfe · re-run with @qwen-code /triage

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Confidence: 4/5 — clean, minimal fix verified against the base code; the only unverified bits are the A/B red baseline (author's own run) and non-Linux unit runs (repo-gated), neither of which changes the call.

Stepping back: this is exactly the kind of PR the gate should wave through. The problem is real and verified — sendMessage genuinely rejects on queue-drop/backpressure/write errors while the tool unconditionally claimed full delivery, and #10072 was maintainer-triaged as a P2 bug. The fix is the minimum that solves it: broadcast() returns the outcome it already computed, the tool splits into three honest results, and the happy-path wording is untouched byte-for-byte so nothing downstream drifts. I formed my own proposal before reading the diff and landed on the same shape — no simpler path was missed, and there's no drive-by in the diff.

The tests are the right kind: fault injection through the real TeamManager (queue dropped via teammate shutdown), asserting all three outcomes including the byte-identical success regression guard. CI is green on this commit for both pull_request workflows. The remaining gap is only that nobody but the author has seen the tests run red against the base — /verify closes that if a maintainer wants the A/B proof; I don't consider it merge-blocking for a 58-line change this thoroughly pinned.

Approving. ✅

中文说明

信心:4/5 —— 干净、最小的修复,已对照基线代码核实;唯一未验证的是由红转绿的 A/B 基线(作者本人运行)和非 Linux 的单测运行(仓库门控),两者都不影响结论。

退一步看:这正是分诊闸门应该放行的 PR。问题真实且已核实——sendMessage 确实会在队列被清理/背压/写入失败时 reject,而工具却无条件声称全部送达;#10072 已被维护者分诊为 P2 bug。修复是解决问题所需的最小改动:broadcast() 返回它本来就计算好的结果,工具区分三种诚实的结果,快乐路径文案逐字节未动,下游不会漂移。我在看 diff 前先形成了自己的方案,结论与之相同——没有遗漏更简路径,diff 里也没有顺手改动。

测试也是对的:通过真实 TeamManager 做故障注入(终止 teammate 使队列被清理),断言全部三种结果,包括"成功文案逐字节不变"的回归保护。该提交上两个 pull_request 工作流均为绿。剩余缺口仅在于除作者外没人见过这些测试在基线上变红——维护者如需 A/B 证明可用 /verify 补上;对这样一个 58 行、测试充分锁定的改动,我认为它不构成合并阻塞。

通过。✅

Qwen Code · qwen3.8-max

Reviewed at 24d7fc55d41d74afd70a8131aaa454499c408cfe · re-run with @qwen-code /triage

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, looks ready to ship. ✅

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed — no blockers. Suggestions are inline.

Test Plan (not a blocker): src/tools/send-message.broadcast.test.tsno such file or directory; src/tools/send-message.test.tsno such file or directory; src/agents/team/test-utils/coordination-harness.test.tsno such file or directory; Tests 112 passed — this review observed 21645, 1702, 24761, 1659, 601, 4235, 630 passed.

中文说明

已审查——无阻断问题。 建议见行内评论。

Test Plan(非阻断):src/tools/send-message.broadcast.test.tsno such file or directory; src/tools/send-message.test.tsno such file or directory; src/agents/team/test-utils/coordination-harness.test.tsno such file or directory; Tests 112 passed — this review observed 21645, 1702, 24761, 1659, 601, 4235, 630 passed

— qwen3.8-max via Qwen Code /review (v0.22.0)

Comment on lines 707 to 710
debug.warn(
`Broadcast: ${failures.length}/${results.length} send(s) failed ` +
`Broadcast: ${failedRecipients.length}/${results.length} send(s) failed ` +
`(recipient likely terminated).`,
);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] The warning prints only the failure count, but the failed recipients' names are available at the log site — failedRecipients is in scope one line above. This line is the only operational trace outside the conversation when a broadcast fails: no team event carries delivery failures (TeamEventType has MESSAGE_SENT only, emitted on success paths), and failedRecipients surfaces nowhere else. So when reconstructing an incident — which teammate missed a shutdown approval or a status update — an oncall sees Broadcast: 1/3 send(s) failed (recipient likely terminated). with no name to attribute, and must correlate agent-exit events by timestamp. Include the names in the log line:

Suggested change
debug.warn(
`Broadcast: ${failures.length}/${results.length} send(s) failed ` +
`Broadcast: ${failedRecipients.length}/${results.length} send(s) failed ` +
`(recipient likely terminated).`,
);
debug.warn(
`Broadcast: ${failedRecipients.length}/${results.length} send(s) failed ` +
`for: ${failedRecipients.join(', ')} (recipient likely terminated).`,
);
中文说明

该警告只打印失败数量,但失败收件人的名字在打日志的位置就可以拿到——failedRecipients 就在上一行。这一行是广播失败时对话之外唯一的运维痕迹:团队事件不携带投递失败信息(TeamEventType 只有 MESSAGE_SENT,且只在成功路径触发),failedRecipients 也没有在其他地方暴露。因此当需要排查「哪个 teammate 错过了关闭审批或状态更新」这类问题时,值班同学只能看到 Broadcast: 1/3 send(s) failed (recipient likely terminated).,没有名字可以归属,只能靠时间戳去关联 agent 退出事件。建议把失败收件人的名字加进日志行(修复代码见上方 suggestion)。

— qwen3.8-max via Qwen Code /review (v0.22.0)

Comment on lines +95 to +96
/** Names of recipients whose delivery was rejected. */
failedRecipients: string[];

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] failedRecipients: string[] collapses the four distinct sendMessage rejection causes — terminated recipient, unknown recipient, leader-inbox write failure, and backpressure — into bare names, and the only semantic annotation of "rejected" in this module (the warn just above the return) asserts "(recipient likely terminated)", which is wrong for three of the four. Backpressure is transient: its direct-send error explicitly tells the caller to wait for the backlog to drain, and the queue flushes the moment the recipient goes IDLE. Concrete trigger: teammate bob is ACTIVE with MAX_PENDING_MESSAGES (50) pending; the leader runs send_message(to: "*"); bob's delivery rejects — the tool then reports delivery failed for: bob. The listed recipients did not receive the message., byte-identical to bob having terminated. The leader model gets no hint that waiting and retrying would succeed, and can act as though bob is gone — reassigning bob's tasks or spawning a duplicate teammate — dropping a message that would have landed on bob's next idle flush. Consider carrying the rejection reason alongside the name (built from the PromiseRejectedResult.reason values in the allSettled results), surfacing the reason in the partial/total-failure text, and fixing or dropping the "(recipient likely terminated)" clause — or minimally, keep the shape but stop asserting a cause the data doesn't carry.

中文说明

failedRecipients: string[]sendMessage 的四种不同 reject 原因——收件人已终止、收件人不存在、leader 收件箱写入失败、背压——压缩成了纯名字,而模块中唯一对「rejected」的语义标注(return 上方那行 warn)断言 "(recipient likely terminated)",对其中三种原因来说都是错的。背压是瞬时的:直接发送的错误信息明确提示调用方等待积压消化,且收件人一旦进入 IDLE 队列就会被清空。具体场景:teammate bob 处于 ACTIVE 且积压了 MAX_PENDING_MESSAGES(50)条消息,leader 执行 send_message(to: "*"),bob 的投递被 reject——工具报告 delivery failed for: bob. The listed recipients did not receive the message.,与 bob 已终止时的输出逐字节相同。leader 模型得不到「稍等重试即可成功」的提示,可能表现得像 bob 已经消失一样——重新分配 bob 的任务或复制一个新 teammate——从而丢掉一条本会在 bob 下次 idle flush 时送达的消息。建议把 reject 原因随名字一起携带(从 allSettled 结果的 PromiseRejectedResult.reason 取值),在部分/全部失败文案中呈现原因,并修正或去掉 "(recipient likely terminated)" 从句——最小的改法是保持类型不变,但不再断言数据中不存在的原因。

— qwen3.8-max via Qwen Code /review (v0.22.0)

@qqqys qqqys left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving. Independent review of the current head (24d7fc55) found no blocking (Critical) issues:

  • TeamManager.broadcast() now returns a BroadcastResult (total / failedRecipients) instead of Promise<void>. The only production caller is send-message.ts (this PR); the return-value addition is backward-compatible and the never-reject (Promise.allSettled) semantics are unchanged.
  • failedRecipients index-alignment is correct (allSettled preserves recipient order; the results[i]?.status guard is safe).
  • Complete/partial/total-failure branching is sound; total failure correctly surfaces as a tool error listing every unreachable recipient, fixing the misleading "broadcast to all" result from #10072.
  • No permission/security surface change; behavior is reporting-only.

CI is green and the bot has approved; concur.

@yiliang114

Copy link
Copy Markdown
Collaborator Author

Independent A/B verification

Verified the current PR head 24d7fc55d41d74afd70a8131aaa454499c408cfe against base d4664fdc89ff3e77d4d4bdb82e6ec187fdd20acf in an isolated detached worktree.

I did not use tmux capture-pane for this check: the observable contract is the send_message(to: "*") tool result, not interactive TUI rendering. An invocation-level fault-injection test through the real TeamCoordinationHarness, TeamManager, and per-agent queues gives a more direct and repeatable signal.

Method

  • Fresh npm ci/build in an isolated worktree; the existing project checkout was not used or modified.
  • Ran the same three invocation scenarios at base and head:
    • one terminated recipient (partial rejection);
    • all recipients terminated (total rejection);
    • all recipients active (complete-success control).
  • The base predates the new test file, so I temporarily supplied the same three cases there. The head run used this PR's src/tools/send-message.broadcast.test.ts unchanged.
# base
git worktree add --detach <tmp>/repo d4664fdc89ff3e77d4d4bdb82e6ec187fdd20acf
cd <tmp>/repo
npm ci
cd packages/core
npx vitest run src/tools/send-message.broadcast.test.ts

# head
git checkout --detach 24d7fc55d41d74afd70a8131aaa454499c408cfe
cd packages/core
npm run build
npx vitest run src/tools/send-message.broadcast.test.ts

Before / after

Scenario Base PR head
Partial rejection Failed: still returned Message broadcast to all teammates. Passed: reports the unreachable recipient without claiming complete delivery
Total rejection Failed: error was still undefined Passed: returns a tool error naming all unreachable recipients
Complete success Passed Passed: original success message remains unchanged

Base result:

src/tools/send-message.broadcast.test.ts (3 tests | 2 failed)
× reports partial rejection
  → expected 'Message broadcast to all teammates.' not to be 'Message broadcast to all teammates.'
× reports total rejection
  → expected undefined to be defined
✓ preserves complete success

Test Files  1 failed (1)
Tests       2 failed | 1 passed (3)

Head result:

> @qwen-code/qwen-code-core@0.22.0 build
> node ../../scripts/build_package.js
Successfully copied files.

✓ src/tools/send-message.broadcast.test.ts (3 tests)
Test Files  1 passed (1)
Tests       3 passed (3)

Environment: macOS 15.1.1 arm64, Node.js 22.22.0, npm 10.9.4, Vitest 3.2.7.

This closes the red-to-green A/B gap: the misleading partial/total-failure behavior is reproducible at the base commit and fixed at the PR head, while the complete-success behavior is preserved. I did not run a full interactive CLI E2E; the current head's green CI provides the broader module/build coverage.

@yiliang114
yiliang114 added this pull request to the merge queue Aug 26, 2026
Merged via the queue into QwenLM:main with commit a82a11a Aug 26, 2026
141 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Agent Team: broadcast can report complete success after rejected deliveries

3 participants