agentscope-ai / agentscope-ai/AgentTeams

Design: task-lifecycle attention events & channel routing (unified view for #1206 and #1219)

Ouverte
#1,229 1 commentaire 0 réactions 0 personnes assignées Voir sur GitHub
Langage dominant
Go
Étoiles
5.6k
Forks
692
Merge moyen
5 j 4 h
PR mergées (30 j)
23

Description

# Design: task-lifecycle attention events & channel routing (unified view for #1206 and #1219)

Related: #1206 (Part A implementation) · #1219 (Part B implementation) · #1220 (L2 permission & capability model) · #1217 (L2 team-scoped workspace access) · #1177 (community proposal: lost completion wake-up — directly addressed by this design)

Per the review feedback on both PRs, this issue consolidates the shared design they depend on: **when a team event needs a human, how is that human reliably reached, through which channel, and who may configure those channels?**

## 1. Background: one root cause, two production gaps

### 1.1 The reporting gap (motivates #1206; answers #1177)

Lifecycle asymmetry (code-level fact on current main):

- `delegate_task` writes task state, syncs shared storage, and sends a room notification **in code** (stable txn id, membership check, m.mentions).
- `submit_task` writes task state and publishes artifacts, but sends **no notification in code** — the response carries only a `notificationNeeded` hint. The completion message (and the @mention that wakes the Leader) is a separate LLM tool call.

Consequence (reproduced in production and independently in #1177): if the worker's turn or process stops between `submit_task` and the follow-up message, the Leader is never woken; downstream tasks stall at `submitted`/`waiting`; the only completion wake-up is lost with no recovery. The same asymmetry class covers every other "needs a human" moment — blocked tasks awaiting approval, mid-task decisions, external requests an agent cannot resolve alone. None of them has a code-level delivery path, so all of them depend on prompt compliance.

Prompt-level mitigations are a behavior layer, not a delivery layer: they decay with attention, session length, and process lifetime. The design principle adopted here:

> **Any state transition that changes "who acts next" must have a code-level delivery side effect — delivery is guaranteed, or the operation is retryable.** The LLM layer owns the *quality* of reports (conclusions, context); the code layer owns the *existence* of the signal.

### 1.2 The channel onboarding gap (motivates #1219)

Connecting an agent to a messaging channel (QQ / Matrix / DingTalk / WeChat / ...) has required SSH + `docker exec` + hand-editing `agent.json` on the host. No L1 admin or L2 team user has an in-band path. This matters for Part A: the "user's own channel" half of dual-channel reporting cannot exist for end users if users cannot self-serve channel onboarding.

### 1.3 Why one issue

Channel routing for attention events (Part A) depends on which channels exist and who may configure them (Part B). One design umbrella: **event → routing → channel → access control**.

## 2. Part A — Task-lifecycle attention events (#1206)

### 2.1 Event matrix

| Event | Trigger | Sender | Recipients | Default channels | Idempotency key | Implementation |
|---|---|---|---|---|---|---|
| Task assignment | `delegate_task` | Leader | assignee | room @assignee | `delegate-{task}` | shipped |
| Task terminal (any accepted result status) | `submit_task` | Worker | team leader + human members of the task room | room @mention | `submit-{task}-{result_status}` | #1206 |
| Attention needed mid-task (approval / decision / escalation of an external request) | `request_attention` (new action) | Worker or Leader (Q1) | leader + human members (+ optional DM / external, §2.3) | room, plus requested channels | `attention-{task}-{kind}-{attempt}` | #1206 |
| Project completion (all tasks terminal) | `complete_project` | Leader | leader + human members + requester route | room; requester channel via message tool | `project-{id}` | #1206 |
| Stale task (submitted-unaccepted / attention unresolved > N) | watchdog script over shared task meta | script (no LLM) | leader + human | room + configured external | task-dir timestamp | follow-up, L2 layer |
| Tool-level approval (runtime HITL) | QwenPaw ACP | runtime | ACP console | UI | — | known gap: channel bridge is QwenPaw-side, tracked separately |

Accepted result statuses: `SUCCESS`, `SUCCESS_WITH_NOTES`, `REVISION_NEEDED`, `BLOCKED`, `FAILED`, `PARTIAL` (+ `INTERRUPTED` — Q4). Every non-success status must render its own first-line contract (status visible in line 1); current code annotates only `REVISION_NEEDED`/`INTERRUPTED`, so `FAILED`/`PARTIAL`/`BLOCKED`-in-some-paths render as a generic completion line.

### 2.2 Ordering contract (P0)

Shared-storage sync **strictly precedes** notification. A recipient is woken by the mention and immediately pulls `shared/tasks//result.md`; if the sync has not completed, it observes missing or stale data, and the only completion wake-up has already been consumed (stable txn prevents a re-send).

- sync failure → return a retryable failure and do **not** notify; a retry re-syncs (idempotent) and then notifies.
- the completion event id is persisted optimistically (before the HTTP send) or the persist failure is surfaced explicitly, so a retry cannot loop on an unsaved event id.
- `delegate_task` already implements this pattern; `submit_task` adopts the same one.

### 2.3 Channel routing

Physical capability matrix (who can physically send what):

| Channel | Worker process | Leader process | Manager (single-user mode) |
|---|---|---|---|
| Matrix task room | code-level (this PR) | yes | yes |
| Matrix DM to a human | no (worker role has no message tool — by design) | message tool | message tool |
| External channel (QQ / DingTalk / WeChat / ...) | no (process has no such channel) | only if that channel is connected to the leader | only if connected |

Routing policy:

- completion → room @leader + @human members
- `BLOCKED` / attention (approval, decision, escalation) → room @leader + @human, **plus** DM to human members (higher salience); external requester channel when a requester route exists and the sender can physically use it
- project completion → room; requester-route data carried in the response payload so the leader/manager sends the requester-channel report via the message tool and marks it with the existing `mark_requester_report_sent` flag

Mechanics: a `channels` request parameter is validated against the sender's physical capabilities and deployment mode. The code may **downgrade** a requested route to what is deliverable; it never **upgrades** beyond capabilities. The LLM can only request; it cannot select beyond what its process can actually send.

Requester route: projects/tasks can persist a structured requester route (channel + target user/session, e.g. the IM conversation a request originated from). Notification payloads carry it so the leader/manager prompt has the data to complete the cross-channel half without re-derivation.

Deployment-mode matrix:

| | single-user (Manager present) | multi-user (no Manager; Leader is the authority) |
|---|---|---|
| Code-level notification | room @leader + @humans (identical code path) | identical |
| External relay | Manager (holds external channels) or leader | leader of the owning team |
| Isolation invariant | n/a | notification targets restricted to human members of the task's own team; never cross-user |

### 2.4 Resubmission & event identity

- A task becomes immutable once its project node reaches a terminal status (`completed`/`revision`/`blocked`/`cancelled`) — resubmission is rejected.
- There is a window between a worker submitting (e.g. `BLOCKED`) and the leader recording a decision (node still non-terminal) in which the same `task_id` can be submitted again. The completion event slot is therefore keyed on `(task_id, result_status)`:
- same-status resubmit → reuse the recorded event (true idempotent retry, no duplicate mention)
- different-status resubmit → invalidate the recorded event and send a new mention under a new stable txn (`submit-{task}-{result_status}`)
- This prevents a corrected result (`BLOCKED` → `SUCCESS`) from being silently suppressed, and prevents duplicate delivery of an identical retry. (A single `submit-{task}` txn makes the suppression structural: a re-PUT with a different body is rejected by the homeserver.)

### 2.5 `request_attention` (new taskflow action)

Mid-task "needs a human" without terminating the task:

- `taskflow(action="request_attention", {taskId, kind: approval | decision | escalation | other, question, deadline?, channels?})`
- Code behavior: append a versioned attention record to task meta (kind, question, requested_at, resolved=false) → sync → notify per §2.3 → the response carries the notification result. An attention record is resolved when the leader accepts a result or a follow-up `request_attention(..., resolved=true)` is recorded.
- Why not "just submit BLOCKED": `BLOCKED` signals stop (recovery = leader re-delegation). Approval and decisions are commonly **mid-flight and resumable**. And the worker currently has **no** non-terminal code-level path to a human at all (no message tool by design); its only alternative today is room chatter, which is subject to truncation and attention failures.
- Staleness: an unresolved attention record older than N is the watchdog's scan target (§2.1, row 5) and the re-ping source for the 10–30 min heartbeat fallback (protocol layer, not this PR; heartbeat is a cadence fallback by design, not a real-time driver).

### 2.6 What #1206 implements (v2 scope, single PR)

1. §2.2 ordering + regression tests (delayed/failed sync ⇒ no mention before durable state).
2. Status validation + first-line rendering for every accepted status.
3. Completion @mention list = leader + human members (from the room's TeamHarness meta; membership-validated; absent humans simply not mentioned).
4. `request_attention` action + attention state + tests (worker + leader gate per Q1).
5. `complete_project` code-level notification (room; requester-route data in payload).
6. Tests for all skip branches (no leader config / leader not in room / no Matrix env / no human members).
7. Dead-parameter cleanup + optimistic event-id persistence.

### 2.7 Explicitly out of scope (tracked, not silently deferred)

- ACP tool-approval → channel bridge (QwenPaw-side).
- Stale-task watchdog (L2 script layer; the scan target is defined here).
- Heartbeat-based re-ping (protocol layer).

## 3. Part B — Worker channel configuration access (#1219)

The capability model, secret contract, and audit design are defined in #1220 (L2 permission & capability model) — referenced here, not redefined. This section pins the **channel-endpoint-specific** parts.

### 3.1 Role matrix for the channel routes

| Caller | Scope | Read | Update (non-sensitive) | Credentials |
|---|---|---|---|---|
| L1 admin/manager | all workers | yes | yes | with `channel_secrets` capability (masked reads still apply, §3.3) |
| L2 human | workers of own `accessibleTeams` only | yes | **leader channel = standard path; worker channel = allowed but not recommended** (UI marker, no API block) | with `channel_secrets` capability |
| team leader (agent role) | own team | yes | **403 — read-only** | no |
| scoped caller, other team / standalone worker | — | **404** (existence not probeable) | 404 | 404 |

Dedicated actions `ActionChannelRead` / `ActionChannelUpdate` / `ActionChannelSecrets` per #1220; no inheritance of the generic worker `ActionUpdate` (whose L2 field whitelist, #1212, covers the generic worker update endpoint, not these routes).

**Why the leader channel is the L2 standard.** In multi-user deployments the leader is the user-facing entry point: humans DM the leader on their own channels; workers are team-internal plumbing. An L2 user reconfiguring a *worker's* channel can break shared team infrastructure used by other users, so the rule is: allowed (no hard block — a worker-level external channel is occasionally legitimate), but marked "not recommended" in the UI and documented as a leader-channel operation.

### 3.2 Sensitive-field classification

Schema-driven, not hard-coded: the controller derives the sensitive-field set per channel from the qwenpaw channel schema registry (e.g. `client_secret`, `app_secret`, `access_token`, `bot_token`, `password`, QR-login credential fields). Adding a channel type = adding a classification entry. Default for a field absent from the registry: open question Q3.

### 3.3 Secret contract (applies to all roles, including L1, on the normal read path)

- Read: masked value or `configured: true`; never plaintext.
- Write: write-only; omitted in an update ⇒ the existing value is preserved (nil-pointer merge-patch).
- Plaintext reveal: **not in v1**; the `secret_reveal` capability is reserved as a separately authorized and separately audited operation, if ever shipped (#1220 capability model).
- QR login: `qrcode` / `qrcode/status` require `ActionChannelSecrets` (initiating a scan-login is acquiring a credential). QR material is ephemeral by design — passed through, never persisted to MinIO, CR status, or logs.

### 3.4 Version & timing contract

- `conflict-check` requires **qwenpaw ≥ 2.1.1** (the route is absent from the pinned 2.0.1 router; verified against source). The proxy maps the upstream 404 to a versioned "unsupported" error and the client hides the entry. The endpoint table documents a version floor per route.
- MinIO read-back: the worker `push_loop` is mtime-based with a default 60 s production interval; the current ~6 s read-back window mis-reports most successful writes as unpersisted. Contract: return `pending` immediately, verify asynchronously against a `2×` push-interval deadline, log the outcome (audit); the write path never blocks on convergence.

### 3.5 Audit

Per #1220: structured logs + append-only daily JSONL (actor, action, target, field class — never values).

## 4. Part C — The unified loop

1. An L2 user self-serves channel onboarding for their team leader (Part B standard path) — e.g. connects QQ to the leader.
2. A task event fires (Part A): a worker submits `BLOCKED` "approval needed" or calls `request_attention` → the code-level notification wakes the leader **and** the human in the task room, DMs the human, and carries the requester route.
3. The human replies on the channel they were notified on; the leader relays the decision through taskflow (`accept_task_result` / resubmit). No human is required in the loop for *delivery* — only for *decision*.
4. Isolation: in multi-user mode, users are only notified in/on their own team's channels (membership-validated), and a scoped L2 cannot read or write another team's channel configuration (404).

Single-user variant: the Manager (which holds external channels) is the external relay; the code-level floor (room @leader + @humans) is identical.

Design consequence: the bottom line of the reporting chain — someone is woken, with the artifact path, durably — is a state-machine side effect that cannot be silently skipped; everything above it (conclusions, cross-team perspective, channel flavor) is LLM/protocol value-add.

## 5. Open questions

- **Q1.** `request_attention` role gate — worker + leader (proposal) vs leader-only (worker reaches humans via `BLOCKED` submit only)?
- **Q2.** `INTERRUPTED` — accept it into the accepted result-status set (the message builder already renders it) or remove it from the builder?
- **Q3.** Sensitive-field classification default for schema fields absent from the registry: sensitive (safe) or non-sensitive (pragmatic)?
- **Q4.** Should unresolved attention records be projected to the dashboard HITL inbox (whose data surface already syncs Matrix globally)?
- **Q5.** Stale-task watchdog threshold N (proposal: 30 min for unresolved attention, 4 h for submitted-unaccepted) and who owns the alert channel.

## 6. Linked PRs & rollout

- **#1206** — Part A implementation (TeamHarness MCP layer): §2.2–2.6.
- **#1219** — Part B implementation (controller proxy + authorizer actions): §3.1–3.5 (capability/secret/audit per #1220).
- Rollout: worker image rebuild carrying the new teamharness zip (production cluster: 20+ workers) + controller upgrade; the channel UI (workbench plugin) unlocks against the same endpoints.

## 7. Appendix: failure-mode evidence (reporting gap analysis)

- **Code audit.** `delegate_task` = state write + sync + code-level room notification (txn idempotent, membership check, m.mentions). `submit_task` = state write + artifact publish + **no notification** (hint only). Across all `notificationNeeded` return points, only the delegate path has a code-level send fallback.
- **Production failure chains** (multi-agent cluster, Matrix + IM channels, 20+ workers):
1. Worker completes and the follow-up message is never sent (turn ends, session lost) → Leader never woken → downstream tasks stall; recovery requires a human poking the room.
2. Long-message truncation: a completion report beyond the room text budget is replaced by a truncated preview; a leading @mention is cut off → the artifact exists, the signal is lost, the room is silent. (The `m.file` artifact event carries no mention and cannot wake anyone.)
3. Blocked work: a worker cannot proceed (approval/decision needed) and has no non-terminal code-level path to a human → room chatter or silence. In one deployment, an audit of 24 workers found the protocol's escalation route (@manager mention) physically unreachable on ~75% of them (channel allow-list configuration) — a "must escalate" rule that is silently dropped.
4. Prompt-level mitigation chain (repeated protocol-rule additions across four generations) did not reduce the silent-loss rate — consistent with LLM attention decay; this design moves the guarantee to the code layer.
- **Channel onboarding**: today SSH + `docker exec` + hand-edited `agent.json`; no in-band path for any user class (motivates #1219).

---

# 设计:任务生命周期注意力事件与通道路由(#1206 与 #1219 的联合视图)

相关:#1206(Part A 实现)· #1219(Part B 实现)· #1220(L2 权限与 capability 模型)· #1217(L2 团队作用域工作区访问)· #1177(社区 Proposal:完成唤醒丢失——本设计直接回应)

根据两个 PR 的 review 反馈,本 issue 整合两者共同依赖的设计:**当一个团队事件需要人类时,如何可靠地触达该人类、经由哪个通道、以及谁有权配置这些通道?**

## 1. 背景:一个根因,两个生产缺口

### 1.1 汇报缺口(#1206 的动因;回应 #1177)

生命周期不对称(当前 main 的代码级事实):

- `delegate_task` 在代码中写任务状态、同步共享存储并发送房间通知(稳定 txn 幂等键、成员校验、m.mentions)。
- `submit_task` 写任务状态并发布产物,但**代码不发送任何通知**——响应只带一个 `notificationNeeded` 提示。完成消息(以及唤醒 Leader 的 @mention)是一次独立的 LLM 工具调用。

后果(生产中复现,#1177 独立复现):如果 Worker 的 turn 或进程在 `submit_task` 与后续消息之间停止,Leader 永远不会被唤醒;下游任务滞留 `submitted`/`waiting`;唯一的完成唤醒丢失且无恢复路径。同样的不对称覆盖所有其他"需要人类"的时刻——等待审批的阻塞任务、中途决策、Agent 无法独自解决的外部请求——它们都没有代码级触达路径,因此全部依赖提示词遵从。

提示词级缓解是行为层,不是投递层:它会随注意力、会话长度和进程生命周期衰减。本设计采用的原则:

> **任何改变"下一个行动者是谁"的状态迁移,都必须有代码级投递副作用——投递要么有保证,要么操作可重试。** LLM 层负责汇报的*质量*(结论、上下文);代码层负责信号的*存在性*。

### 1.2 通道接入缺口(#1219 的动因)

把 Agent 接到消息通道(QQ / Matrix / 钉钉 / 微信 / …)一直需要 SSH + `docker exec` + 在宿主机上手改 `agent.json`。L1 admin 和 L2 团队用户都没有带内路径。这对 Part A 很关键:如果用户不能自助接入通道,双通道汇报中"用户自己的通道"这一半就不可能为最终用户存在。

### 1.3 为什么是一个 issue

注意力事件的通道路由(Part A)取决于存在哪些通道、谁可以配置它们(Part B)。一个设计伞:**事件 → 路由 → 通道 → 访问控制**。

## 2. Part A — 任务生命周期注意力事件(#1206)

### 2.1 事件矩阵

| 事件 | 触发 | 发送者 | 接收者 | 默认通道 | 幂等键 | 实现 |
|---|---|---|---|---|---|---|
| 任务派发 | `delegate_task` | Leader | 被派者 | 房间 @被派者 | `delegate-{task}` | 已上线 |
| 任务终态(任一已接受结果状态) | `submit_task` | Worker | 团队 Leader + 任务房间人类成员 | 房间 @mention | `submit-{task}-{result_status}` | #1206 |
| 任务中途需人(审批 / 决策 / 外部请求升级) | `request_attention`(新 action) | Worker 或 Leader(Q1) | Leader + 人类成员(+可选 DM / 外部,§2.3) | 房间,另加请求的通道 | `attention-{task}-{kind}-{attempt}` | #1206 |
| 项目完成(全部任务终态) | `complete_project` | Leader | Leader + 人类成员 + 请求方路由 | 房间;请求方通道经 message 工具 | `project-{id}` | #1206 |
| 任务滞留(已提交未接受 / 注意力未解决 > N) | 看门狗脚本扫共享任务 meta | 脚本(无 LLM) | Leader + 人类 | 房间 + 配置的外部通道 | 任务目录时间戳 | 后续,L2 层 |
| 工具级审批(运行时 HITL) | QwenPaw ACP | 运行时 | ACP 控制台 | UI | — | 已知缺口:通道桥接在 QwenPaw 侧,另行跟踪 |

已接受结果状态集:`SUCCESS`、`SUCCESS_WITH_NOTES`、`REVISION_NEEDED`、`BLOCKED`、`FAILED`、`PARTIAL`(另加 `INTERRUPTED`——Q4)。每个非 success 状态必须在第一行契约中渲染自身状态(状态在第 1 行可见);当前代码只对 `REVISION_NEEDED`/`INTERRUPTED` 加状态行,因此 `FAILED`/`PARTIAL`/`BLOCKED` 在部分路径下渲染为通用完成行。

### 2.2 顺序契约(P0)

共享存储同步**严格先于**通知。接收者被 mention 唤醒后会立即拉取 `shared/tasks//result.md`;如果同步尚未完成,它会观察到缺失或过期数据,而唯一的完成唤醒已被消费(稳定 txn 阻止重发)。

- 同步失败 → 返回可重试失败且**不通知**;重试时重新同步(幂等)后再通知。
- 完成事件 id 乐观落盘(HTTP 发送前)或显式暴露落盘失败,使重试不会卡在未保存的事件 id 上。
- `delegate_task` 已实现此模式;`submit_task` 采用同一模式。

### 2.3 通道路由

物理能力矩阵(谁在物理上能发什么):

| 通道 | Worker 进程 | Leader 进程 | Manager(单人模式) |
|---|---|---|---|
| Matrix 任务房间 | 代码级(本 PR) | 可以 | 可以 |
| Matrix DM 给人类 | 不能(worker 角色没有 message 工具——设计如此) | message 工具 | message 工具 |
| 外部通道(QQ / 钉钉 / 微信 / …) | 不能(进程没有该通道) | 仅当该通道已接入 leader | 仅当已接入 |

路由策略:

- 完成 → 房间 @leader + @人类成员
- `BLOCKED` / 注意力事件(审批、决策、升级)→ 房间 @leader + @人类,**另加**给人类成员的 DM(更高显著性);存在请求方路由且发送方物理可用时,发外部请求方通道
- 项目完成 → 房间;请求方路由数据随响应 payload 携带,Leader/Manager 用 message 工具发请求方通道汇报,并以现有 `mark_requester_report_sent` 旗标标记

机制:`channels` 请求参数对照发送方的物理能力与部署模式校验。代码可以把请求的路由**降级**到可投递的范围;绝不**升级**超越能力。LLM 只能请求,不能选择其进程实际发不出去的东西。

请求方路由:项目/任务可持久化结构化请求方路由(通道 + 目标用户/会话,例如请求发起所在的 IM 会话)。通知 payload 携带它,使 Leader/Manager 提示词无需重新推导即可补全跨通道汇报。

部署模式矩阵:

| | 单人模式(有 Manager) | 多人模式(无 Manager;Leader 即权威) |
|---|---|---|
| 代码级通知 | 房间 @leader + @人类(相同代码路径) | 相同 |
| 外部转述 | Manager(持有外部通道)或 Leader | 所属团队的 Leader |
| 隔离不变量 | 不适用 | 通知目标限制在本任务团队的人类成员;绝不跨用户 |

### 2.4 重提与事件身份

- 一旦任务的 project node 到达终态(`completed`/`revision`/`blocked`/`cancelled`),任务变为不可变——重提被拒绝。
- Worker 提交(如 `BLOCKED`)与 Leader 记录决定(node 仍非终态)之间存在一个窗口,同一 `task_id` 可再次提交。因此完成事件槽按 `(task_id, result_status)` 键控:
- 同状态重提 → 复用已记录事件(真幂等重试,不重复 mention)
- 异状态重提 → 作废旧事件,用新的稳定 txn(`submit-{task}-{result_status}`)发新 mention
- 这既防止修正后的结果(`BLOCKED` → `SUCCESS`)被静默吞掉,也防止同一重试被重复投递。(单一 `submit-{task}` txn 会让吞掉变成结构性的:不同 body 的 re-PUT 会被 homeserver 拒绝。)

### 2.5 `request_attention`(新 taskflow action)

任务中途"需要人类"但不终结任务:

- `taskflow(action="request_attention", {taskId, kind: approval | decision | escalation | other, question, deadline?, channels?})`
- 代码行为:向任务 meta 追加版本化注意力记录(kind、question、requested_at、resolved=false)→ 同步 → 按 §2.3 通知 → 响应携带通知结果。Leader accept 结果或记录后续 `request_attention(..., resolved=true)` 时,注意力记录被解决。
- 为什么不是"直接 submit BLOCKED":`BLOCKED` 表示停止(恢复=Leader 重新派发)。审批和决策通常是**中途且可恢复的**。而且 Worker 目前**没有任何**非终态的代码级触达人类的路径(设计上没有 message 工具);它今天唯一的替代是房间刷屏,而这会受截断和注意力失败的影响。
- 滞留:超过 N 未解决的注意力记录是看门狗的扫描目标(§2.1 第 5 行)和 10–30 分钟心跳兜底的再提醒源(协议层,不在本 PR;心跳按设计是周期兜底,不是实时驱动)。

### 2.6 #1206 实现内容(v2 范围,单个 PR)

1. §2.2 顺序 + 回归测试(延迟/失败同步 ⇒ 持久状态前无 mention)。
2. 状态校验 + 每个已接受状态的第一行渲染。
3. 完成 @mention 列表 = Leader + 人类成员(取自房间的 TeamHarness meta;成员校验;不在场的人类自然不被 @)。
4. `request_attention` action + 注意力状态 + 测试(worker + leader 门控按 Q1)。
5. `complete_project` 代码级通知(房间;payload 携带请求方路由数据)。
6. 所有 skip 分支的测试(无 Leader 配置 / Leader 不在房间 / 无 Matrix 环境 / 无人类成员)。
7. 死参数清理 + 事件 id 乐观落盘。

### 2.7 明确不在范围内(跟踪中,非静默推迟)

- ACP 工具审批 → 通道桥接(QwenPaw 侧)。
- 滞留任务看门狗(L2 脚本层;扫描目标已在此定义)。
- 基于心跳的再提醒(协议层)。

## 3. Part B — Worker 通道配置访问(#1219)

Capability 模型、secret 契约与审计设计定义于 #1220(L2 权限与 capability 模型)——此处引用,不重定义。本节固定**通道端点专属**的部分。

### 3.1 通道路由的角色矩阵

| 调用方 | 范围 | 读 | 写非敏感 | 凭据 |
|---|---|---|---|---|
| L1 admin/manager | 全部 worker | 可以 | 可以 | 带 `channel_secrets` capability(脱敏读仍适用,§3.3) |
| L2 human | 仅本 `accessibleTeams` 的 worker | 可以 | **leader 频道=标准路径;worker 频道=允许但不推荐**(UI 标记,API 不阻断) | 带 `channel_secrets` capability |
| team leader(agent 角色) | 本队 | 可以 | **403 — 只读** | 无 |
| 作用域调用方,其他团队 / 独立 worker | — | **404**(存在性不可探测) | 404 | 404 |

按 #1220 的独立 Action `ActionChannelRead` / `ActionChannelUpdate` / `ActionChannelSecrets`;不继承通用 worker `ActionUpdate`(其 L2 字段白名单,#1212,覆盖通用 worker 更新端点,不覆盖这些路由)。

**为什么 leader 频道是 L2 的标准。** 多人部署下,Leader 是用户-facing 入口:人类在自己的通道上 DM Leader;Worker 是团队内部管道。L2 用户重配 *worker* 的通道可能弄坏其他用户使用的共享团队基建,因此规则是:允许(不硬禁——worker 级外部通道偶尔合法),但 UI 标记"不推荐",并在文档中记为 leader 频道操作。

### 3.2 敏感字段分类

模式驱动而非硬编码:controller 从 qwenpaw 通道 schema 注册表按通道推导敏感字段集(如 `client_secret`、`app_secret`、`access_token`、`bot_token`、`password`、QR 登录凭据字段)。新增通道类型 = 新增分类条目。注册表缺失字段的默认值:开放问题 Q3。

### 3.3 Secret 契约(对普通读路径上的所有角色生效,含 L1)

- 读:脱敏值或 `configured: true`;绝不明文。
- 写:只写;更新中省略 ⇒ 保留原值(nil 指针合并补丁)。
- 明文 reveal:**v1 不做**;`secret_reveal` capability 保留为(如有落地)独立鉴权且独立审计的操作(#1220 capability 模型)。
- QR 登录:`qrcode` / `qrcode/status` 需要 `ActionChannelSecrets`(发起扫码登录=获取凭据)。QR 材料按设计是临时性的——透传,绝不落 MinIO、CR 状态或日志。

### 3.4 版本与时序契约

- `conflict-check` 需要 **qwenpaw ≥ 2.1.1**(锁定的 2.0.1 路由表无此路由;已对源码核实)。代理把上游 404 映射为带版本的"不支持"错误,客户端隐藏入口。端点表按路由标注版本下限。
- MinIO 回读:worker `push_loop` 基于 mtime,生产默认 60 秒周期;当前约 6 秒的回读窗口会把大多数成功写误报为未持久化。契约:立即返回 `pending`,按 `2×` 推送周期截止异步验证,结果记审计;写路径绝不阻塞等待收敛。

### 3.5 审计

按 #1220:结构化日志 + 追加式按天 JSONL(actor、action、target、字段类别——绝不落值)。

## 4. Part C — 统一回路

1. L2 用户为自己的团队 Leader 自助接入通道(Part B 标准路径)——例如给 Leader 接 QQ。
2. 任务事件触发(Part A):Worker submit `BLOCKED`"需要审批"或调 `request_attention` → 代码级通知在任务房间唤醒 Leader **和**人类、给人类发 DM、携带请求方路由。
3. 人类在被通知的通道上回复;Leader 经 taskflow 回流决定(`accept_task_result` / 重提)。*投递*不需要人类参与回路——只有*决策*需要。
4. 隔离:多人模式下,用户只在本团队通道内/上被通知(成员校验),作用域 L2 不能读写其他团队的通道配置(404)。

单人模式变体:Manager(持有外部通道)是外部转述方;代码级底线(房间 @leader + @人类)相同。

设计后果:汇报链的底线——有人被唤醒、带产物路径、持久化——是不能被静默跳过的状态机副作用;其上的一切(结论、跨团队视角、通道风味)是 LLM/协议的增值。

## 5. 开放问题

- **Q1.** `request_attention` 角色门控——worker + leader(提案)vs 仅 leader(worker 只经 `BLOCKED` submit 触达人类)?
- **Q2.** `INTERRUPTED`——纳入已接受结果状态集(消息构造器已渲染它)还是从构造器中移除?
- **Q3.** 注册表缺失的 schema 字段,敏感字段分类的默认值:敏感(安全)还是非敏感(务实)?
- **Q4.** 未解决注意力记录是否投影到 dashboard HITL inbox(其数据面已全局同步 Matrix)?
- **Q5.** 滞留任务看门狗阈值 N(提案:未解决注意力 30 分钟、已提交未接受 4 小时)及告警通道归属。

## 6. 关联 PR 与上线

- **#1206** — Part A 实现(TeamHarness MCP 层):§2.2–2.6。
- **#1219** — Part B 实现(controller 代理 + authorizer action):§3.1–3.5(capability/secret/审计按 #1220)。
- 上线:worker 镜像重建(携带新 teamharness zip;生产集群 20+ worker)+ controller 升级;通道 UI(工作台插件)对相同端点解锁。

## 7. 附录:失败模式证据(汇报缺口分析)

- **代码审计。** `delegate_task` = 状态写 + 同步 + 代码级房间通知(txn 幂等、成员校验、m.mentions)。`submit_task` = 状态写 + 产物发布 + **无通知**(仅提示)。所有 `notificationNeeded` 返回点中,只有 delegate 路径有代码级发送兜底。
- **生产失败链**(多 Agent 集群,Matrix + IM 通道,20+ worker):
1. Worker 完成但后续消息从未发出(turn 结束、会话丢失)→ Leader 永不被唤醒 → 下游任务滞留;恢复需要人类戳房间。
2. 长消息截断:超出房间文本预算的完成汇报被截断预览替换;行首 @mention 被切断 → 产物在、信号丢、房间静默。(`m.file` 产物事件不带 mention,无法唤醒任何人。)
3. 阻塞工作:Worker 无法推进(需审批/决策)且没有非终态代码级路径触达人类 → 房间刷屏或静默。在一个部署中,对 24 个 worker 的审计发现协议的升级路径(@manager mention)在约 75% 上物理不可达(通道 allowlist 配置)——"必须升级"的规则被静默丢弃。
4. 提示词级缓解链(四代协议规则的反复追加)没有降低静默丢失率——与 LLM 注意力衰减一致;本设计把保证移到代码层。
- **通道接入**:今天 = SSH + `docker exec` + 手改 `agent.json`;任何用户类别都没有带内路径(#1219 的动因)。

Guide de contribution

Aucun guide de contribution indexé pour ce dépôt

Évaluation

Cette issue n'a pas encore été évaluée.

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.