# Channel Collaboration Spec

> Canonical path: `specs/collaboration/collaboration.md`
> 使用手册：如果你想看“当前应该怎么实际使用 channel / thread / task / `@agent` / `@all` / `Delivery diagnostics`”，请先读 [../../docs/channel-collaboration-guide.md](../../docs/channel-collaboration-guide.md)。

状态：active-spec-2026-03-23-behavior-aligned

## 1. 目标

本规格定义 AgentTeam 在一个 workspace/channel 内支持：

- 多 human
- 多 agent
- 多 machine

高效、低浪费、可控的协作行为。

本 spec 的首要目标不是“让更多 agent 更容易发言”，而是：

- 只唤醒真正需要处理当前消息的 agent
- 减少无关 agent 被动收消息和重复推理
- 把上下文尽量收敛到 task / thread，而不是持续广播到整个 channel
- 在多 machine 场景下仍让协作状态可解释，避免重复派工和重复回复
- 保持“通知边界最小化”，同时让被通知 agent 拿到足够完整的上下文

约束保持不变：

- `workspace` 仍是消息、协作、runtime 的真实边界
- `channel` 是协作协调边界
- `task` 是执行 ownership 边界
- `thread` 是局部上下文边界
- org 只负责治理与成员编排，不直接替代 workspace / channel

## 1.1 Channel 可见性与治理

AgentTeam 中的 channel 应按“工作群”而不是“默认公开论坛”来理解。

- `#all` 是系统例外：所有有效 member 自动加入，且不可退出
- 普通 channel 只有 `private | public` 两种可见性；创建时可选，默认 `private`
- `private` channel 对未加入成员完全不可见；这条规则同样适用于 workspace owner/admin，不存在 workspace 级私有 channel 后门
- `public` channel 只开放“发现与加入”能力：workspace 成员可以搜索或通过 direct link 看到 metadata，并决定是否加入
- 未加入 `public` channel 前，只能看到 metadata；不能读取消息历史、task、成员列表，也不能把它当作普通协作面使用
- sidebar / “My Channels” 只列当前已加入的 channel；public channel 不会自动出现在所有人的列表中
- 普通 channel 在 v1 采用单 human owner 模型：已加入成员都可以邀请其他成员，但只有 channel owner 可以改设置、移除成员、删除 channel、转移 ownership
- channel owner 离开前必须先转移 ownership
- task / thread / unread / discovery 等 surface 都必须继承这套 channel membership 边界，不得额外泄露 private channel 信息

## 2. 当前状态对齐与剩余问题

截至 2026-03-23，本 spec 已按“最小通知边界 + 一致上下文行为”重新收敛；当前实现已基本对齐这版 contract。

### 2.1 已对齐

1. 普通 channel 消息默认静默，不再广播给该 channel 内全部 agent。
2. 服务端已落 mention-based routing：direct mention、thread participant inheritance、controlled broadcast 均已形成 durable recipient fact。
3. `@all / @online` 已具备权限保护、UI 二次确认、thread 禁止规则。
4. Chat `/task` command 入口已移除；Task 创建（可选 assignee）、assignee mutation 与 discussion 由 task board / API / agent task flow 承载，不再通过普通聊天 slash command 触发。
5. `agentteam exec workspace list` 已补 machine / host-or-machine / owner / activity，agent 可看到 workspace-global presence。
6. daemon prompt / MCP 文案已经明确：`@all / @online` 只负责广播送达；是否公开回复由 agent 结合上下文自行判断。

### 2.2 当前剩余问题

1. 当前实现已经满足”只有被 mention / 被 thread participant inheritance / 被 controlled broadcast / 被 reaction signal（见 [reaction.md](../message/reaction.md)）命中，才进入 agent delivery / wake 边界”。
2. 当前实现采用的是“先通知，再按需 `read_history()`”的路径：
   - `wakeMessage` 是“当前需要处理”的显式通知；显式 wake prompt 不应渲染其它 scope 的 `unreadSummary`
   - agent 如需更多上下文，可在**收到显式通知后**再显式调用 `read_history()`
   - `unreadSummary` 只可在没有 `wakeMessage` 的 idle/resume 未读摘要里使用，不应单独触发 agent 自动拉起，也不应要求 agent 主动扫描 thread / channel 历史
   - 这种实现方式在本版 spec 中是允许的；spec 约束的是上下文面与行为一致性，而不是强制 server-side preload
3. 当前 wakeup / delivery explainability 已有 diagnostics API + product affordance；后续剩余工作主要是可选 richer audit surface 与 context preload 优化，而不是主链路行为缺口。

当前主链路重点仍是保持“通知边界最小化 + 上下文面一致”。

## 3. 设计原则

### 3.1 显式派工优先

在 agent-heavy channel 中，默认应以“显式指派”替代“广播式旁听”。

### 3.2 task 是执行主线

消息用于：

- 发起需求
- 讨论方案
- 澄清问题
- 审核结果

task 用于：

- 拆解工作
- 领取工作
- 表达 ownership
- 表达执行状态

### 3.3 线程承载局部上下文

主 channel 保留高层协调。
具体实现细节、某个 task 的跟进、某个 agent 的局部讨论，优先进入 thread。

### 3.4 可见性必须跨 machine

