docs: add peer session collaboration design - #9399
Conversation
Supersedes the fleet architecture proposed in #8719 (closed unmerged). Designs collaboration between independently started Qwen Code sessions: authority moves from TeamManager's in-memory state to the durable file board under ~/.qwen/, making the transport tier an optimization rather than a correctness requirement. Adopts the discovery registry and receive-side consent gate from the #8724 stack (#8728, #8730).
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
@yiliang114
## Risk & Scope— main risk or tradeoff, what's not validated / out of scope. For a design doc that supersedes earlier proposals, the tradeoffs belong exactly here.## Linked Issues— the body references #8718, #8719, #8724, #8728, #8730, #8869, #9276, #9282, #9283, but there is no formal linked-issues section.- The Chinese
<details>translation of the body — the template asks for a full paragraph-by-paragraph translation (完整翻译上面的英文正文,逐段对应).
Please add those sections, then re-trigger with @qwen-code /triage. The write-up itself and the reviewer test plan look solid — this is a template-completeness gate, not a judgement on the design.
中文说明
## Risk & Scope(风险与范围)——主要风险或取舍、未验证/超出范围的部分。对于一份取代既有方案的设计文档,取舍正应该写在这里。## Linked Issues(关联 Issue)——正文引用了 #8718、#8719、#8724、#8728、#8730、#8869、#9276、#9282、#9283,但没有正式的关联 issue 部分。- 中文
<details>翻译——模板要求完整逐段翻译英文正文,不要省略或缩写。
请补充以上部分后使用 @qwen-code /triage 重新触发。描述本身和 reviewer 测试计划都很扎实——这只是模板完整性检查,不代表对设计本身的评判。
— Qwen Code · qwen3.8-max
|
📄 Docs-only change — the automatic review ran at
Reviewed head: 中文说明📄 纯文档变更 —— 自动评审以 |
packages/cli/src/agent-view/ has no references elsewhere in main, and the first revision concluded from that it was abandoned and should be deleted. It is the base of #7799, whose remaining four PRs (#7800-#7803) are open, non-draft, and touch the directory directly. Static reachability in main cannot see consumers that live in pending PRs. This surfaces a real conflict the document was papering over: that series builds natively the terminal and supervisor layer section 4 argues to leave to herdr. Recorded as open question 7.6 rather than resolved, because it is a product decision.
|
Qwen Code review did not complete successfully. The review pipeline failed before a review could be posted. A transient error is retried automatically; if you are seeing this, retry with |
The row read as settled fact by citing herdr alone. #7800 is building PTY workers natively, so §4 states one side of an open disagreement rather than a decision. Label it as a position and point at §7.6, which owns it.
The doc was written against a pre-#8969 snapshot while stamping 179c8f8 as baseline. #8969 (merged 2026-08-17, ancestor of that baseline) already shipped the live-session registry, `qwen sessions ps`, and `utils/process-liveness.ts`, so section 1.3 (no discovery of any kind), section 1.5 (absence claim), and Stage 1 (revive #8728) all describe work that has landed. Restate discovery as liveness-only with no messaging channel, mark #8728 superseded, and shrink Stage 1 to carry-forward hardening. Also fix stale citations (TeamManager.ts 312 -> 325, harness 751 -> 1,456 LOC, teamHelpers 378 -> 365, tasks.ts 1,050 -> 1,056).
Adds the vocabulary the design was operating without: session, task, ask, decision. Two of these change the architecture rather than just naming it. `decision` is new and load-bearing. Approval, acceptance of a result, and adjudication of conflicting results are one act — they need authority, and no agent has more of it than another. Naming it gives the default view something to show and gives escalation a destination that is not another agent, which is what lets the leader leave the routing path entirely (2.4). Membership goes. A session is addressable because it registered, not because it joined, so team_join, the member record and the team namespace are all removed; Stage 3 becomes the ask/decision surface instead. Addressing follows one field with several schemes, so a new transport costs a prefix rather than a field and a branch. No generic message: task carries status and context, ask carries questions, decision carries authority. ask and decision stay separate rather than sharing a kind field, which would reintroduce the #9276 anti-pattern. Also narrows the consent-gate claim: with no free-form message the inbound surface is smaller, and Claude Code ships cross-session messaging with no inbound gate at all, so #8730 is hardening beyond the reference rather than parity with it.
There is no conflict. #7800, #7801 and #7803 touch no file under packages/core; #7802 touches three unrelated utilities; none of the four touches agents/team/, send-message.ts, tasks.ts or mailbox.ts — the entire surface this design changes. The conflict was an artifact of how 4 was written. Phrased as "not building X" it read as a project-wide ruling rather than this design's scope, so a PR building X looked like a contradiction. It is not one: whether Qwen hosts terminals natively is orthogonal to whether two sessions can ask each other a question. Removes open question 6, restores the two non-goal rows, and scopes 4 explicitly to this design.
Eight places still described the model the four nouns replaced. Fixed together because individually they read as typos and jointly they read as a different design: - 0 used "authority" for the files while 0.1 names a plane Authority (the human). Files are authoritative about state; they decide nothing. - 0.2 still sold a transferable leader field, which 2.4 rejects. - 0.2 and 4 still said "joined peer"; there is no join. - 2.3 recorded identity "at join" rather than at registration. - 3.5 routed reporting through send_message for "blockers, questions"; those are now an ask and a decision. - 3.6 exited via team_leave and reaped "membership". Redraws 2.1 around the four planes rather than the coordination plane alone. The diagram now carries three claims the prose was making implicitly: no arrow returns from an agent to the human box, every participant reaches the board without a proxy, and the foreign-agent column has no inbound arrow — which makes it a standing test, since a capability that cannot be drawn as an arrow out of that box does not exist for anything we did not write.
Two were dissolved by earlier changes rather than answered: - Remote join has no subject left, since there is no join. The question that survives is whether A may give B unasked-for work: yes, but assignment is a proposal, not an obligation. Naming an owner is not the authority to compel one, which is what keeps the peer model from quietly becoming a hierarchy. - Re-engagement over #8724 was a process item in a technical list, and moot: #8969 landed the registry independently and the rest is public on an open branch. Nothing needs anyone's agreement to proceed. Same category error as the conflict retracted in 1.6 — treating a social step as a dependency. Three answered against the code: - Storage roots stay split for now; consolidate before Stage 5 ships the board surface, since that is when the layout first becomes externally readable. The merged registry already carries schemaVersion. - Daemon sessions register. Only startInteractiveUI calls registerSession today and SessionRegistryRecord has no kind field; 2.1 already draws the daemon as a participant, and kind is needed anyway to tell interactive, daemon and wrapped foreign agents apart. - Sub-session residency stays out of scope. create-sub-session states it is NOT kept resident; making it resident rebuilds the spawn model this design exists to avoid. Whoever wants a resident participant starts a session. Kept as a resolutions section rather than deleted, so they are not relitigated.
…tion The requirement is that different kinds of agent can communicate. Nothing can push into a process it did not launch and whose stdin it does not hold, so a foreign agent can never be a delivery target — but it can run a command. The shared operation is therefore fetching, and anything built on delivery excludes, by construction, every agent we did not write. The document had this the other way round: delivery first, fetching as the fallback for foreigners. That ordering is what bought the sockets, the receive-side gate, the wake frames, and an assignment protocol that had to decay when undelivered. All four exist to make push safe. Inverted: - 0.1 is three required layers (board, access, authority) with push listed explicitly as later. The access layer is required rather than convenient, because a capability reachable only through Qwen's in-process tools does not exist for anything else. - 2.5 keeps #8730's receive-before-send argument in full and applies it: a gate must precede a push path, so v1 has neither. - 3.2 drops the offer-and-decay assignment protocol. It repaired a state only push can produce — assigned, sent, never arrived. Fetching has no undelivered state, so visibility does the whole job. - 5 reorders around it. Stage 2, the CLI over the board, is where the requirement is actually met; the Qwen-native surfaces come after so the CLI sets the contract rather than inheriting one. Adds 1.8 for the constraint itself, with the 2x2 whose bottom-right cell is impossible for anyone, herdr included.
The strongest objection to a fetch contract is that an idle interactive session never fetches, so a question addressed to it is never answered. That is real, and it is not an argument for push: responsiveness is a property of how often a participant chooses to look, and looking costs one readdir plus a few small reads. A five-second idle timer with a footer badge is below the threshold a human notices, while keeping the property that makes the contract work — the participant chose the moment, so no door is open. Push therefore buys nothing in v1, not even latency. 2.8 settles what a board is on disk: one root per board, an explicit name so the cross-workspace case is expressible (a board that is a directory cannot span two repos), participant records keyed by name that read liveness from #8969's pid-keyed registry rather than heartbeating, name claiming by suffixing through the existing generateUniqueTeammateName, and manual pruning because automatic deletion of a record someone may be mid-read on is a concurrency problem worth not having yet. Also bounds --wait: default 30s timeout and an exit status that distinguishes found from timed out, since a foreign agent running it is blocking its own turn.
Flat JSON with schemaVersion first, matching SwarmTask so the existing atomic write and lock discipline covers them without special cases. Three choices carry weight. expiresAt makes timeout terminal without a sweeper: any reader seeing state open past the deadline computes the same answer, and settling is lazy. That matters because a fetch-based system has no daemon guaranteed to be running — the only process you can count on is the one currently asking. decision has no expiry on purpose. Silent expiry converts "nobody has looked" into "the system decided", which is the one authority nothing but a human may hold. IDs are short because a person types them into resolve commands. The CLI is the contract, and a surface painful by hand gets wrapped — at which point the wrapper is the real contract.
packages/cli/src/agent-view/ — supervisor-server, supervisor-client, supervisor-store, supervisor-process, supervisor-runner, terminal-bridge, protocol and current-cli-argv — has no consumer. All 87 exported symbols are unreferenced outside the directory, no module imports it by path or by name, no CLI flag or sentinel string reaches it, and no build config mentions it. It only imports node builtins and core, so it is a leaf. It is the bespoke supervisor and PTY half of the fleet architecture, whose last open PR (#8869) is closed. The peer-session design that replaces it does not build a terminal or supervisor layer natively, so nothing here gets a consumer later. Left in place because it is live and unrelated despite the similar name: packages/cli/src/ui/components/agent-view/ (AgentTabBar, AgentChatView, AgentComposer), imported by DefaultAppLayout. Refs #9399
…2's size The vocabulary specified top-level nouns (qwen task / ask / decision). The implementation groups them under one board namespace with flat verbs, because the id prefix already carries the noun — t- task, a- ask, d- decision — so board claim t-3 says everything board task claim t-3 does in two levels instead of three. That shortening matters for a surface an agent types every turn, and it is why 2.9 makes ids short and prefixed rather than UUIDs. Also records fleet up, which the plan never had: the demo entry point was missing entirely because spawn was treated as an existing capability rather than as the thing that makes a one-command demo possible. And corrects Stage 2's estimate. It was sized at ~350 lines on the assumption the board already existed; it landed near three times that because agents/team/tasks.ts could not be reused — it is Agent Team's in-session list with dependency edges and a scheduler-facing change emitter, under a different storage root. Reusing it would have meant rewriting that scheduler.
An invariant the runtime does not enforce is worse than a convention that says so, and three places asserted properties the shipped code does not have. task was defined as owner/status/deps and 2.7 said dependencies are already modelled. BoardTaskRecord carries subject, owner, status and notes; SwarmTask has the edges but could not be reused (5), so the deadlock detection 3.3 describes has no data yet. "No agent resolves a decision" read as an invariant. qwen board resolve is on the same command line the prompt teaches agents to use, so anything with a shell could settle its own. Now stated as agreement, with what enforcing it would take.
|
@qwen-code /review |
|
Qwen Code review request accepted. Review is queued in workflow run. |
|
Template sections added: @qwen-code /triage |
|
@qwen-code /triage |
|
Sandboxed verification: This PR changes documentation/assets only — there is no code to execute, so a sandboxed verification has nothing to verify. 中文 — 判定:
|
|
Note for a reviewer: the bot's Its ask was the template — I could not get the state cleared automatically. Nothing here blocks merge on its own: |
|
Thanks for the PR! Template sections from the previous run are all in place now ✓ ( Problem: this is an observed coordination gap, not a theoretical one — the multi-agent direction genuinely has no written baseline today: #8718 closed not-planned, its Stage 1B draft #8869 closed, the fleet design #8719 closed unmerged and never landed, and #8724 open with its implementation stack closed. Meanwhile #9402 is actively implementing against this document. The problem exists and is costing real context right now. Direction: aligned. Cross-session collaboration is an area the ecosystem is actively investing in — Claude Code's CHANGELOG carries repeated entries for cross-session Size: docs only — one new file under Approach: scope matches the stated goal — one dated plan document that records the traced current state, the target architecture, the staged build plan, and the disposition of every related item. No unrelated changes in the diff. Risk: no elevated risk signals — no code paths touched at all. Moving on to review the document content, including spot-checking the traced claims in §1 against the current tree. 🔍 中文说明感谢贡献!上一轮要求的模板部分已全部补齐 ✓( 问题:这是一个已观测到的协调缺口,不是理论问题——多 agent 方向目前确实没有书面基线:#8718 已按 not-planned 关闭、其 Stage 1B 草稿 #8869 已关闭、fleet 设计 #8719 关闭未合入且从未进入仓库、#8724 开着但实现栈已关闭。同时 #9402 正在以本文档为参照进行实现。问题真实存在,且正在消耗实际成本。 方向:对齐。跨会话协作是生态正在持续投入的方向——Claude Code 的 CHANGELOG 中反复出现跨会话 规模:纯文档—— 方案:范围与目标一致——一份带日期的计划文档,记录实测现状、目标架构、分阶段构建计划,以及每个相关条目的处置。diff 中无无关改动。 风险:无升级风险信号——完全不触及代码路径。 进入文档内容审查,包括对照当前代码树抽查 §1 的实测结论。🔍 — Qwen Code · qwen3.8-max Reviewed at |
|
Code review — docs-only: one new file, I followed the PR's own test plan and spot-checked the traced claims against the current tree. They hold, with line-level precision in most cases:
One nit found (the Testing Unattended CI run — no PR code was built or executed (nothing runnable exists in this PR). Evidence is the PR's own CI checks on the reviewed commit, fetched via the API: every completed check is green or legitimately skipped. The unit suite ran on Linux only (
中文说明代码审查 —— 纯文档:新增单个文件 按照 PR 自带的测试计划,对 §1 的实测结论在当前代码树上做了抽查,绝大多数精确到行号:
只发现一处小问题(上述 测试 无人值守 CI 运行——未构建或执行任何 PR 代码(本 PR 也没有可执行内容)。证据为评审提交上 PR 自己的 CI 检查(经 API 获取):所有已完成的检查非绿即合理跳过。单测仅在 Linux 运行(成功);macOS/Windows 测试与集成测试被跳过,对纯文档改动无影响。唯一 — Qwen Code · qwen3.8-max Reviewed at |
|
Confidence: 4/5 — clean review across every stage; the only nit is one imprecise config number. The independent baseline I wrote before reading the diff — one dated document that traces what actually exists, pins the decisions, lays out a staged build plan, and records the disposition of past work — is exactly what this PR delivers, and it delivers it with more rigor than that baseline asked for: line-numbered traces, a reuse ledger, and explicit self-corrections of its own earlier revision. I didn't find a simpler form that would do the job; a single document is the minimal shape of this deliverable, and the diff contains nothing but that document. I did not take the document's claims on faith. Every §1 reference I spot-checked held — several to the exact line ( What tips this from "fine" to "genuinely valuable" is that the failure modes it documents already happened: the fleet plan never landed so it could be neither cited nor corrected, #8718's recorded close rationale misstated a position it never held, and the first revision of this very document misread Non-blocking nit for the next time the document is touched: correct the Approving, pinned to the reviewed commit. ✅ 中文说明信心:4/5 —— 各阶段审查均干净;唯一的小问题是某个配置数字不精确。 我在读 diff 之前写下的独立方案——一份带日期的文档,实测现状、固化决策、给出分阶段构建计划、记录既有工作的处置——正是这个 PR 交付的东西,而且它的严谨程度超出了该方案的最低要求:带行号的实测引用、复用清单、对自身早期版本的明确更正。我没有找到能完成同样任务的更简形式;单个文档就是这一交付物的最小形态,diff 中也只有这一个文档。 我没有轻信文档的结论。抽查的每一处 §1 引用都成立——多处精确到行( 让它从"还行"变成"真正有价值"的,是它所记录的那些失败模式已经发生过:fleet 计划从未落地因此既无法引用也无法更正、#8718 的关闭理由误述了它从未持有的立场、本文档的第一版把 非阻塞小问题,留待下次改动文档时处理:更正 §1.1 中 批准,锚定在被审查的提交上。✅ — Qwen Code · qwen3.8-max Reviewed at |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. ✅
…rrors - The reused agents/team helpers do NOT supply 0700/0600; mandate the modes explicitly in 2.8/2.9 following session-registry.ts (#8969), and correct 2.5/4 to name the mandated mode rather than claim the substrate provides it. - 0.3: #8724 is the open umbrella; its 7,018-line implementation is #8730. - 1.1: split retry counts (tasks.ts 30, mailbox.ts 10).
- 1.4/9: quote production vs incl-tests LOC (3,482/9,714 supervisor; 3,033/6,186 agent-view). - 1.5: the cross-session-inbox branch is in the author fork, not this repo. - 1.8: #9047 is an open PR proposal, not a trace of shipped code.
|
Closeout progress (two commits pushed):
Seven threads replied + resolved (1 Critical + 6 Suggestion). The remaining Suggestions are mostly design-decision items (participant identity mechanism, |
Superseded by 27548dc: the design was narrowed to the minimal board MVP, all review threads were resolved, and the PR body now matches the current scope.
|
The final design document is now included in #9402 alongside the Stage 1/2 implementation, so this PR no longer needs to land separately. |
…onfigured cap (QwenLM#9403) The skill stated an absolute "one to three" while the real ceiling is `agents.team.maxTeammates` (default MAX_TEAMMATES = 10), enforced by a throw in spawnTeammate. team_create's own prompt already derives its number from the setting; the skill did not, so lowering the cap to one or two left the skill instructing a spawn that fails outright. Express the guidance relative to the cap rather than as a second absolute, and give the reason for preferring few teammates — past roughly three the leader spends more of its turn reconciling reports than working. Refs QwenLM#9399 Co-authored-by: yiliang114 <jinjing.zzj@gmail.com>
What this PR does
Defines the minimum portable contract for collaboration between independently started agents: a durable, pull-based board containing tasks, asks, and decisions.
The MVP deliberately has no membership, join/leave lifecycle, roster, heartbeat, name claim, implicit project scope, or ambient identity. Every command names a board explicitly, and every mutation declares an actor label explicitly. Those labels are audit metadata, not authentication.
The design also separates the portable contract from optional future Qwen-native UI, orchestration, and push delivery. The implementation in #9402 follows the Stage 1 and Stage 2 contract only.
Why it's needed
Independently started agents do not share a parent process or a common inbound channel, but they can all run a command and read durable state. A pull-based board is therefore the smallest collaboration model shared by Qwen Code, Codex, shell scripts, and scheduled jobs.
Narrowing the first release to this contract gives reviewers concrete security, concurrency, failure, and acceptance boundaries without committing the project to speculative participant management or process orchestration.
Reviewer Test Plan
How to verify
Evidence (Before & After)
N/A — documentation only.
Tested on
Environment (optional)
N/A — no runtime behavior changes.
Risk & Scope
Linked Issues
Refs #8724
Implemented by #9402
中文说明
这个 PR 做了什么
定义独立启动的 Agent 之间进行协作所需的最小可移植契约:一个持久化、拉取式的 Board,其中只包含任务、提问和决策。
MVP 明确不包含成员系统、加入/离开生命周期、名册、心跳、名称占用、隐式项目范围或环境身份。每条命令都显式指定 Board,每次修改都显式声明操作者标签。这些标签只用于审计记录,不代表身份认证。
设计还将可移植契约与未来可选的 Qwen 原生 UI、编排和推送能力分离。#9402 只实现 Stage 1 和 Stage 2 契约。
为什么需要
独立启动的 Agent 不共享父进程,也没有通用的输入通道,但它们都能执行命令并读取持久状态。因此,拉取式 Board 是 Qwen Code、Codex、Shell 脚本和定时任务都能使用的最小协作模型。
将首个版本收窄到这一契约,可以让 reviewer 针对明确的安全、并发、失败和验收边界进行检查,同时避免项目过早承诺成员管理或进程编排等推测性能力。
评审测试计划
如何验证
证据(前后对比)
不适用——仅包含文档。
测试环境
见上表。
运行环境(可选)
不适用——没有运行时行为变化。
风险与范围
关联 Issue
Refs #8724
由 #9402 实现