
一盏绿灯到底在说什么?从 Sutando 的协作者进度缺失看 Agent 界面的状态投影边界
一个协作者正在真实的实时执行会话里工作,也持续写入进度文件,团队页面却什么都没有显示。
不是网络断了,不是 Agent 没启动,也不是进度数据没有产生。问题只出在一个看似合理的条件:页面先问“是不是任务所有者”,不是所有者就不推送进度。
这是 Sutando 的 PR #3432 记录的公开反例。原来的 should_stream_task() 只允许所有者进入进度推送路径。它背后的理由并不荒唐:普通的非所有者团队任务运行在只读沙箱中,不会更新 core-status.json,因此没有实时步骤可以展示。真正的缺陷是协作者恰好是这个规则的例外——协作者不走那条只读沙箱路径,会真实写入状态文件,却仍被“不是所有者”这个条件挡在界面之外。
也就是说,页面做错的不是一个颜色,而是一种事实替换:
把“角色或访问身份”误当成了“是否存在实时执行”。
修复后的差异很小:team + collaborator 从 false 变为 true;普通团队任务仍为 false;所有者仍为 true。PR 新增四个回归用例,但作者也明确保留了一个验证缺口:尚未完成桥接服务重启后的真实端到端见证,因此该 PR 在本文复核时仍是开放状态。
这个例子很适合拿来问一个更一般的问题:
Agent 控制台上的“在线”“执行中”“有进度”“已完成”,到底分别在证明什么?
1. 界面状态不是事实源,而是一份投影合同
多 Agent 面板很容易把许多底层事实压成一个 status。但这些事实本来来自不同来源、不同对象、不同时间窗口:
| 事实轴 | 它真正回答的问题 | 不能替代什么 |
|---|---|---|
| 查看权限 | 当前用户能不能看这条信息? | 任务在哪里执行 |
| 网关连通性 | 浏览器或手机能否连到当前运行时? | 某个执行会话是否还活着 |
| 会话活性 | 这次执行是否还有新鲜、可验证的活动? | 任务是否会成功交付 |
| 执行进度 | 最近有没有可解释的工作进展? | 工作内容是否正确 |
| 正式报告到达 | 执行结果是否已经形成正式 REPORT? | REPORT 是否被接受 |
| 任务生命周期 | 任务当前处于 inbox / active / review / done 哪一层? | 关联证据是否没有冲突 |
这些事实轴可以同时成立,也可以同时出现矛盾。
例如:网关可以在线,但作业心跳已经过期;执行会话可以已经结束,但正式 REPORT 尚未到达;权威工作流可以是 done,同时某条审计证据仍然是 conflict;用户也可以有权限查看一个任务,却不是这个任务的执行者。
因此,一个可靠的界面状态应该被理解成:
底层事实 → 明确投影规则 → 页面语义
而不是:
页面颜色 → 反推系统真相
这一区别很关键。前者要求页面说明自己依据什么;后者会让“绿灯”逐渐变成一个没有来源的总判断。
2. Sutando 的问题为什么不只是一个进度显示缺陷?
Sutando #3432 的直接问题是协作者进度没有流出来。但更值得注意的是条件之间发生了错位:
访问级别 / 所有者身份
↓ 被错误替代为
执行位置 / 是否存在实时进度原规则对普通团队任务的解释是成立的,却被扩展到了一个不满足同样运行条件的协作者。
这类问题在 Agent 产品里很常见,因为很多字段在正常路径中高度相关:任务所有者通常也是执行者;网关在线通常伴随活跃的执行会话;会话结束后通常很快会有 REPORT;任务进入 review 时通常已经聚合了相关证据。
可“通常一起出现”不等于“可以互相替代”。真正危险的缺陷往往就出现在例外组合:
- 不是任务所有者,但确实存在新鲜的本地执行会话;
- 网关在线,但当前作业已经失去活性;
- 执行会话已经结束,但正式 REPORT 尚未写入;
- 工作流已经
done,但某条证据关联仍然冲突。
因此,界面投影测试不应只覆盖正常路径,还要专门制造这些事实轴之间不再同步的反例组合。
3. 我们自己的审计:五类会话观察必须先分开
Sutando 的 PR 不能证明我们存在同样的协作者缺陷。正确的做法不是把外部缺陷直接映射到自己,而是回到自己的读取路径,检查有没有把不同事实压成同一个结论。
在我们当前审计到的会话观察路径中,存在五种互斥输出:
executing_with_progress
executing_without_fine_progress
session_without_live_execution
completed_waiting_report
technical_error这些字段名是实际实现中的状态标识,因此保留英文。它们背后的判断顺序很重要:
- 会话为
failed,或恢复状态已经是session_lost→technical_error; - 会话为
completed,但正式 REPORT 尚未写入 →completed_waiting_report; - 会话不是
running→ 不投影为执行状态; - 会话为
running,但没有实时活性证据 →session_without_live_execution; running且仍有实时活性,再根据是否存在细粒度进度区分前两类。
这说明界面在生成文案之前,至少可以先拒绝几个常见的错误升级:
- 没有细粒度进度 ≠ 执行失败;
- 有会话记录 ≠ 仍在实时执行;
- 会话已经结束 ≠ 已正式交付;
technical_error≠ 业务任务被拒绝。
第一方源测试中的一个定向测试实际包含 5 个分类断言。早先把它写成“1/1”虽然字面没错,却容易让读者误以为只有一个状态被验证;更准确的口径是:一个定向测试用例,覆盖五种分类结果。
4. 公开证据现在可以自己重跑,而不只看我们描述
为了让这篇文章的核心分类不只停留在私有源码和文字说明里,我们把 R3 物化成了三个公开、脱敏的附件:
公开测试样本提供五条输入,每条对应一种已披露语义;读取器按公开判断顺序复现分类合同;检查脚本对五条记录逐条检查 actual === expected,并同时核对五类计数。
运行:
node 2026-08-27-r3-ui-status-projection-check.mjs预期结果:
{"fixture":"deidentified_runtime_session_observation","assertions":5,"status":"PASS"}这里的边界必须讲清楚:这个公开读取器是披露合同的独立复现器,不是私有生产源码。五条公开断言也不是桌面端、PWA、权限过滤和完整交付链的端到端认证。
它真正增加的是可复核性:外部读者现在不需要相信一句“我们有五类状态”,可以直接检查五条输入怎样被分类。
5. 冲突应该是一等输出,而不是界面要消灭的噪声
状态投影还有另一类危险:当不同来源互相矛盾时,页面为了“整洁”悄悄选一个最顺眼的答案。
我们更倾向于相反的做法:来源冲突本身就是一个需要保留的事实。
例如,在已审计路径中:
- 权威工作流缺失或来源互相冲突时,可以得到
projection_conflict,而不是从运行时、REPORT 或验收字段猜一个生命周期; - 网关是否在线,需要运行时、磁盘配置和上下文身份相互对齐,实例身份不一致时不能默认发布绿色;
- 工作流已经
done时,一条证据冲突仍可以单独保留,不能因为任务生命周期已终态就吞掉审计冲突。
这背后是一条很简单的原则:
一个事实轴的确定性,不能替另一个事实轴消除不确定性。
done 回答任务位置;证据冲突回答关联关系是否可靠。把两者压成一个状态,无论最后显示绿还是红,都会损失信息。
6. 每一盏灯至少应该声明五件事
如果状态只是一个颜色加一行文案,开发者很容易在组件里继续拼布尔值。更稳的做法是把每个状态当成一份小型投影合同,至少声明:
| 声明 | 例子 |
|---|---|
| 来源 | “网关在线”来自运行时、磁盘配置和上下文身份的一致性,而不是 REPORT |
| 对象 | 它描述的是网关、执行会话、任务还是 REPORT |
| 时效 | 这个判断针对哪个时间窗口或版本成立 |
| 能证明 / 不能证明 | 能证明连接可用;不能证明执行会话仍有活性或任务已验收 |
| 冲突策略 | 来源冲突时显示冲突或未知,而不是回退成绿色 |
这五项一旦明确,很多界面缺陷会从“颜色不对”变成更容易测试的问题:
- 当前用户不是任务所有者,但有查看权限且存在新鲜的本地执行会话,进度会不会被错误隐藏?
- 网关在线、作业心跳已经过期,页面能否同时展示“连接可用”和“执行失活”?
- 执行会话已经结束、REPORT 尚未到达,是否停在“等待正式报告”,而不是直接显示“交付完成”?
- 工作流为
done、证据关联仍有冲突,两个事实能否同时存在? - 运行时与磁盘中的实例身份不一致,远程页面会不会继续把旧实例标成当前?
这些测试不需要先发明一个更大的全局状态机。它们只要求:每个条件只回答自己拥有证据的问题。
7. 绿色不是结论,只是一个有范围的投影
真正好的 Agent 面板当然需要简单。用户不应该读十几行内部状态才能知道系统现在大概怎样。
但简单不等于把六种事实压成一个万能绿灯。Sutando 的协作者进度缺失很有代表性:一个原本正确的理由,只要被应用到错误对象上,就足以把真实执行隐藏掉。
我们的本地审计也只支持一个有限结论:会话活性、执行进度、REPORT 等待和 technical_error 可以先拆开;冲突可以保留为独立事实轴;这些规则可以被定向测试和公开复现。它不支持“所有界面、PWA 和权限组合都已经正确”的更强主张。
所以,下次看到一个 Agent 状态写着“在线”或“执行中”,更有价值的问题不是“它是不是绿色”,而是:
这条状态来自哪一个事实源?描述哪个对象?对哪个时间窗口有效?它证明了什么,又明确不能证明什么?
如果页面答不出这些问题,这盏灯就承担了超过自己证据范围的权力。
公开证据
来源与证据边界
- Sutando PR #3432 在本文复核时仍为开放 PR。本文只使用它作为“访问级别或角色身份不能替代执行位置与实时进度”的公开反例。PR 的四个回归用例是作者报告;作者同时明确说明尚未完成桥接服务重启后的真实端到端见证,因此本文不把该修复描述为已经完整生产验证。
- 第一方 R3 证据支持五类会话观察语义、对应定向源测试和公开脱敏复现器。公开读取器复现的是披露合同,不是私有产品源码;它不证明全部网页面板、桌面端、PWA、查看权限或权限过滤路径都已经通过完整正交性审计。
- 本文的核心结论是关于投影边界:查看权限、网关连通性、会话活性、执行进度、正式 REPORT 到达、任务生命周期与证据冲突应分别保留来源和语义。本文不据此评价 Sutando 或 CodeFlowMu 的整体可靠性。