workspace 内的 agent 视图必须是 workspace-global，而不是“本机视角”。
agent 必须能看见：

- 这个 agent 在不在
- 在哪台 machine
- 大致是否在线/忙碌

### 3.5 通知边界最小化，上下文面一致

本 spec 明确区分两件事：

- 哪些 agent 应该被通知
- 被通知 agent 在开始处理前应该拿到多少上下文

原则：

- **通知边界仍应最小化**：如果没有命中某个 agent，该消息就不应进入该 agent 的系统级 delivery / unread / wake pipeline
- **上下文面必须一致**：一旦某个 agent 被明确命中并开始处理，它所使用的上下文应来自正确的 channel / thread / DM 协作面，而不是只凭当前一条消息文本
- **实现方式不是 spec 约束点**：上下文可以由 server/runtime 在 wake 时预载，也可以由 agent 收到通知后通过 `read_history()` 获取；spec 关注的是行为一致，而不是 transport 形态

结论：

- `@mention` 是“谁需要处理”的边界
- 但不是“这个 agent 只能看到哪些消息”的边界
- `read_history()` 是标准上下文读取工具，可以成为 direct mention / wake 后正常处理路径的一部分

### 3.6 上下文面不能递归展开

`context scope` 必须严格受当前协作面约束，不能递归把子上下文一起带下去。

边界规则：

- `channel context`
  - 只包含该 channel 的**一级消息历史**
  - **不包含**该 channel 下各个 thread / reply-subchannel 的消息
- `thread context`
  - 只包含该 thread 自身的消息历史
  - 可以附带 parent channel / parent message ref
  - **不包含** parent channel 的整段一级消息历史
- `dm context`
  - 只包含该 DM 主线历史
  - 如果未来 DM 也支持 reply thread，则 DM 主线 context 同样**不递归包含**其 thread 消息

结论：

- channel mainline 和 thread 是两个独立上下文面
- “给足上下文”不等于“把所有子线程都递归打包下发”
- 如果 agent 后续确实需要查看某个 reply thread，系统应提供显式读取该 thread 历史的接口
- 这类 thread 历史读取属于 agent 主动 pull，不属于 server 在 channel wake 中主动 push

### 3.7 若未来做预加载，上下文窗口应有上限

`context scope` 定义的是**正确的协作面边界**，不等于未来的 wake payload 必须无限展开。

这部分是**可选实现优化**，不是当前 spec 的主线硬要求。

默认规则：

- 如果未来选择在 wake 时预载上下文，server/runtime 应优先直接下发该 `context scope` 内**最近的 50 条消息**
- 如果 agent 处理当前消息仍需要更早历史，可再通过 `read_history()` 主动回拉更早消息
- 这个“50 条默认窗口”适用于：
  - channel mainline context
  - thread context
  - DM context

约束：

- 50 条窗口必须仍然严格受 `context scope` 边界约束
- channel wake payload 的 50 条，只能来自该 channel 的一级消息
- thread wake payload 的 50 条，只能来自该 thread 自身消息
- 不允许为了“多给点上下文”而跨 context surface 混装消息

优先级说明：

- 当前 spec 只要求上下文行为一致，不强制先实现 wake payload 预加载
- 如果未来引入预加载，再把默认 wake payload 限制为最近 50 条，属于次优先级的 transport / scaling 优化

## 4. 消息意图与路由语义

本 spec 不再采用 channel 级 `open / work` 模式。

原因：

- channel 级模式过粗，不自然
- 用户很难判断何时该切换模式
- 真正决定 token 成本的不是 channel，而是单条消息的意图

本 spec 改为按消息意图路由。

### 4.1 三个核心概念

每条消息都需要区分三个维度：

- `recipient scope`
  - 哪些 agent 应当收到这条消息
- `wakeup scope`
  - 哪些 agent 现在就应被立即唤醒处理
- `context scope`
  - 当某个 agent 处理该消息时，应以哪一段会话上下文作为理解来源

补充：

- `context scope` 先定义“上下文属于哪个协作面”
- 具体上下文如何获得是实现细节：可以是 wake payload 预载，也可以是 `read_history()` 按需拉取
- 如果未来做 wake payload 预载，默认可只携带该协作面内最近的 50 条消息
- 如果需要跨到某个 reply thread 查看局部上下文，也应通过显式 history read 按 thread / parent ref 拉取，而不是由 server 隐式夹带下发

这样才能同时表达：

- durable current-event fanout
- live wakeup
- context-consistent handling
- backlog-only task
- live broadcast only to online agents

### 4.2 普通消息

定义：

- 不带 agent `@mention`
- 不是受控广播
- 不是 explicit recipient
- 不是任何当前启用的结构化聊天命令

如果消息中有 live `@agent`，它就不是本节的普通消息，而是 §4.3 定向消息；被 mention 的 agent 按 direct mention 规则投递和唤醒。

行为：

- `sparse recipient scope = none`
- `direct wakeup scope = none`
- `context scope = 当前 scope`
- 不创建 direct mention、broadcast 或 explicit recipient delivery event
- public agent 是否进入当前事件 daemon delivery / auto-wake eligibility，由 scope membership、notification setting 和 policy 决定；可读历史仍由 read_history/search visibility 决定
- private agent 是否看到该消息，由 owner-private authority 和 visibility filter 决定
- 发送端可以选择“同时开启 reply thread”；这只创建 / 打开以该 channel 消息为 root 的 reply thread 并订阅发送者，不改变普通消息的 recipient / wakeup / context 规则

结论：

- channel 中的普通聊天不产生 direct mention 或 broadcast sparse delivery event
- live `@agent` 按 §4.3 定向消息处理，会进入对应 direct mention 投递/唤醒规则
- public agent 是否通过普通聊天 receive / auto-wake，由 [`specs/message/scope-model.md`](../message/scope-model.md) 定义的 scope membership、notification setting、cursor 和 policy 决定
- private agent 是否通过普通聊天 receive / wake / read，由 owner-private authority 和 VisibilityFilter 决定
- 普通消息不得写 per-recipient sparse delivery row 来模拟 default/broad 投递
- 这是本 spec 的第一条降 token 规则

### 4.3 定向消息

定义：

- 一条消息中包含一个或多个 `@agent`

行为：

- `recipient scope = 被 mention 且属于发送者的 agent`
- `wakeup scope = 当前在线、被 mention 且属于发送者的 agent`
- `context scope = 当前协作面的完整可见上下文`
- 不在线的被 mention agent 上线后也应看到并收到这条消息

补充：

- 多个属于发送者的 agent 可以同时被 mention
- human mention 不改变 agent 投递集合
- 对不属于发送者的 `@agent`，系统只按展示文本处理，不进入 recipient scope，也不触发 wake
- Markdown code（fenced code block 或 inline code span）中的 `@mention` 视为字面文本，不进入 recipient scope，也不触发 wake；同理，`@all` / `@online` 在 Markdown code 中也只是展示文本
- Markdown blockquote（以 `>` 开头的行）中的 `@mention` 同样视为字面文本，不进入 recipient scope，也不触发 wake；`@all` / `@online` 在 blockquote 中也只是展示文本。canonical 语法见 §4.3.bis
- 未被 mention、或虽被 mention 但不属于发送者的 agent 必须静默
- 对于被 mention 的 agent，后续处理应基于该协作面的完整可见上下文，而不是只看 mention 到它的片段消息

Agent-authored message 的 direct mention 和 human-authored direct mention 一样是 wake 语义：平台不能因为发送者是 agent 就把 `@agent` 降级为 no-wake。防止 agent wake cascade 的责任在 runtime 行为语义：被 agent-authored mention 唤醒后，应处理实质请求 / handoff，但不要为了确认收到、确认接手、说自己暂停、或要求对方暂停而发送可见消息；轻量确认用 reaction，有结果、阻塞、决策请求或新信息时再发消息。

上下文行为规则：

- 如果 mention 发生在 channel 主线：
  - agent 处理该消息时，应以该 channel 的**一级消息完整可见历史**作为上下文来源，而不是只看当前一条 mention 消息
  - 这份 channel context **不包含**该 channel 下各个 thread / reply-subchannel 的消息
  - 实现上可以由 wake payload 直接附带，也可以由 agent 按需 `read_history(target=\"#channel\")` 拉取
  - 如果未来使用 wake payload 预载，默认应只带该 channel 一级消息里最近的 50 条
  - 如果 agent 之后需要查看某个 reply thread，系统应允许它按 thread / parent ref 显式读取该 thread 历史；但 server 不应把这些 thread 消息主动塞进 channel mainline context
- 如果 mention 发生在 thread：
  - agent 处理该消息时，应以该 thread 的完整可见历史作为上下文来源，并可拿到父 channel / parent message 引用
  - 这份 thread context **不包含** parent channel 的整段一级消息历史
  - 实现上可以由 wake payload 直接附带，也可以由 agent 按需 `read_history(target=\"#channel:shortid\")` 拉取
  - 如果未来使用 wake payload 预载，默认应只带该 thread 最近的 50 条
- 如果 mention 发生在 DM：
  - agent 处理该消息时，应以该 DM 的完整可见历史作为上下文来源
  - 实现上可以由 wake payload 直接附带，也可以由 agent 按需 `read_history(target=\"dm:@peer\")` 拉取
  - 如果未来使用 wake payload 预载，默认应只带该 DM 最近的 50 条

重要澄清：

- direct mention 是**通知边界**
- 不是“上下文只允许包含 mention 到我的消息”的边界
- “收到 wake 后再继续 `read_history()`”是允许实现；关键是最终使用的上下文面必须正确且一致

发送时同时开启 reply thread 的补充规则：

- channel 主线 direct mention 可以和“同时开启 reply thread”同时使用。
- public agent mention 仍按普通 channel direct mention 投递和唤醒；新 reply thread 是由该 root message 派生的协作面，不把 public agent delivery 改写成 thread delivery。
- owner-private / private-agent mention 保持既有 started-thread 语义：被 private mention 命中的 recipient 订阅新 reply thread，opener delivery 面向这些 private recipient 时使用新 reply thread 作为 target；父 channel 中仍保留一条 root message，新 thread 初始不复制 root message。

### 4.3.bis Blockquote 作为 routing-token 的 inert 语法（canonical）

Markdown blockquote 是 AgentTeam 支持的第三类 **routing-token inert** 语法（与 fenced code block、inline code span 并列）。本节钉死 blockquote 在路由层的识别规则，避免不同实现各自解释 CommonMark 产生漂移。

**覆盖的 routing token 类别**：

- `@<name>` 个体 mention
- `@all` / `@online` 广播
- 当前启用的 structured command（如未来新增）也必须在 blockquote 中保持 inert，不触发创建 / 更新 / 调用流程

以上所有类别落在 blockquote 中均视为**字面文本**，不产生 routing / wake / command 副作用。

canonical 规则：

- **Tight line-prefix 语法**：一行只有在**物理行首**（可选前导空白后）紧跟 `>` 字符，且 `>` 之后是 U+0020 space、U+0009 tab、另一 `>` 或行尾，才视为 blockquote 标记行
  - `> @name` → inert
  - `>@name` → **不是** blockquote（无 space/tab/`>`），按普通文本处理，`@name` 正常路由
  - `> =some expression` → blockquote（`>` 后有 space）；注意 `>=` 紧贴形式不是 blockquote
  - `>> @name` / `> > @name` → 嵌套 blockquote，同样 inert
- **不支持 lazy continuation**：CommonMark 中 blockquote 允许下一行不带 `>` 仍归属同一 blockquote；AgentTeam routing 层**不**识别 lazy continuation——每一物理行的 blockquote 归属独立判定。未带 `>` 前缀的行即使紧邻一段 blockquote，也按普通文本处理，其中的 `@mention` 正常路由
- **空行终止 blockquote**：连续的 `>` 行构成一整段 blockquote；其中，仅有一个 `>` 字符（可选尾随空白）的行视为 blockquote **内部的显式空段**，不终止 blockquote；只有完全空行（无任何字符、且不以 `>` 开头）才终止。之后即使再出现 `>` 行，也算新的 blockquote。该规则与 service event delivery 的 external-payload auto-wrap 生成策略一致：auto-wrap 在 external 文本原空行处写出 `>`，以保持 blockquote 连续
- **嵌套**：一行可以有多个 `>` 前缀（`>>`、`> >` 等效），每多一个 `>` 表示嵌套一层；嵌套 blockquote 内的 `@mention` 仍 inert
- **与 fenced / inline code 的合取语义**：一个 token 只要落在 fenced block / inline code / blockquote **任一**之内即视为 inert；三种 inert 语法叠加时相互兼容，不需要优先级裁决
- **`@all` / `@online`**：在 blockquote 中同样只是展示文本，不构成受控广播
- **structured command**：blockquote 内的命令形态只作字面展示；与 §4.3.bis 开头列出的 routing token 类别一致处理

写入时的实现要求：

- 服务端在解析 raw content 的 `recipient scope` 与 `wakeup scope` 时，必须按上述规则剔除 blockquote 内的 mention token；对齐 §6 第 1 条
- 不得依赖客户端渲染后的 HTML 来做 routing 判断

auto-inert 使用场景：

- 本规则的直接用户之一是 service event delivery render pipeline——provider external payload（GitHub / Gitea PR title / review body / comment body 等）在 render 时由 App/service event delivery pipeline **自动**包裹为 blockquote，保证外部 attacker-controlled 文本不会携带 routing 信号唤醒其他 agent
- 普通 human 或 agent 如果希望在消息中引用一段可能含 `@...` 的外部文本而不触发 routing，也可以手工加 `>` 前缀

### 4.4 受控广播

本 spec 支持两种 agent 广播：

- `@all`
- `@online`

二者都只针对 agent，不针对 human。

human 本来就在 channel 中，不需要“特殊唤醒”的额外协议。

#### `@all`

行为：

- `recipient scope = 当前 channel 中已有 membership、且属于发送者的全部 agent`
  - 当前 channel 的 public agent membership 与 owner-private agent membership 都在候选内
  - 不在当前 channel 的 agent 不会被投递，也不会因为广播自动创建 membership
- `wakeup scope = recipient scope 中 machine / daemon 当前可连接的 agent`
  - 这里的“可 wake”以 machine / daemon connected 为准，不要求 agent runtime 已经 active
  - 如果 machine / daemon connected，即使 agent runtime 处于 offline / inactive，也应进入 wake/start 列表并被拉起
- `context scope = 当前 channel 的完整可见上下文`
- 这里的 channel context 仅指该 channel 的一级消息，不包含其 thread 消息
- machine / daemon 当前不可连接的 agent 之后上线时，也应补收到这条消息

#### `@online`

行为：

- `recipient scope = 当前 channel 中已有 membership、属于发送者、且 machine / daemon 当前可连接的 agent`
  - public agent membership 与 owner-private agent membership 都在候选内
  - 不要求 agent runtime 已经 active；connected machine 上的 offline / inactive agent 也应被 wake/start
- `wakeup scope = recipient scope`
- `context scope = 当前 channel 的完整可见上下文`
- 这里的 channel context 仅指该 channel 的一级消息，不包含其 thread 消息
- machine / daemon 当前不可连接的 agent 之后不上补这条消息

#### 权限与保护

`@all` / `@online` 不是普通 mention，而是受控广播能力。

当前规则：

- 不引入跨 ownership 的额外广播权限
- 任何能在当前主 channel 发消息的 human，都只能用它命中自己名下、且已经在当前 channel 中的 agent
- private channel agent 只在 owner 自己发起广播时可被命中；其他 owner 的 public / private agent 都不能被唤醒
- 发送前必须经过 UI 二次确认
- UI 必须显式提示这是一种高 token 成本操作

#### 广播后的默认回复策略

广播只解决“谁收到”，不解决“谁应该公开回复”。

默认规则：

- `@all` / `@online` 不等于“所有 agent 都应回复”
- 它们属于**非直接指定消息**
- agent 收到广播后，应先判断是否已有明确 owner / 已有 task / 已有人 claim
- 如果已有 owner 或已有 task 已被 claim / `in_progress` / `done`，应避免重复公开汇报
- agent 只有在这些情况下才适合公开回复：
  - 它随后被继续单独 `@mention`
  - 它准备立即创建或 claim 一个 task
  - 广播明确是在征集认领者
  - 广播明确要求多个独立输出
- 如果公开回复不能新增有效信息，优先继续通过 task 和 thread 收敛协作，而不是堆出一串重复回执

离线补收规则：

- 因 `@all` 补收到历史消息的离线 agent，如果相关 task 已被 claim / `in_progress` / `done`，仍应避免补发冗余公开回执

#### thread 限制

当前阶段：

- thread 中暂时不允许 `@all`
- thread 中暂时不允许 `@online`

如果要做广播，必须回主 channel 发起。

## 5. Thread 语义

### 5.1 参与者继承

当前 spec 采用“线程参与者继承”，不要求“每条都重新 mention”。

规则：

- 如果某个 agent 因为主 channel 中的定向消息进入 thread，或已经在该 thread 中发过言，则它成为 thread participant
- thread reply 默认投递给当前 thread participants
- 在 thread 中新增 `@agent` 是允许的，并且会把该 agent 加入 thread participants
- 未参与该 thread、也未被新增 mention 的 agent 不接收 thread reply

理由：

- 避免 channel 级广播
- 避免人在 thread 中每句都重新 `@agent`
- 保持局部上下文自然延续

### 5.2 thread 中的定向消息

thread 中允许普通 `@agent`。

行为：

- 被 mention 且属于发送者的 agent 成为新的 thread participant
- 当前在线的会立即收到
- `context scope = 该 thread 的完整可见历史 + parent ref`
- thread context 只包含该 thread 自身消息，不递归包含 parent channel 的整段主线历史
- 当前不在线的之后上线也应补收到

### 5.3 thread 中的广播限制

thread 中暂时不支持：

- `@all`
- `@online`

这样可以避免 thread 破坏主 channel 的广播边界。

### 5.4 DM

DM 继续保持当前语义：

- DM 两侧相关 agent 正常可达
- 不使用 `@all` / `@online`

## 6. Mention 作为一等协议

当前仅靠前端字符串替换不够。

当前 spec 要求把 mention 提升为服务端可解释的协议层事实：

1. 消息写入时解析 raw content 中的 routing token（`@name` 个体 mention、`@all` / `@online` 广播，以及任何当前启用的 structured command），但 Markdown code（fenced 或 inline）与 Markdown blockquote（以 `>` 开头的行，canonical 语法见 §4.3.bis）内的上述 token 均只按字面文本处理，不参与路由、不触发 structured command 执行
2. 仅对当前 channel / thread 中可达的 agent member 视为有效 mention
3. 多个 mentions 全部保留
4. 消息持久化时保存 `recipient scope`
5. 如有立即投递，还要保存对应的 `wakeup scope` 语义结果
6. 当前事件 delivery / automatic continuation / wake 都基于持久化后的 routing fact 和 daemon accepted payload，而不是事后重新猜测或补扫历史 unread
7. 未命中 `recipient scope` 的消息，不得进入对应 agent 的系统级 delivery / wake pipeline，也不得通过历史 readable backlog 伪装成待处理事件
8. 命中 `recipient scope` 的消息，在处理时必须以对应 `context scope` 作为上下文来源，而不是把 mention 语义退化成“只看当前消息文本”
9. `read_history()` 是标准上下文读取工具，可作为处理一条新 mention 的正常路径；如果未来引入预加载，二者也应指向同一 `context scope`
10. `channel context` 只包含一级消息；`thread context` 只包含该 thread 自身消息；二者不得递归混装
11. 如果未来引入 wake payload 预加载，默认应限制为对应 `context scope` 内最近的 50 条消息；更早历史再通过 `read_history()` 按需补拉
12. 系统应提供显式的 thread history read 能力，使 agent 可按 thread / parent ref 按需读取 reply thread；此能力不改变 delivery / unread / wake 事实，也不应让 server 主动推送 thread 消息
13. `unreadSummary` 只可用于没有 `wakeMessage` 的 idle/resume 未读摘要；显式 wake prompt 不应展示其它 scope 的未读摘要。没有新的 `wakeMessage` / routed delivery 时，agent 应保持静默，不应因 unread backlog 自行发起 catch-up、claim、回执或新的公开回复

不建议的做法：

- 只在 daemon delivery / local continuation 时临时重新 parse 文本

原因：

- agent rename / membership change / online state 变化后会导致历史投递语义漂移

## 7. Task 的边界

当前 spec 的职责边界应明确为：

- `channel`：协作协调面
- `task`：执行 ownership 面
- `thread`：某次派工 / 某个 task 的上下文面

task 继续保持 **channel-bound**，不引入 workspace-global task object。

如果需要“全局 backlog”，当前建议直接使用 `#all` channel，而不是额外造一个 workspace 级任务系统。

### 7.1 Task 创建不是普通聊天广播

Task 创建、assignee mutation 与 discussion attention 不应退化成普通聊天广播。

它们是 Task-native 结构化动作：

- 创建 Task 只有一个 operation，可选 `assigneeRef`；
- assign / claim / reassign / unassign 写 canonical assignee fact 与 typed Activity；
- Message 与 Activity 各自保存稳定 attention recipient snapshot；
- Agent delivery 使用 structured Task packet，不通过 hidden dispatch Message 或聊天 slash command。

### 7.1.1 Task attention contract

一条 committed Task event 的 recipients 是：

```text
explicit watchers ∪ current assignee ∪ current Message direct mentions − actor
```

Human recipient 获得聚合 Task Inbox unread；Agent recipient 获得 delivery /
wake。Typed Activity 是事实、Message 是协作内容、attention 是投影，三者
不能互相冒充。

约束：

- participant / creator / commenter 不因 membership 自动收到通知；
- direct mention 和 assignee attention 可以穿透 Unwatch，但不改变持久 Watch；
- Message paired `discussion_added` Activity 不产生第二次 fanout；
- packet 必须暴露 canonical Task scope 作为回复 target；
- channel 与 source thread 只提供 context / provenance，不能作为缺失 Task target 时的隐式回复兜底。

### 7.2 Create with assignee / claim

- Create 可选 Human 或 Agent assignee，不含 assign/start mode；
- claim 是当前 principal 把未指派 Task 设给自己，不能静默抢走其他 assignee；
- status 与 assignee 独立；create / claim 不隐式推进 status；
- Agent assignee 若不是 actor，会通过 assignment Activity attention 收到 structured packet；
- actor exclusion 防止创建者兼 assignee 时 self-wake。

### 7.3 backlog task

行为：

- 在当前 channel 创建 backlog task
- 不通知任何 agent
- 不模糊唤醒 agent
- 需要后续显式 assign、claim、Watch 或 direct mention

### 7.4 thread 来源 task

如果 task 从 thread 上下文创建：

- task 仍归属父 channel
- 不归属 thread
- 但应保存来源 thread 的链接，用于回看上下文

## 8. Task 面板与 review surface 边界

详细的 board column、task detail、review surface、artifact、review request 语义，统一由 [`specs/task/board.md`](../task/board.md) 定义；workspace task hub 与 channel `List` / `Board` 的 browse/query/pagination contract 统一由 [`specs/task/board-pages.md`](../task/board-pages.md) 定义。

本协作 spec 只保留与协作边界直接相关的 task 规则：

- task 面板仍以 channel 为默认边界
- nullable `assignee` 是唯一当前责任事实，Human / Agent 共享同一模型
- task 保存不可变的 source thread lineage 作为上下文入口；讨论归 task 自身的 `tsk_` scope，不存在 primary thread 概念
- task 变化默认仍然 task-native；如需在 thread 或 channel 中广播，应显式执行
- Review Request reviewer 是独立 review fact，不是 Task 行责任字段
- Watch 只表达订阅，assignee 表达责任；两者命中同一 member 时 recipient 去重

## 9. 推荐协作流

### 9.1 普通聊天

- human 正常聊天
- 不带 `@agent` 时，agent 完全静默

### 9.2 定向协作

1. human 用 `@agent` 明确点名
2. 只有被点名且属于发送者的 agent 收到
3. agent 按该协作面的正确上下文面处理该消息；实现上可以直接预载，也可以随后 `read_history()` 补齐
4. agent 简短响应
5. 需要执行时尽快落成 task

### 9.3 backlog 协作

1. human 在相关 channel board/API 上创建 backlog task
2. 如果没有 mention，则不主动唤醒 agent
3. backlog 通过 `#all` 或后续定向 mention 被消费

### 9.4 广播协作

1. human 使用受控广播 `@all` / `@online`
2. 这是高成本操作，必须有确认
3. 广播不等于强制集体回复
4. 作为非直接指定消息，agent 需结合上下文自行判断是否该公开回复
5. 如确需回复，应尽量避免群体性重复回复

### 9.5 审核与收口

- agent 完成后应发起 task-native review request，并让 task 进入 `in_review`
- human review 的主要工作面是 task 自己的 review surface；thread 或 channel 主要用于讨论与补充上下文
- 通过后转 `done`；要求修改则回到 `in_progress`
- 如果某条广播消息在某个 agent 补收时对应 task 已被 claim 或 done，则该离线 agent 应避免补发冗余公开回执

## 10. Owner quote

`owner_quote` 是 human 在普通协作面上整理并引用来源消息的显式消息产物。

它的目标不是“再复制一次原消息列表”，而是：

- 把多个来源消息收敛成一条可发送、可审核的 human-authored quote message
- 让 human 直接编辑真正重要的人类语义，而不是被协议字段淹没
- 把 pending 提示集中到当前协作面的统一入口，而不是挂在每条来源消息上制造噪音

### 10.1 artifact model

- 当前持久化 / delivery 的具体 message type 是 `messageType = owner_quote`
- `owner_quote` 是一条 concrete ordinary message：它属于目标 channel / DM / thread 的 transcript，而不是 task-owned source/context record
- 来源 provenance 由该 message 的 source refs / `scope_message_sources` 维护；不得再引入独立 task context source/add record、hidden delivery carrier、第二份 transcript copy row 或 publication mirror
- 产品 UI 默认应使用 `Owner quote` / 引用消息语义，不再使用退役的 share/context-source/add 文案

### 10.2 editable surface contract

- raw draft 是默认处理路径
- human 默认进入的是一整个可编辑草稿，而不是逐条消息勾选器
- 当前 raw draft 的主编辑区只允许直接编辑两块可见内容：
  - `Body:`
  - `Quoted messages:`
- `source message refs`、target agent、delivery reason、message type 等机器语义应保留为系统维护的 metadata，而不是主编辑区的一部分
- current writers must only emit the canonical labels above; retired share-context labels are compatibility data only and must not be emitted by current UI/API code

### 10.3 surface rules

- channel surface：
  - 当该协作面存在可处理的 pending quoted messages 时，显示与 `Chat / Tasks` 同层的 lightweight quote entry
  - 没有 pending 且当前不在 context surface 时，应隐藏这个 tab
- thread surface：
  - 不再使用 banner、卡片、显眼提示
  - 只在 thread header 提供一个轻量入口图标
- pending 提示必须按 surface 聚合，而不是逐条消息高亮
- channel surface 仍要求当前 channel 有 owner-manageable agent 可作为当前协作面的处理对象，否则不应显示 pending quoted-message 提示
- thread surface 允许 owner 手动选择 quote 目标：只要当前 thread 有 pending quoted messages，且 owner 在 parent channel 仍有可管理 agent，即使这些 agent 还没有参与该 thread，也可以在 thread header 的 `...` 中显示 owner quote 入口
- 当前实现允许在 quote surface 顶部显示只读摘要卡，展示 scope、quoted source 数、author 数、target 数等信息；这些信息属于说明，不属于主编辑字段

### 10.4 processing contract

- 打开 quote surface 时，系统应把当前 pending quoted messages 收敛成一份 raw draft
- 发送后生成一条 human-authored `owner_quote` message
- `owner_quote` 的递交 / 唤醒规则必须复用普通消息的 mention contract：
  - 没有出现在最终 owner quote 内容中的 agent mention，不应被默认递交或默认唤醒
  - owner 最终保留下来的 quoted messages 中，如果包含其他 human 已写出的 agent mention，这些 mention 仍属于有效递交目标
- 当前 pending set 必须支持一次性 `dismiss all`
- 已被 fold 进 owner quote 的 source message，需要保留可解释的 trace affordance，例如 `Used in owner quote`
- 如果当前 surface 没有 pending quoted messages 且也没有有效 draft，UI 应回到普通 chat surface，而不是停留在空白处理页

## 11. Task 协作边界的当前判断

Task 的详细产品方向现在由 [`specs/task/board.md`](../task/board.md) 与 [`specs/task/board-pages.md`](../task/board-pages.md) 共同定义。

当 task 从来源 thread 创建或绑定时，来源关系由 [`specs/task/embedded-view.md`](../task/embedded-view.md) 定义：thread 保持安静，只保留显式 task reference、普通回复或 task announcement；task detail / task activity / task discussion 继续作为结构化单一事实来源。

本协作 spec 不再重复定义：

- board column 细节
- task detail 信息密度
- review request 数据模型
- review surface / artifact / checklist 的产品 contract

本协作 spec 保留的 task obligations 只有：

1. task 继续保持 channel-bound
2. task create 只有一个 operation，可选 assignee；后续 assign / claim 与
   Task discussion 分别写 canonical Task fact / Message，并统一通过
   `Watch ∪ assignee ∪ direct mention − actor` 的 Task attention 投递，而不是
   维护 create mode、start/dispatch 特例，或把 task 当作普通聊天文本
3. task 与 thread 的边界保持清晰：task 是 ownership/object，thread 是局部上下文面；task discussion 是默认 post-create follow-up surface
4. task-native 变化默认不自动污染聊天，但允许关键节点显式 announce
5. 一旦 task 已创建，默认后续协作应优先沉淀在 task discussion / task detail，而不是要求回到 thread 才能继续执行
6. task discussion 保持 flat；如果协作需要 branching、局部子话题或更广可见性，复用现有 channel/thread，而不是在 task detail 内再做一套 nested reply tree

## 12. Cross-Machine Agent Presence

`agentteam exec workspace list` 的 agent 信息至少应补齐：

- `agent name`
- `status`
- `machine display name`
- `machine host / machine id` 的可读摘要
- `owner human` 或创建者
- 可选：`activity`

示例目标输出：

- `@jett-agent1 (online, machine=jett-macbook, owner=@jett)`
- `@wink-agent1 (idle, machine=wink-linux, owner=@wink)`

当前阶段目标不是做完整 observability，而是避免 agent 产生“只看得到本机代理”的错误心智模型。

## 13. 建议的实现顺序（按 2026-03-23 对齐）

### Phase 1 — 已完成

1. 服务端写消息时解析 agent mentions
2. 为消息持久化 `recipient scope`
3. 实现普通消息默认静默
4. 实现定向消息 durable recipient + online wakeup
5. 实现 thread participant inheritance
6. `agentteam exec workspace list` 补 machine / owner / richer status

### Phase 2 — 已完成

1. 实现受控广播 `@all / @online`
2. 接入权限判断与二次确认
3. 明确文案：广播是高 token 成本操作
4. thread 中禁止 `@all / @online`

### Phase 3 — 已完成

1. 实现早期 `/task` 结构化入口 —— 已完成，后续已被 task board/API create flow 取代
2. 增加 source thread link —— 已完成；现已进入 task domain / API / UI
3. 在 channel 中落系统记录 —— 已完成，当前不再由聊天 `/task` 入口创建
4. 进一步优化 task-native 协作 UX —— 已完成

### Phase 4 — Canonical Task assignee / attention cutover

1. Task responsibility 收敛为 nullable `assignee`；删除 current agent、claim owner、suggested assignee 与 Task-row reviewer aliases。
2. Create 收敛为一个可选 assignee 的 operation；claim 成为防 takeover 的 self-assignment。
3. Watch / Unwatch 复用 Scope notification；membership 不自动订阅。
4. Task event recipients 统一为 Watchers、当前 assignee 与本条 Message direct mentions，排除 actor。
5. Human recipients 使用统一 Inbox Task row / unread；Agent recipients 使用同一 snapshot delivery / wake。
6. Message 与 paired Activity 不 double fanout；pure Activity 不合成 Message。

### Phase 5 — operator diagnostics 已完成；剩余仅可选优化

#### Step 5.1：operator diagnostics affordance —— 已完成

- 目标：把现有 diagnostics contract 接到更易发现、也更易扫读的 operator 入口。
- 包含：
  - 复用 `GET /api/messages/:messageId/delivery`
  - message row / system message 可直接打开 diagnostics modal
  - modal 顶部补 server-owned summary（targeted / excluded / wake deliver / wake start / wake blocked）
  - per-agent card 继续由 server contract 提供 `scopeStatus` explainability，区分“仍在当前 scope”与“只因 historical fanout payload/status 保留的历史 recipient”
  - routing / context scope / wake inference / per-agent delivery+wake explanation 继续由 server contract 提供，web 仅渲染并保留 raw code 作为次级 debug 信息
  - 不复制 explainability 逻辑
- 验收：
  - operator 可在不查库的前提下查看 message delivery / wake explainability
  - operator 打开 modal 后可先看 summary，再按 agent 卡片下钻细节
  - operator 不必手动翻译 raw enum token，也能直接理解 routing / delivery / wake 含义

#### Step 5.2：optional context preload exploration（可选）

- 目标：如果未来决定做 wake payload 预加载，确保它与当前 `read_history()` 行为保持一致。
- 包含：
  - 预加载仍严格受 `context scope` 约束
  - channel mainline 不递归混入 thread
  - 如采用预加载，默认窗口可限制为最近 50 条
- 验收：
  - 无论采用 preload 还是 `read_history()`，agent 看到的上下文面一致

## 14. 非目标

本轮不做：

- org 替代 workspace 作为消息边界
- 让所有 agent 自动订阅所有 channel
- 未被命中的 agent 的“旁听式全量订阅”
- 把 task 变成 workspace-global object
- 重做 task 状态机
- 完整在线监控面板
- 跨 workspace 的统一 agent presence

## 15. 验收标准

满足以下条件即可认为当前 spec 方向正确：

1. 一条普通消息在 channel 中不会唤醒任何 agent
2. 一条消息可同时 `@` 多个属于发送者的 agent，且只有这些 agent 收到
3. `@all` 会在当前 channel 内 public/private membership 中筛出发送者自有 agent 形成 durable broadcast，并 wake/start 其中 machine connected 的 agent；`@online` 只作用于发送时 machine connected 且属于发送者的当前 channel agent
4. thread 跟进只投递给 thread participants / 新 mention 且属于发送者的 agent，且 thread 中不能发 `@all / @online`
5. task create / assign dispatch 会创建 task 并通知明确目标 agent，但 assign 不自动 claim
6. backlog task 不主动唤醒 agent
7. agent 可从 `agentteam exec workspace list` 判断其他 agent 属于哪台 machine，而不是误以为“当前 workspace 里没有”
8. 被唤醒 agent 在处理消息时能稳定获得对应 channel / thread / DM 的正确上下文面；实现上可以直接预载，也可以后续 `read_history()` 获取
9. `@all` / `@online` 作为非直接指定消息，由 agent 结合上下文自行判断是否公开回复
10. channel mention 的上下文只包含 channel 一级消息，不包含 thread 回复
11. 如果未来引入 wake payload 预加载，默认窗口应限制为对应上下文最近 50 条；更早历史可按需回拉
12. agent 如有需要可显式读取指定 reply thread 的历史，但 server 不主动推送这些 thread 消息
13. 多 agent 协作时，重复响应和重复执行显著减少
