docs: add workbench realtime authority spec

This commit is contained in:
Codex
2026-07-09 14:08:19 +02:00
parent dae5a15adf
commit da4f873c82
5 changed files with 223 additions and 61 deletions
@@ -19,7 +19,7 @@
| 短名 | Web工作台 |
| 层级 | L2 课题 |
| 状态 | 已生效 |
| 实现引用版本 | draft-2026-06-20-p0-long-running-workbench; draft-2026-06-20-p0-error-diagnostics; draft-2026-06-20-p0-passive-web-probe-observer; draft-2026-06-20-p1-view-local-timing-ticker; draft-2026-06-24-p0-no-ui-timing-fabrication; draft-2026-06-25-p0-web-caserun-e2e; draft-2026-06-25-p0-project-management-mdtodo; PJ2026-0104010803 唯一投影 draft-2026-06-20-p0-durable-facts-model; draft-2026-06-20-p1-zero-split-durable-realtime; draft-2026-06-20-p2-terminal-outbox-recovery; draft-2026-06-24-p0-aggregate-event-stream; draft-2026-06-25-p0-serve-session-aggregate-authority; draft-2026-06-25-p0-session-warm-runner-contract; draft-2026-06-27-p0-workbench-read-model-contract |
| 实现引用版本 | draft-2026-06-20-p0-long-running-workbench; draft-2026-06-20-p0-error-diagnostics; draft-2026-06-20-p0-passive-web-probe-observer; draft-2026-06-20-p1-view-local-timing-ticker; draft-2026-06-24-p0-no-ui-timing-fabrication; draft-2026-06-25-p0-web-caserun-e2e; draft-2026-06-25-p0-project-management-mdtodo; PJ2026-0104010803 唯一投影 draft-2026-06-20-p0-durable-facts-model; draft-2026-06-20-p1-zero-split-durable-realtime; draft-2026-06-20-p2-terminal-outbox-recovery; draft-2026-06-24-p0-aggregate-event-stream; draft-2026-06-25-p0-serve-session-aggregate-authority; draft-2026-06-25-p0-session-warm-runner-contract; draft-2026-06-27-p0-workbench-read-model-contract; PJ2026-010401080313 Workbench实时权威 draft-2026-07-08-p0-workbench-realtime-authority-v2; draft-2026-07-09-p1-single-step-debug |
| 需求规格模板 | [ISO/IEC/IEEE 29148 需求规格模板](../../templates/iso-iec-ieee-29148-requirements-spec-template.md) |
| 上级规格 | [PJ2026-0104 客户端](PJ2026-0104-client.md) |
| 规格治理索引 | [规格治理](spec-governance.md) |
@@ -34,7 +34,7 @@ Web工作台负责 HWLAB 登录后的浏览器主入口,使用户能够在同
本课题的目标状态是:主任务入口优先、诊断信息低噪声、状态反馈及时、桌面和移动端都能直接找到输入区,并且用户可以从基本工作台发起最小 Agent 调用并获得可查询执行状态。Web 工作台与 HWLAB CLI 必须共用同源 API、composer policy 和 trace renderer 语义,使浏览器问题可以通过同一运行端点的 CLI 非视觉复现。
长程可靠 Workbench 是本课题的黄金用例:用户在同一 public origin 内新建或选择 session,发送 `hi` 或等价最小任务,等待真实 Agent turn 运行,看到主消息区 final response 和 Trace terminal,随后刷新页面、切换到其他 session、再回到原 session、多标签页打开同一 deep link,仍看到同一组 sessionId、turnId、traceId、messageId、sealed final response 和 trace page。主 timeline 不等待完整长 Trace 全量拉完;Trace detail、projection diagnostic、transport diagnostic 和性能指标只解释可见性,不拥有主消息 lifecycle 或 final response 写权。trace/timeline 顺序、完成行位置、terminal sealed duration 和 SSE replay 必须来自唯一投影的 aggregate event stream revision,不能由浏览器 arrival order、trace hydration 顺序、session list 刷新或 probe 后处理重排。
长程可靠 Workbench 是本课题的黄金用例:用户在同一 public origin 内新建或选择 session,发送 `hi` 或等价最小任务,等待真实 Agent turn 运行,看到主消息区 final response 和 Trace terminal,随后刷新页面、切换到其他 session、再回到原 session、多标签页打开同一 deep link,仍看到同一组 sessionId、turnId、traceId、messageId、sealed final response 和 trace page。主 timeline 不等待完整长 Trace 全量拉完;Trace detail、projection diagnostic、transport diagnostic 和性能指标只解释可见性,不拥有主消息 lifecycle 或 final response 写权。trace/timeline 顺序、完成行位置、terminal sealed duration 和 SSE replay 必须来自唯一投影的 aggregate event stream revision,不能由浏览器 arrival order、trace detail 到达顺序、session list 刷新或 probe 后处理重排。
命令输入区的 prompt、steer、cancel 和 retry 都必须先成为唯一投影的 serve/session aggregate control command。Web 可以展示 optimistic placeholder,但不得从 DOM active card、session list、trace tail、localStorage 或 request timeout 推断 active turn、final response、running、duration 或 cancel 结果。
@@ -50,6 +50,7 @@ Web工作台负责 HWLAB 登录后的浏览器主入口,使用户能够在同
- Web 与 HWLAB CLI 共用的 Code Agent composer policy、trace renderer 和同源 API 行为。
- Web 入口对用户管理、Agent编排、硬件池、HarnessRL 和平台运维输出事实的展示契约。
- Workbench 浏览器回归验证,包括基于真实采集脱敏 fixture 的 Playwright 独立测试、mock server 重放、状态投影断言和截图 artifact。
- Workbench 实时调试台,包括独立根导航、fake SSE/sync/detail fixture、无补洞单步 reducer 驱动、cross-page convergence 对比和请求禁用 ledger。
### 2.3 范围外
@@ -69,6 +70,7 @@ Web工作台负责 HWLAB 登录后的浏览器主入口,使用户能够在同
| 主任务区 | 用户完成主要工作流的区域,包括会话消息和命令输入区。 |
| 命令输入区 | 用户输入新任务、引导运行中任务、取消或重试任务的 Web 控件集合。 |
| 诊断入口 | 用户主动打开的工作台状态详情入口,通常以 topbar 内的小型按钮或状态图标呈现。 |
| Workbench实时调试台 | 独立于真实 Workbench 业务页的调试路由,用 fake fixture 单步驱动 reducer 和 UI 投影,验证高纯度 SSE、统一 sync replay、detail-only 隔离和禁止补洞规则。 |
| 低噪声展示 | 诊断、trace、状态和辅助信息不抢占主任务区,也不伪装成用户或 Agent 正文。 |
| 响应式工作台 | 同一工作台在桌面和移动端按不同可视空间重排,但仍优先保证任务输入和状态理解。 |
| 同源业务入口 | Cloud Web 与 HWLAB CLI 通过同一 Web origin、相对 REST/JSON API path 和业务标识访问 HWLAB 能力的入口。 |
@@ -95,7 +97,7 @@ Web工作台负责 HWLAB 登录后的浏览器主入口,使用户能够在同
| 唯一投影 | [PJ2026-0104010803 Workbench唯一投影](PJ2026-0104010803-workbench-unique-projection.md) 定义的状态链路:AgentRun 执行事实只经 HWLAB projection writer/finalizer 写入 durable Workbench factsREST、SSE、CLI、fake-server 和 Web 前端只消费该投影。 |
| Serve Session Aggregate Authority | [PJ2026-0104010803 Workbench唯一投影](PJ2026-0104010803-workbench-unique-projection.md) 定义的会话级权威链路;Web 的 prompt、steer、cancel、retry、run-state、message/part、trace/timing 和 final response 只能消费该 aggregate read model。 |
| session execution lane | [PJ2026-0104010803 Workbench唯一投影](PJ2026-0104010803-workbench-unique-projection.md) 定义的会话执行通道;Web 只能展示其 read model 和 diagnostic,不从 DOM active card 或本地 pending 状态推断是否复用了 runner。 |
| sealed final response | 唯一投影 terminal commit 写入的 assistant 主正文、finalResponse、message/turn terminal status 和 sealed 标记;它是主消息区用户结果的唯一权威,不被后续读侧诊断、trace hydration 失败、turn polling 失败或 SSE gap 覆盖。 |
| sealed final response | 唯一投影 terminal commit 写入的 assistant 主正文、finalResponse、message/turn terminal status 和 sealed 标记;它是主消息区用户结果的唯一权威,不被后续读侧诊断、trace detail 失败、turn polling 失败或 SSE gap 覆盖。 |
| 破坏性投影权 | 删除 session、清空 active session、清空当前消息页、把 composer 改为无 session 或把 session lifecycle 改写为 not-found/archived/deleted 的权力。Web 前端 reducer、selectors、route hydrate、GET/list/detail/messages/SSE consumer 和测试 helper 没有破坏性投影权;只有显式用户 mutation 成功或后端 canonical lifecycle projection 可以改变这些事实。 |
| 读侧推理 | Web、API、CLI、fake-server 或测试根据 trace tail、message text、tool event、result cache、session summary、list row、workspace snapshot、localStorage 或 elapsed timeout 推断 lifecycle、terminal、running 或 final response 的行为;Web工作台禁止该模式。 |
| 0repair | Web 工作台在首次进入、刷新、deep link、session 切换、SSE 重连和 trace 补洞时必须按同一 authority 自然收敛;不得通过 reload、切走再切回、`sessionRepair``realignFreshSession`、workspace selection repair、active tab repair、GET read-through、localStorage truth 或测试 helper 自动点击,把已经分裂的 route/session/message/trace 状态补成看起来正确。 |
@@ -139,6 +141,7 @@ Web工作台负责 HWLAB 登录后的浏览器主入口,使用户能够在同
| PJ2026-01040111 | 长程观测 | 本规格 6.11 | 纯客户端 web-probe observer、命令文件控制、无副作用采样、JSONL artifact 和后处理分析 | 平台运维、公开入口 | Workbench 长程稳定性、瞬态竞态复现 |
| PJ2026-01040112 | CaseRun入口 | 本规格 6.12 | Web CaseRun 视图、run 卡、证据阅读、blocker 展示和 web-probe 同路径验收 | HarnessRL、API契约、硬件池、Agent编排 | 用户端到端硬件验证 |
| PJ2026-01040113 | 项目启动 | 本规格 6.13 | 公共 Workbench launch context 消费、session metadata 投影和项目任务解耦边界 | 项目管理、API契约、Agent编排 | `/projects/mdtodo` 一键启动 Workbench |
| PJ2026-01040114 | Workbench调试台 | 本规格 6.14 | 根导航调试入口、fake SSE 单步、authority gate、cross-page convergence 和请求 ledger | Workbench实时权威、唯一投影、API契约 | Workbench 状态投影单步排障 |
### 5.1 目标数据流程图
@@ -147,12 +150,12 @@ flowchart TD
U[用户 prompt/steer/retry/cancel] --> C[Composer UI]
C --> A[POST Turn Admission]
A --> O[Optimistic user/assistant message IDs]
A --> T[TurnSnapshot]
A --> E[SSE /workbench/events]
A --> E[SSE /workbench/events typed events]
A --> SY[/workbench/sync replay when needed]
E --> R[Workbench Reducer]
M[GET session messages] --> R
TS[GET turn snapshot] --> R
TE[GET trace events page] --> R
SY --> R
SNAP[Initial snapshot] --> R
DETAIL[Explicit detail/history] --> TD[Trace Detail Projection]
O --> R
R --> S[Workbench Server State]
S --> P[Timeline Projection]
@@ -163,7 +166,7 @@ flowchart TD
TD --> UI
```
目标数据流必须保证:提交请求只负责 admission 和稳定标识;实时事件、REST snapshot、trace pagination、页面刷新 hydrate 和 session-bound gap-fill 全部进入同一个 reducer晚到响应只能更新其所属 session、turn 或 trace cache,不能改变当前 active session、active route、当前消息区或 composer。
目标数据流必须保证:提交请求只负责 admission 和稳定标识;主状态只接受 initial snapshot、SSE typed event、统一 `/workbench/sync` replay/delta 和可对账的 optimistic echo。显式 detail/history 只进入 detail 或 diagnostic bucket。晚到响应只能更新其声明的 session、turn、message 或 trace bucket,不能改变当前 active session、active route、当前消息区或 composer。
### 5.2 目标架构图
@@ -271,7 +274,9 @@ flowchart LR
Web工作台在本规格中只保留前端消费边界:Cloud Web reducer、selectors、session rail、timeline、composer 和 Trace detail 只能消费该专项定义的 durable projectionSSE 只是 projection commit 后的通知和加速;fake-server fixture 只重放同一 REST/SSE 合同。Web reducer/selectors、Trace renderer、CLI renderer、fake-server 和测试断言不得从 trace tail、message text、tool event、result cache、session summary、list row、workspace snapshot、localStorage 或 elapsed timeout 推断 lifecycle、terminal、running 或 final response。运行中相对时间展示是唯一允许的显示层本地 now 使用场景:`startedAt``lastEventAt``finishedAt` 和 sealed `durationMs` 仍来自 durable projection,浏览器只用本地 now 对这些权威字段做直接格式化,不产生新事实。前端严禁为了掩盖跳变、归零、非单调、投影滞后或后台 tab 抖动而加入平滑、滤波、单调 floor、跳变 cap、二次缓存、最近更新时间重算或“看起来连续”的时间造假;发现此类异常必须暴露 diagnostic 并修 durable projection、read model、SSE/outbox 或上游时间源。详细架构图、数据流图、关键时序图、0repair 和严禁读侧推理约束以专项规格为准,本文不再维护第二份投影架构正文。
[PJ2026-0106050514 Workbench实时运行面](PJ2026-0106050514-workbench-realtime-runtime.md) 是 Workbench 浏览器实时链路、防请求风暴和 freeze/blocker 的专项规格。Web 工作台新增或修改 SSE/EventSource、REST gap-fill、health probe、session/message/turn/trace refresh、timeline row、storage、scroll 或 runtime diagnostic 逻辑时,必须遵守该专项的 typed error、scoped key、refresh queue/single-flight、SSE transport、browser memory policy 和 no-probe-masking 要求。卡死、内存上涨和请求风暴的修复不得落在 web-probe/Playwright 资源削减、自动刷新或 analyzer 降级上;这些探针只能提供证据和红灯
[PJ2026-010401080313 Workbench实时权威](PJ2026-010401080313-workbench-realtime-authority.md) 是 Workbench 高纯度 SSE、统一 sync replay、detail-only 隔离、禁止前端多源补洞和单步调试台的专项规格。Web 工作台新增或修改 EventSource、sync replay、detail/history、Colada key、server-state reducer 或 debug fixture 时,必须遵守该专项的 authority gate、sealed guard、legacy fan-out 禁止路径和 fake SSE 单步验收
[PJ2026-0106050514 Workbench实时运行面](PJ2026-0106050514-workbench-realtime-runtime.md) 是 Workbench 浏览器实时链路、防请求风暴和 freeze/blocker 的专项规格。Web 工作台新增或修改 SSE/EventSource、sync replay、health probe、explicit snapshot/detail/history refresh、timeline row、storage、scroll 或 runtime diagnostic 逻辑时,必须遵守该专项的 typed error、scoped key、queue/single-flight、SSE transport、browser memory policy 和 no-probe-masking 要求。卡死、内存上涨和请求风暴的修复不得落在 web-probe/Playwright 资源削减、自动刷新或 analyzer 降级上;这些探针只能提供证据和红灯。
长程可靠 Workbench 的用户可见验收矩阵如下,后续实现和回归验证必须直接引用这些稳定场景,而不是用单次 canary 或局部截图替代。
@@ -280,7 +285,7 @@ Web工作台在本规格中只保留前端消费边界:Cloud Web reducer、sel
| 场景 | 权威事实 | 用户可见结果 | 禁止路径 |
| --- | --- | --- | --- |
| 新建 session 并发送最小 prompt | admission 写入稳定 sessionId、turnId、traceId、userMessageId、assistantMessageId | 当前消息区立即显示用户消息、assistant pending 和可追踪 running 状态 | 用 workspace selected snapshot 或本地 stub 伪造 session/message |
| terminal commit 后显示 final response | sealed message/part/turn/trace facts | 主 timeline 显示 sealed final responseTrace detail 显示 terminal | turn polling、SSE gap、trace hydration error 覆盖主正文 |
| terminal commit 后显示 final response | sealed message/part/turn/trace facts | 主 timeline 显示 sealed final responseTrace detail 显示 terminal | turn polling、SSE gap、trace detail error 覆盖主正文 |
| 刷新或 fresh deep link | durable session/message/turn/trace projection | 同一 session/turn/trace/message 恢复,composer 可继续操作 | `sessionRepair`、reload 后自动新建 conversation、localStorage truth |
| 切换 session 后返回 | route sessionId 和 read model bucket | 只更新目标 session bucket,不让迟到响应抢 active route | late response 覆盖当前消息区或 session rail |
| 长 Trace 分页 | `projectedSeq` cursor 和 durable terminal 字段 | 首屏消息先可见,Trace 分页继续加载且不丢 terminal | `hasMore=false` 推断 turn terminal 或全量扫描 live trace tail |
@@ -340,7 +345,7 @@ Web工作台应提供低噪声诊断反馈,使用户或管理员可以主动
Trace Explorer 打开入口只由运行配置控制。前端不得在组件内硬编码 Tempo、Jaeger、Grafana 或平台内部地址;当 `observability.traceExplorerUrlTemplate` 缺失、`trace_id` 非 32 位十六进制或配置禁用打开入口时,只显示复制按钮。URL 构造只能替换 `{trace_id}`,不得拼接用户输入、`tracestate``baggage`、cookie、token、prompt、assistant 正文或 raw upstream payload。
错误诊断块适用于 Workbench admission 失败、projection blocker、transport timeout、SSE gap、trace hydration error、Skills/Gate/Performance 等普通页面 API 错误和全局 route exception。各页面不得各自拼接一套中文诊断文案或只抛出 `Error(response.error)`API client 应保留结构化 `ApiError` / `ErrorDiagnostic`,字符串只作为最终用户文案。
错误诊断块适用于 Workbench admission 失败、projection blocker、transport timeout、SSE gap、trace detail error、Skills/Gate/Performance 等普通页面 API 错误和全局 route exception。各页面不得各自拼接一套中文诊断文案或只抛出 `Error(response.error)`API client 应保留结构化 `ApiError` / `ErrorDiagnostic`,字符串只作为最终用户文案。
### 6.4 CLIENT-WB-REQ-004 响应布局
@@ -406,13 +411,13 @@ Web工作台的正式浏览器入口必须来自目标 node/lane YAML 声明的
| --- | --- | --- | --- |
| CLIENT-WB-REQ-008 | 状态投影 | PJ2026-01040108 状态投影 | [PJ2026-010403 API契约](PJ2026-010403-api-contract.md)、[Agent编排](PJ2026-0102-agent-orchestration.md) |
Web工作台应把 REST snapshot、SSE event、submit optimistic、trace pagination 和 page refresh hydrate 统一归入 Workbench Server State。Server State 应按 sessionId、messageId、partId、turnId 和 traceId 归一化保存服务端事实;conversationId、projectId 和 workspaceId 只能作为后端 metadata 或兼容映射字段挂接在对应 session/message 上,不得作为 active 对象、权限判断或恢复路径的 authority。UI transient state 只保存 route、显式选择、composer draft、scroll、展开状态和临时交互状态。
Web工作台应把 initial snapshot、SSE typed event、统一 `/v1/workbench/sync` replay/delta 和 submit optimistic echo 统一归入 Workbench Server State。显式 trace detail/history 读取只进入 detail 或 diagnostic bucket,不拥有主状态写权。Server State 应按 sessionId、messageId、partId、turnId 和 traceId 归一化保存服务端事实;conversationId、projectId 和 workspaceId 只能作为后端 metadata 或兼容映射字段挂接在对应 session/message 上,不得作为 active 对象、权限判断或恢复路径的 authority。UI transient state 只保存 route、显式选择、composer draft、scroll、展开状态和临时交互状态。
Timeline Projection 只能从 messages、parts、turn status 和 trace events 派生用户可见 rows。Trace detail 只消费 TraceEventPagesession rail 只消费 session list 和 turn summarycomposer 只消费当前 route/selected session 与明确 running turn。组件、trace polling、submit/cancel 回调不得直接写 messages、final response 或 trace authority。
Timeline Projection 只能从 messages、parts、turn status 和被主 authority 接受的 trace marker 派生用户可见 rows。Trace detail 只消费 detail/history projectionsession rail 只消费同一 durable projection 的 session summary 和 turn summarycomposer 只消费当前 route/selected session 与明确 running turn。组件、trace polling、submit/cancel 回调不得直接写 messages、final response、turn status 或 trace authority。
[PJ2026-0104010803 Workbench唯一投影](PJ2026-0104010803-workbench-unique-projection.md) 要求 Web 工作台把 `sessionId``messageId``partId``turnId``traceId` 作为 reducer 合并键。REST snapshot、SSE event、submit optimistic 和 trace pagination 的任何 late response 都只能更新其自身 key 对应的事实,不得清空当前 selected session、覆盖已存在 message page、或用 list summary/localStorage/workspace snapshot 重建当前 timeline。running trace 在 terminal 前收到 `hasMore=false` 只表示当前 trace page 暂时追平,不表示 `fullTraceLoaded=true`;后续 SSE 或 REST catch-up 仍必须能追加 terminal event 和 final response。
[PJ2026-0104010803 Workbench唯一投影](PJ2026-0104010803-workbench-unique-projection.md) 和 [PJ2026-010401080313 Workbench实时权威](PJ2026-010401080313-workbench-realtime-authority.md) 要求 Web 工作台把 `sessionId``messageId``partId``turnId``traceId` 作为 reducer 合并键,并同时检查 `realtimeAuthority`、entity family/id/version、cursor 和 `projectionRevision`。initial snapshot、SSE typed event、sync replay、submit optimistic 和 explicit detail/history 的任何 late response 都只能更新其自身 key 与授权 bucket 对应的事实,不得清空当前 selected session、覆盖已存在 sealed message、或用 list summary/localStorage/workspace snapshot 重建当前 timeline。running trace 在 terminal 前 detail page 收到 `hasMore=false` 只表示 detail page 暂时追平,不表示主 turn 已 terminal;后续 SSE typed event 或 sync replay 仍必须能追加 terminal seal 和 final response。
Message page 在刷新、切换 session、SSE 重连和 REST 补洞后必须保持同一 turn timeline 顺序:同一轮用户输入应先于对应 assistant/agent terminal,跨轮次按 turn timeline/aggregate seq/事件时间排序,不得按 role、source table、投影写入批次或 `updatedAt desc` 形成 `UUAA``UUUAAA` 等用户消息聚簇。前端不得通过 DOM 后处理或本地排序掩盖 read model 聚簇;若 API 已经聚簇,修复点必须回到 WorkbenchReadModel 或 projection writer。
Message page 在刷新、切换 session、SSE 重连和 sync replay 后必须保持同一 turn timeline 顺序:同一轮用户输入应先于对应 assistant/agent terminal,跨轮次按 turn timeline/aggregate seq/事件时间排序,不得按 role、source table、投影写入批次或 `updatedAt desc` 形成 `UUAA``UUUAAA` 等用户消息聚簇。前端不得通过 DOM 后处理或本地排序掩盖 read model 聚簇;若 API 已经聚簇,修复点必须回到 WorkbenchReadModel 或 projection writer。
Session rail 的 title/preview 必须来自同一 durable message/part projection 的脱敏摘要,并随 session list/detail 返回稳定字段。仅当 read model 明确缺失 title/preview 时,UI 才能短暂展示 fallback `Session ses_*`web-probe 与 OTel 必须把 fallback 数量、比例和示例暴露为 projection/read-model 问题,而不是把 fallback 当作正常标题。
@@ -420,17 +425,17 @@ Web 工作台严禁读侧推理。`turn.status`、`message.status`、`session.ru
浏览器本地 now 只属于 view-layer render input。它不得进入 reducer action、store mutation、projection merge、lifecycle selector、session sorting、terminal 判定、transport diagnostic 或 fake-server fixture 事实生成。前端回归必须覆盖固定 projection timestamp 且无 SSE/API 更新时,运行中“最近”和“耗时”仍随 fake clock 前进;同时覆盖 terminal 消息耗时不随 fake clock 前进。
Web 工作台必须把主消息投影、trace detail、session status 和 transport diagnostics 分仓保存。对已经带有 `sealedAt` 或等价 sealed 标记的 assistant message/turnturn polling 失败、trace hydration 失败、SSE gap、realtime timeout、late compat wrapper error 或 elapsed timeout 只能更新诊断 bucket 或消息详情入口,不得替换主 timeline 的正文、finalResponse、message status 或 turn terminal。`showMessageText()`、message diagnostic selector、Trace 面板和组件模板不得让读侧诊断文案压过 sealed final response;需要修改 sealed 主正文时只能来自同一 durable projection 的新 revision、受控 replay/reprojection 或明确用户 mutation。
Web 工作台必须把主消息投影、trace detail、session status 和 transport diagnostics 分仓保存。对已经带有 `sealedAt` 或等价 sealed 标记的 assistant message/turnturn polling 失败、trace detail 失败、SSE gap、realtime timeout、late compat wrapper error 或 elapsed timeout 只能更新诊断 bucket 或消息详情入口,不得替换主 timeline 的正文、finalResponse、message status 或 turn terminal。`showMessageText()`、message diagnostic selector、Trace 面板和组件模板不得让读侧诊断文案压过 sealed final response;需要修改 sealed 主正文时只能来自同一 durable projection 的新 revision、受控 replay/reprojection 或明确用户 mutation。
错误诊断中的 `trace_id`、requestId、route、layer 和 code 只属于 diagnostic bucket。它们可以驱动复制按钮、Trace Explorer 链接、消息详情感叹号或 transport health 展示,但不得参与 `running``completed``failed``canceled``blocked`、finalResponse 选择、自动折叠、session active 选择或主消息正文替换。completed sealed final response 后出现的 polling、SSE 或 trace hydration 错误只能追加诊断,不得把主消息正文改成“连接上游失败”“等待超时”或同类读侧错误态。
错误诊断中的 `trace_id`、requestId、route、layer 和 code 只属于 diagnostic bucket。它们可以驱动复制按钮、Trace Explorer 链接、消息详情感叹号或 transport health 展示,但不得参与 `running``completed``failed``canceled``blocked`、finalResponse 选择、自动折叠、session active 选择或主消息正文替换。completed sealed final response 后出现的 polling、SSE 或 trace detail 错误只能追加诊断,不得把主消息正文改成“连接上游失败”“等待超时”或同类读侧错误态。
Web reducer/selectors 必须同时遵守无破坏性投影权。读侧路径中的 `forgetSession``replaceActiveSessionSelection(null)`、清空 messages、清空 tabs、把 composer 降级为 `session_required` 或把 route session 标记为 not-found/archived/deleted,都是 lifecycle mutation,不能由 GET/list/detail/messages/SSE 失败、空响应、404、route hydrate、refreshSessions 或 late response 触发。读侧只能把目标 session 的 loading、degraded、unknown、blocker 或 canonical lifecycle projection 展示出来;若需要删除、归档或失活,必须走后端 lifecycle projection 或用户显式 mutation 的成功结果。
初发刷新一致性是 Web 工作台的硬约束:同一 prompt 从 admission 开始生成稳定 userMessageId、assistantMessageId、turnId 和 traceId;初发 UI 可以 optimistic 展示这些 ID,但刷新、切换 session、SSE 重连和 REST 补洞必须用同 ID 的 durable message、part、turn 和 trace projection 确认。若 trace events 缺失,只影响 trace detail;主 timeline 的用户消息、assistant final response 和 turn terminal 状态不得因此退化为“思考中”。
初发刷新一致性是 Web 工作台的硬约束:同一 prompt 从 admission 开始生成稳定 userMessageId、assistantMessageId、turnId 和 traceId;初发 UI 可以 optimistic 展示这些 ID,但刷新、切换 session、SSE 重连和 sync replay 必须用同 ID 的 durable message、part、turn 和 trace projection 确认。若 trace detail events 缺失,只影响 trace detail;主 timeline 的用户消息、assistant final response 和 turn terminal 状态不得因此退化为“思考中”。
OpenCode 的参照边界是职责结构,不是技术栈照搬。可借鉴的边界包括 route/sessionKey 作为当前 session authority、per-session message/part cache、REST message page 与 optimistic message 通过同一 messageID 合并、SSE delta 只作为实时加速、完整 message/part snapshot 可重建刷新结果。HWLAB 不得继续让 workspace snapshot、trace polling、result polling、list summary 和 localStorage 共同竞争当前消息面板。
状态投影正确性必须能被独立浏览器回归验证覆盖。测试应在同一 Web 构建产物上使用 mock server 重放真实采集 fixture,断言 session rail、主 timeline、workspace card、composer、message card 和 trace detail 在 session 切换、刷新页面、SSE 断线重连、REST 补洞和 trace 分页补拉后保持同一组 sessionId、turnId、traceId、messageId 与终态语义;conversationId/projectId/workspaceId 只可作为 metadata 被展示或调试,不得改变通过条件。测试不得依赖 live AgentRun、Cloud API、HWPOD、数据库或 Kubernetes 状态作为通过条件。
状态投影正确性必须能被独立浏览器回归验证覆盖。测试应在同一 Web 构建产物上使用 mock server 或 debug harness 重放真实采集 fixture,断言 session rail、主 timeline、workspace card、composer、message card 和 trace detail 在 session 切换、刷新页面、SSE 断线重连、sync replay 和 trace detail 分页后保持同一组 sessionId、turnId、traceId、messageId 与终态语义;conversationId/projectId/workspaceId 只可作为 metadata 被展示或调试,不得改变通过条件。测试不得依赖 live AgentRun、Cloud API、HWPOD、数据库或 Kubernetes 状态作为通过条件。
状态投影回归还必须覆盖 sealed final responsecompleted assistant 正文已经显示后,turn snapshot、trace event page、SSE 或 realtime diagnostic 返回 timeout/500/gap/close 时,主消息正文和终态保持不变,诊断只进入 trace detail、transport diagnostic、session health 或消息详情入口。测试必须用用户可见 DOM 断言,不通过内部 store 或 localStorage 判定通过。
@@ -458,13 +463,13 @@ mock 数据应优先来自目标 node/lane 的真实受控样本,而不是从
合成 fixture 只能补足真实样本难以稳定覆盖的边界条件,例如延迟响应、SSE 断线、分页缺口、列表缺少当前选中项、可选字段格式异常、空集合和特定 HTTP 错误。每个合成 fixture 必须标明 `derivedFrom``syntheticReason`,不得替代可从真实运行面采集的常规会话、完成态、失败态或 Trace 数据。
浏览器回归验证至少覆盖以下用户可见路径:切换 session 后主工作区显示 loading 并恢复目标 sessionfresh browser context 深链进入 session 后以同一 message、turn 和 trace 标识还原 timelineSSE 事件丢失或重连后通过 REST snapshot 和 trace page 补洞;session 标签、workspace card、message card、composer 主按钮和 Trace 终态显示 running、completed、failed、canceled 等状态时保持一致;Trace 阅读视图按事件顺序渲染可读 row,正确处理分页、终态、失败、自动展开和终态折叠;深链进入 session 与普通点击 session 走同一 authority path,删除或归档 session 后 deep link 不得复活 archived active tab。
浏览器回归验证至少覆盖以下用户可见路径:切换 session 后主工作区显示 loading 并恢复目标 sessionfresh browser context 深链进入 session 后以同一 message、turn 和 trace 标识还原 timelineSSE 事件重复、乱序、丢失或重连后通过 SSE replay 或统一 `/v1/workbench/sync` replay 收敛,不触发旧 trace/session/turn 多端点补洞;session 标签、workspace card、message card、composer 主按钮和 Trace 终态显示 running、completed、failed、canceled 等状态时保持一致;Trace 阅读视图按 detail/history 事件顺序渲染可读 row,正确处理分页、终态、失败、自动展开和终态折叠;深链进入 session 与普通点击 session 走同一 authority path,删除或归档 session 后 deep link 不得复活 archived active tab。
黄金链路回归必须覆盖完整用户流程:新建 session、发送 `hi`、running 可见、final response 可见、Trace terminal 可见、刷新、切换 session、回到原 session、删除或归档。长程链路回归必须使用真实采集脱敏长 Trace fixture,覆盖分页、SSE gap、projection lag、terminal sealed、diagnostic 分仓和截图 artifact。两类回归都必须记录 fixture schema/redaction 版本、capturedFrom、capturedAt、derivedFrom、截图 SHA 或等价 artifact 校验值。
黄金链路回归必须覆盖完整用户流程:新建 session、发送 `hi`、running 可见、final response 可见、Trace terminal 可见、刷新、切换 session、回到原 session、删除或归档。长程链路回归必须使用真实采集脱敏长 Trace fixture,覆盖 detail 分页、SSE gap、sync replay、projection lag、terminal sealed、diagnostic 分仓和截图 artifact。两类回归都必须记录 fixture schema/redaction 版本、capturedFrom、capturedAt、derivedFrom、截图 SHA 或等价 artifact 校验值。
状态投影回归必须覆盖新建 session 后读侧失败的负向用例:用户显式 create mutation 成功并进入新 session 后,即使随后的 session detail、messages、list refresh、SSE 或 route hydrate 返回失败、404、空列表或网络错误,URL、active tab、当前消息区和 composer 仍归属新 session;页面可以展示该 session 的 loading/degraded/blocker,但不得回退到旧 session、清空 active selection、清空 tabs/messages 或显示 `session_required`。这类用例必须作为 fake-server Playwright 红灯保留,线上 web-probe 只做同一 public origin 的原入口验收,不用 repair helper 修正页面后判通过。
状态投影回归必须覆盖新建 session 后读侧失败的负向用例:用户显式 create mutation 成功并进入新 session 后,即使随后的 session detail、messages、list refresh、SSE、sync replay 或 route hydrate 返回失败、404、空列表或网络错误,URL、active tab、当前消息区和 composer 仍归属新 session;页面可以展示该 session 的 loading/degraded/blocker,但不得回退到旧 session、清空 active selection、清空 tabs/messages 或显示 `session_required`。这类用例必须作为 fake-server Playwright 红灯保留,线上 web-probe 只做同一 public origin 的原入口验收,不用 repair helper 修正页面后判通过。
状态投影回归还必须覆盖 completed assistant 已 sealed 后的读侧失败负向用例:fixture 中 session messages API 返回 completed final response,随后 turn snapshot、trace event page、SSE 或 realtime diagnostic 失败。主 timeline 的 sealed final response 不变,诊断只显示在详情/感叹号/transport health 区域;Trace detail 延迟返回或分页缺口不得 remount 主消息卡片,也不得重置用户展开/折叠控制。
状态投影回归还必须覆盖 completed assistant 已 sealed 后的读侧失败负向用例:fixture 中主 authority 已返回 completed final response,随后 turn snapshot、trace detail page、SSE、sync replay 或 realtime diagnostic 失败。主 timeline 的 sealed final response 不变,诊断只显示在详情/感叹号/transport health 区域;Trace detail 延迟返回或分页缺口不得 remount 主消息卡片,也不得重置用户展开/折叠控制。
错误诊断回归必须覆盖 HTTP 400、401、403、404、409、500、proxy timeout、network error、Workbench projection blocker 和 sealed final 后 transport error。fake-server fixture 应按正式 `HwlabErrorEnvelope` 或等价结构重放 `error.diagnostic.traceId`、requestId、route、code 和 `valuesRedacted=true`,并用用户可见 DOM 断言诊断块、复制入口和可选 Trace Explorer 链接;测试不得通过内部 store、localStorage 或测试专用后门读取诊断字段。
@@ -520,6 +525,22 @@ Workbench 对 launch context 的使用必须保持只读和脱敏。它可以在
Workbench 模块不得为了项目管理联动新增 iframe、嵌套 Workbench 容器、localStorage 协议、私有 service URL、跨模块 store import 或 Markdown parser 依赖。需要回到项目任务时,只能使用公共 project link URL 或公共 Project Management API 暴露的 link 摘要。
### 6.14 CLIENT-WB-REQ-014 Workbench 调试台
| 编号 | 短名 | 主责模块 | 关联模块 |
| --- | --- | --- | --- |
| CLIENT-WB-REQ-014 | Workbench调试台 | PJ2026-01040114 Workbench调试台 | [PJ2026-010401080313 Workbench实时权威](PJ2026-010401080313-workbench-realtime-authority.md)、[PJ2026-0104010803 Workbench唯一投影](PJ2026-0104010803-workbench-unique-projection.md)、[PJ2026-010403 API契约](PJ2026-010403-api-contract.md) |
Cloud Web 应提供独立 Workbench 实时调试台,用于单步验证高纯度 SSE、统一 sync replay、detail-only 隔离和状态投影收敛。推荐入口是根侧边栏中的独立导航项和独立路由,例如 `/workbench/debug`,而不是继续扩展现有 Workbench topbar 诊断弹窗。原因是单步调试需要深链、fixture 选择、事件播放控制、请求 ledger、cross-page 对照和 Playwright 直达,长期嵌在真实 Workbench 会污染用户任务流和真实 store。
调试台必须默认使用隔离 store/reducer harness。纯 SSE 模式下,页面只消费 fake typed event 数组或 fake EventSource adapter;任何自动 `/v1/workbench/sync``/v1/workbench/turns/:id``/v1/workbench/sessions/:id/messages``/v1/workbench/traces/:id/events` 或旧 `/v1/agent/*` 请求都必须进入 request ledger 红灯。显式打开 sync replay 模式时,也只能使用 `/v1/workbench/sync` 等价 fixture,不得恢复 trace/session/turn 多端点补洞。
调试台至少应提供这些子标签页:纯 SSE 单步、Authority Gate、Terminal Seal、Reconnect/Cursor、Cross Page、Detail Only、Snapshot Merge、Fake Provider 和 Request Ledger。每个子标签页应输出 event header、authority decision、state diff、DOM-like projection、expected verdict 和可复制的 bounded 证据。Fake Provider 页可使用 fake-echo/fake model provider 的脱敏 fixture 或合成事件序列,但不得依赖真实 provider 成败作为通过条件。
调试 fixture 必须可版本化。每个 fixture 至少记录 `fixtureId``schemaVersion``capturedFrom``derivedFrom``syntheticReason`、redaction 状态、输入事件列表和 expected verdict。真实采集 fixture 优先;合成 fixture 只覆盖字段缺失、version 回退、duplicate cursor、late running、detail-only、跨页面到达顺序差异和 provider stream disconnect 等边界条件。
调试台不拥有业务修复权。它不得创建真实 session、发送真实 prompt、改写 localStorage truth、持久化用户 Secret、访问未授权 debug API 或在生产 Workbench 页中安装 repair helper。调试台发现的红灯应沉淀为单元测试、fake-server Playwright 用例、web-probe analyzer 规则或 GitHub issue 证据,再回到 projection/read model/reducer 修复。
## 7. 过程控制
本规格不单独索引过程 issue;跨 L1 的内测、灰度和阶段活动索引统一保留在 [PJ2026-01 HWLAB 总规格](PJ2026-01-HWLAB.md) 的 `7. 过程控制`
@@ -41,7 +41,7 @@ Workbench message/part authority 必须采用类似 OpenCode serve 的职责分
AgentRun terminal outbox 和 command result 是 Code Agent turn terminal 的上游权威输入之一。Projection finalizer 必须以 at-least-once、幂等方式消费对应 run/command 的 terminal outbox/result;即使 events page 没有出现 terminal event、cloud-api 曾重启、进程内 poller 丢失或 result sync 曾超时,后台 resume 也必须重新读取 command result authority 并把真实 terminal 通过同一 writer 写入 Workbench facts。用户 cancel、GET trace/detail、Web reload 或 CLI renderer 都不能代替这个写侧恢复。
Terminal final response 是 Workbench 投影的 sealed 用户结果。Projection writer/finalizer 一旦把 assistant final text、terminal status 和 `sealedAt` 或等价 sealed 标记写入同一 durable terminal commit,主消息区的正文、finalResponse 和 terminal status 就只能由后续同一写侧投影的受控 replay/reprojection 修正;turn polling、trace hydration、SSE gap、realtime timeout、transport close、read model lag 或 compat wrapper 失败只能写入 trace detail、transport diagnostic、projection diagnostic 或 session health,不能覆盖 sealed 主正文。
Terminal final response 是 Workbench 投影的 sealed 用户结果。Projection writer/finalizer 一旦把 assistant final text、terminal status 和 `sealedAt` 或等价 sealed 标记写入同一 durable terminal commit,主消息区的正文、finalResponse 和 terminal status 就只能由后续同一写侧投影的受控 replay/reprojection 修正;turn polling、trace detail、SSE gap、realtime timeout、transport close、read model lag 或 compat wrapper 失败只能写入 trace detail、transport diagnostic、projection diagnostic 或 session health,不能覆盖 sealed 主正文。
CLI trace 视图是 web-probe 工测和自动判别器的人工可读基准。`web-probe collect/observe` 只负责按固定频率保存采样事实;`analyze turn-summary``analyze trace-frame` 从既有采样事实或同源 read model 渲染文字版 trace 截图,不新增第二套保存来源。自动判别器的 finding 必须能被 CLI trace 视图复核;当二者冲突时,以 CLI trace 视图暴露的有序 turn/message/part/final response 事实作为需要修复的可见症状。
@@ -103,7 +103,7 @@ D601 v0.3 可以在 `hwlab-v03` namespace 内为 `hwlab-workbench-runtime` 使
| already terminal cancel | 用户发起 cancel 前,HWLAB 发现 AgentRun command 或本地 sealed projection 已经 terminal 的状态;此时 cancel mutation 不得覆盖 terminal facts,应返回 already-terminal/no-op 或触发 terminal projection。 |
| control command boundary | prompt、steer、retry 和 cancel 的受控 mutation 边界。每个控制命令必须有独立 action、target turn/run/command、HTTP route、commandId 或 no-op reason;采集器、analyzer 和 Web 不能把后续 steer/cancel 误归入前一个 prompt 提交。 |
| AgentRun execution diagnostic | AgentRun durable ledger 对 runner job observation、stale lease、terminalReportState、reconciler backlog 和不可恢复 blocker 的诊断输出;Workbench 只能把它作为 projection diagnostic 输入,不拥有执行状态写权。 |
| transport diagnostic | SSE、turn polling、trace hydration、result sync、realtime timeout 或浏览器网络层观察到的可见性诊断;它只能描述传输或读侧健康,不拥有 message text、finalResponse 或 terminal status 写权。 |
| transport diagnostic | SSE、turn polling、trace detail、result sync、realtime timeout 或浏览器网络层观察到的可见性诊断;它只能描述传输或读侧健康,不拥有 message text、finalResponse 或 terminal status 写权。 |
| projection health | 对 projection diagnostic 的只读解释,状态至少区分 `projecting``caught-up``degraded``stalled`;它不推进 facts,也不得把 stale projecting 标为 caught-up。 |
| timing display input | Workbench ReadModel 输出的 `startedAt``lastEventAt``finishedAt` 和 sealed `durationMs` 等投影时间字段;它们是浏览器相对时间文案的唯一事实输入。浏览器本地 now 只能参与直接渲染计算,不成为 Workbench facts,也不得通过 UI 侧平滑、滤波、单调 floor、跳变 cap、二次缓存或本地推断伪造更连续的 `elapsed/recent` 事实。 |
| TraceEventPage | 由 WorkbenchReadModel 返回的 trace 分页事实,必须按 `projectedSeq` 单调排序,并保持 `range.fromSeq <= range.toSeq`;空页用显式 empty range 表达,不生成非法区间。 |
@@ -466,7 +466,7 @@ MessagePage、session detail 和 session list 必须输出足够的脱敏诊断
Read model 严禁读侧推理。它不得从 `result?.status ?? trace?.status`、最后一条 trace event `status=completed`、message text 是否为空、part/tool row 状态、session list summary、workspace selected state、localStorage mirror、轮询耗时或 elapsed timeout 推断 turn terminal、message terminal、trace terminal、session running、final response 或 projection caught-up。若 durable facts 中唯一投影对象缺少这些字段,Read model 必须返回未知、投影中、degraded 或 blocker 等显式 diagnostic,由 projection writer/finalizer 修复源头;不得在读取时临时合成“看起来正确”的状态。
Read model 读取到 sealed final response 时,必须把主消息正文、finalResponse、message status 和 turn terminal 从 sealed projection 输出;projection diagnostic、trace hydration diagnostic、transport diagnostic、result sync diagnostic 和 session health 只能作为独立字段或详情区数据输出。读取链路不得因为后续 turn snapshot 失败、trace event page 失败、SSE gap、realtime timeout 或 compat wrapper 失败而替换 sealed 主正文。
Read model 读取到 sealed final response 时,必须把主消息正文、finalResponse、message status 和 turn terminal 从 sealed projection 输出;projection diagnostic、trace detail diagnostic、transport diagnostic、result sync diagnostic 和 session health 只能作为独立字段或详情区数据输出。读取链路不得因为后续 turn snapshot 失败、trace detail page 失败、SSE gap、realtime timeout 或 compat wrapper 失败而替换 sealed 主正文。
Read model 输出 running turn 时,finalResponse 必须为空值或等价 `(空内容)` 展示输入;诊断、后台状态解释、steer accepted 文案或用户 prompt 不能填入 finalResponse。Read model 输出 terminal turn 时,finalResponse 只能来自 sealed final part;若 sealed final part 缺失,应输出 projection blocker,而不是回退到 user message、assistant 中间文本、trace 最后一行或 result summary。
@@ -506,13 +506,13 @@ active session 只能由 route sessionId 或用户显式选择产生。REST deta
active turn/trace 只能来自 read model 声明的 current turn、用户显式选择或提交 mutation 返回的 stable ids。Web store 不得通过倒序扫描 messages、查找 running trace、`messageHasCompletedFinalResponse()`、最新 traceId 或 composer mode 推断 active trace;这类推断只能作为迁移期负向扫描对象。prompt、steer 和 cancel 的按钮 action 必须与当前 active command boundary 一致;没有 active turn 时,steer/cancel 应返回 no-op/blocker,不得 fallback 成普通 prompt。
SSE 只作为 projection commit 后的通知和加速,不是事实来源。SSE 断线、丢事件或重连后,Web 只能通过同一 REST snapshot 与 trace page 补洞。running trace 在 terminal 前 `hasMore=false` 只表示当前页追平,不得设置永久 `fullTraceLoaded=true`
SSE typed event 是 projection commit 的 live delivery 主路径,但事实来源仍是 durable aggregate event stream 和 Workbench read model。SSE 断线、丢事件或重连后,Web 只能通过同一 durable cursor 的 SSE replay 或统一 `/v1/workbench/sync` replay 追平;不得回退到 session/messages/turns/trace detail 多端点补洞。running trace 在 terminal 前 detail page `hasMore=false` 只表示当前 detail 页追平,不得设置永久 `fullTraceLoaded=true`,也不得影响主 turn terminal 判定
Web reducer 必须提供 sealed guard:对已经有 `sealedAt` 或等价 sealed 标记的 assistant message/turnturn polling、trace hydration、SSE gap、late realtime diagnostic、compat wrapper error 或 elapsed timeout 只能更新所属 trace/session 的 diagnostic bucket,不得写回 message text、finalResponse、message status 或 turn terminal。需要主动修改 sealed 主正文时,必须来自同一 durable projection 的新 revision、受控 replay/reprojection 或明确用户 mutation,而不是读侧失败。
Web reducer 必须提供 sealed guard:对已经有 `sealedAt` 或等价 sealed 标记的 assistant message/turnturn polling、trace detail、SSE gap、late realtime diagnostic、compat wrapper error 或 elapsed timeout 只能更新所属 trace/session 的 diagnostic bucket,不得写回 message text、finalResponse、message status 或 turn terminal。需要主动修改 sealed 主正文时,必须来自同一 durable projection 的新 revision、受控 replay/reprojection 或明确用户 mutation,而不是读侧失败。
运行中相对时间显示必须保持单一权威:Web server-state 只保存 WorkbenchReadModel 返回的 `startedAt``lastEventAt``finishedAt``durationMs` 和 sealed 标记;浏览器 ticker 只在组件渲染时计算 `now - startedAt``now - lastEventAt` 文案。`Date.now()`、页面本地 `updatedAt``lastEventAgeMs` 快照、轮询间隔和 trace hydration 时间不得写入 reducer/store,也不得影响 lifecycle、terminal、session ordering 或 projection diagnostic。终态消息的耗时只使用 sealed projection 值,不能随浏览器 ticker 继续增长。
运行中相对时间显示必须保持单一权威:Web server-state 只保存 WorkbenchReadModel 返回的 `startedAt``lastEventAt``finishedAt``durationMs` 和 sealed 标记;浏览器 ticker 只在组件渲染时计算 `now - startedAt``now - lastEventAt` 文案。`Date.now()`、页面本地 `updatedAt``lastEventAgeMs` 快照、轮询间隔和 trace detail 读取时间不得写入 reducer/store,也不得影响 lifecycle、terminal、session ordering 或 projection diagnostic。终态消息的耗时只使用 sealed projection 值,不能随浏览器 ticker 继续增长。
刷新和 session switch 恢复必须保持 active session bucket 不变量。Web 在重新 hydrate、切出再切回、双页面同时观察、SSE 断开后 REST 补洞或 optimistic submit reconcile 时,只能用 ReadModel 声明的 sessionId/turnId/messageId/traceId 更新对应 bucketpending optimistic message 必须按 stable ids 与服务器 message 对账,不能按数组位置、最近 traceId、session rail 行或 DOM 到达顺序合并。web-probe 在第 1、5、10 轮刷新或切换后看到的主 timeline digest、role sequence、session rail title/preview 和 traceId 归属必须与同一 ReadModel 快照一致。
刷新和 session switch 恢复必须保持 active session bucket 不变量。Web 在重新 hydrate、切出再切回、双页面同时观察、SSE replay、`/v1/workbench/sync` replay 或 optimistic submit reconcile 时,只能用 ReadModel 声明的 sessionId/turnId/messageId/traceId 更新对应 bucketpending optimistic message 必须按 stable ids 与服务器 message 对账,不能按数组位置、最近 traceId、session rail 行或 DOM 到达顺序合并。web-probe 在第 1、5、10 轮刷新或切换后看到的主 timeline digest、role sequence、session rail title/preview 和 traceId 归属必须与同一 ReadModel 快照一致。
### 6.7 WB-PROJ-REQ-007 regression and validation
@@ -528,7 +528,7 @@ Durable recovery 红灯必须覆盖长 run、多 command 和大量旧事件场
回归还必须覆盖读侧推理清零。后端和前端测试应构造最后一条 trace event 为非终态 `completed`、message text 为空、result/trace/session summary 字段互相矛盾或投影暂时滞后的样本,断言 API 与 DOM 不从这些原始字段推断 terminal/final/running。收口证据应包含负向源码扫描,至少覆盖 `result?.status ?? trace?.status``trace.status` 终结 turn、last event `completed` 终结 turn、message/session/list 状态覆盖 turn、elapsed timeout 改写 terminal 和 no-final-response 占位 final text 等读侧推理路径。
fake-server Playwright 应使用目标 node/lane 的真实采集脱敏 fixture,覆盖 session 切换、刷新、SSE 断线重连、REST 补洞、trace 分页和 terminal final response。D601 v0.3 public origin web-probe 应验证不切 session、不刷新页面、fresh deep link 和多观察者场景都通过同一 read model 收敛。
fake-server Playwright 应使用目标 node/lane 的真实采集脱敏 fixture,覆盖 session 切换、刷新、SSE 断线重连、sync replay、trace detail 分页和 terminal final response。纯 SSE 单步 fixture 应能禁用所有补洞请求,只用 typed event 验证 terminal seal、late stale rejection、detail-only rejection 和 cross-page convergence。D601 v0.3 public origin web-probe 应验证不切 session、不刷新页面、fresh deep link 和多观察者场景都通过同一 read model 收敛。
fake-server Playwright 还必须覆盖 timing display 红灯:在固定 projection timestamp、禁止 SSE/API 后续更新、使用浏览器 fake clock 推进的条件下,running 消息头部的“最近”和“耗时”应随 fake clock 前进;completed/failed/canceled/blocked 等终态消息的 sealed 耗时不得随 fake clock 前进。该测试不得读取内部 store,也不得通过更新 fixture、重新请求 API 或触发 projection repair 判定通过。
@@ -584,13 +584,13 @@ Workbench aggregate event stream 必须成为 Workbench 投影的唯一提交序
每个事件必须携带稳定身份与排序字段:全局 `eventSeq``eventId``aggregateId``aggregateSeq``aggregateType``sessionId``turnId``traceId`、可选 `messageId/partId``sourceRunId``sourceCommandId``sourceSeq/sourceEventId``eventType``occurredAt``committedAt``projectionRevision` 和脱敏 payload 摘要。`eventSeq` 用于跨 aggregate SSE replay 和审计;`aggregateSeq` 用于同一 session/turn/trace/message 内的顺序;`sourceSeq` 只用于来源审计和幂等,不得作为 Workbench API cursor。
Projector 必须按 event stream 的提交序更新 read model。terminal event 必须在同一 projector revision 内原子写入 sealed final response、message/part terminal status、turn terminal、trace terminal marker、session running=false、checkpoint 和 outbox notification。terminal revision 之后的 late event、transport diagnostic、trace hydration result、result archive 或 performance diagnostic 只能追加新的 diagnostic/detail revision;不得重新打开 sealed 主消息、把完成行挪到非最后位置、让完成耗时继续增长,或让 trace/timeline 因到达顺序不同而重新排序。
Projector 必须按 event stream 的提交序更新 read model。terminal event 必须在同一 projector revision 内原子写入 sealed final response、message/part terminal status、turn terminal、trace terminal marker、session running=false、checkpoint 和 outbox notification。terminal revision 之后的 late event、transport diagnostic、trace detail result、result archive 或 performance diagnostic 只能追加新的 diagnostic/detail revision;不得重新打开 sealed 主消息、把完成行挪到非最后位置、让完成耗时继续增长,或让 trace/timeline 因到达顺序不同而重新排序。
SSE replay 必须以 event stream/outbox durable cursor 为权威。`/v1/workbench/events?afterSeq=N` 或等价入口只能返回 `eventSeq/outboxSeq > N` 的投影 commit notification,并在 replay 完成后切到同一 durable tail;不得订阅进程内 `traceStore`、浏览器本地 pending queue、result polling 或 session list diff 作为 live truth。客户端收到重复、乱序或缺口时,应按 cursor 触发 REST/read model 补洞和 transport diagnostic;不能在浏览器 reducer 内通过 arrival order 合成最终顺序。
SSE replay 必须以 event stream/outbox durable cursor 为权威。`/v1/workbench/events?afterSeq=N` 或等价入口只能返回 `eventSeq/outboxSeq > N` 的投影 commit notification,并在 replay 完成后切到同一 durable tail;不得订阅进程内 `traceStore`、浏览器本地 pending queue、result polling 或 session list diff 作为 live truth。客户端收到重复、乱序或缺口时,应按 cursor 触发 SSE replay、统一 `/v1/workbench/sync` replay 或 transport diagnostic;不能在浏览器 reducer 内通过 arrival order 合成最终顺序,也不能调用 trace/session/turn 多端点补洞写主状态
Timing 字段必须由 aggregate event stream 和 projector 统一产生。`startedAt` 来自 admission/source start event`lastEventAt` 来自最新已投影活动事件,`finishedAt``durationMs` 来自 terminal event 或等价 terminal resultbrowser local now 只参与渲染运行中相对文案。web-probe 若发现 trace 乱序、完成行不是最后、完成耗时和 trace 首尾耗时不一致、终态耗时继续增长或红色 timing finding,根因修复必须回到 event stream/projector/read model/SSE cursor,不得再通过 Web reducer、probe analyzer、fake-server fixture 或 CSS/DOM 文案做补偿。
迁移实现必须删除旧的多来源仲裁路径。`traceStore.subscribe`、result envelope fallback、session JSON lifecycle、last trace event heuristic、turn polling status priority、localStorage truth、session repair、fake-server 专用重排和 probe 侧完成行修正只能作为负向扫描对象或迁移输入;保留兼容 wrapper 时也必须调用 WorkbenchReadModel 或正式 mutation。负向回归必须构造 source event 乱序、terminal result 先到、trace hydration 晚到、SSE replay gap、cloud-api rolling restart 和浏览器 session 切换样本,证明完成行、final response、duration、trace page 和 session rail 均按 aggregate event stream revision 收敛。
迁移实现必须删除旧的多来源仲裁路径。`traceStore.subscribe`、result envelope fallback、session JSON lifecycle、last trace event heuristic、turn polling status priority、localStorage truth、session repair、fake-server 专用重排和 probe 侧完成行修正只能作为负向扫描对象或迁移输入;保留兼容 wrapper 时也必须调用 WorkbenchReadModel 或正式 mutation。负向回归必须构造 source event 乱序、terminal result 先到、trace detail 晚到、SSE replay gap、cloud-api rolling restart 和浏览器 session 切换样本,证明完成行、final response、duration、trace page 和 session rail 均按 aggregate event stream revision 收敛。
### 6.11 WB-PROJ-REQ-011 CLI trace view and analyzer contract
@@ -604,7 +604,7 @@ CLI 视图必须提供两层:第一层 `turn-summary` 展示多 turn 摘要,
Trace-frame 行顺序必须来自 Workbench projectedSeq、aggregateSeq、part order 或采样事实中的同源顺序字段;完成行、assistant part 和 diagnostic 行不得按 DOM 到达顺序、文本时间戳排序或 analyzer 私有排序重排。若同一 frame 中存在乱序、completion row 非最后、duration 不一致或缺少 order 字段,CLI 应直接标红或 warning,保留原始矛盾供人工判断,而不是把视图修正成看起来正确。
自动判别器必须以 CLI trace 视图为判据。finding 生成、降级和消除都要能在 `turn-summary``trace-frame` 中复核;不能只依赖机器规则把长期存在的问题误判为通过。任何单次 Workbench 操作、turn 投影、命令执行、finalizer catch-up、trace hydration 或 analyze 阶段超过 120 秒,都必须输出 severe timeout warning,并要求进一步调查;该 warning 不得被普通 poll retry、fallback route、自动刷新或 analyzer 补偿吞掉。
自动判别器必须以 CLI trace 视图为判据。finding 生成、降级和消除都要能在 `turn-summary``trace-frame` 中复核;不能只依赖机器规则把长期存在的问题误判为通过。任何单次 Workbench 操作、turn 投影、命令执行、finalizer catch-up、trace detail/history hydration 或 analyze 阶段超过 120 秒,都必须输出 severe timeout warning,并要求进一步调查;该 warning 不得被普通 poll retry、fallback route、自动刷新或 analyzer 补偿吞掉。
prompt、steer、cancel 和 retry 必须在 CLI 视图中保留 control command boundary。普通 prompt 的 POST `/v1/agent/chat`、运行中 steer 的 `/v1/agent/chat/steer`、cancel 的 `/v1/agent/chat/cancel`、以及 terminal/no-active no-op 必须分别显示 action、target turn/run/command、HTTP status、accepted/no-op/blocker 和相关 idsanalyzer 不得因为同一时间窗内出现 steer 请求就把此前 prompt 判定成 `prompt-routed-to-steer`
@@ -616,7 +616,7 @@ D518/v0.3 business trace closeout 必须能从 node/lane YAML 解析 AgentRun na
| --- | --- | --- | --- |
| WB-PROJ-REQ-012 | 会话聚合权威 | PJ2026-010401080312 ServeSessionAggregate | [API契约](PJ2026-010403-api-contract.md)、[Agent编排](PJ2026-0102-agent-orchestration.md)、[Web工作台](PJ2026-010401-web-workbench.md)、[HWLAB接入](PJ2026-010205-hwlab-dispatch.md) |
Workbench session 必须是用户可见执行状态的 aggregate root。prompt、steer、cancel 和 retry 先写成 durable input/command fact,再进入 AgentRun run/command、runner job 创建、steer delivery 或 cancel deliveryadmission route 只能返回 durable accepted、structured conflict、structured no-op 或 blocker,不得让浏览器请求等待 Kubernetes Job 创建、runner 启动、result polling 或 trace hydration。
Workbench session 必须是用户可见执行状态的 aggregate root。prompt、steer、cancel 和 retry 先写成 durable input/command fact,再进入 AgentRun run/command、runner job 创建、steer delivery 或 cancel deliveryadmission route 只能返回 durable accepted、structured conflict、structured no-op 或 blocker,不得让浏览器请求等待 Kubernetes Job 创建、runner 启动、result polling 或 trace detail hydration。
每个 input/command fact 必须至少绑定 `sessionId``turnId``traceId``userMessageId``assistantMessageId``commandId`、action、target run/command、`admittedSeq``promotedSeq`、status、no-op reason 或 blocker。`turnId/traceId/messageId/partId` 必须在 dispatch 前或首次 projection commit 前稳定分配;后续 AgentRun event/result、steer accepted、cancel requested、terminal result 和 diagnostic 都必须回绑这些身份。提交 HTTP 500、AgentRun dispatch failure 或 delivery timeout 也必须绑定到本次 input fact,不得回落到旧 trace、旧 final response 或旧 active card。
@@ -0,0 +1,139 @@
# PJ2026-010401080313 Workbench实时权威
## 修改历史
| 版本 | 对应 commit id | 更新日期 | 变更说明 |
| --- | --- | --- | --- |
当前正文仍在规格治理草稿中;未定稿前不新增版本号,不为单次编辑追加 `待提交` 版本。
## 正文
## PJ2026-010401080313 Workbench实时权威需求规格
## 1. 文档控制
| 字段 | 内容 |
| --- | --- |
| 编号 | PJ2026-010401080313 |
| 短名 | Workbench实时权威 |
| 层级 | L4 专项规格切片 |
| 状态 | 草稿 |
| 实现引用版本 | draft-2026-07-08-p0-workbench-realtime-authority-v2; draft-2026-07-09-p1-single-step-debug |
| 需求规格模板 | [ISO/IEC/IEEE 29148 需求规格模板](../../templates/iso-iec-ieee-29148-requirements-spec-template.md) |
| 上级规格 | [PJ2026-010401 Web工作台](PJ2026-010401-web-workbench.md) |
| 关联规格 | [PJ2026-0104010803 Workbench唯一投影](PJ2026-0104010803-workbench-unique-projection.md)、[PJ2026-0106050514 Workbench实时运行面](PJ2026-0106050514-workbench-realtime-runtime.md)、[PJ2026-010403 API契约](PJ2026-010403-api-contract.md)、[PJ2026-01060508 Web哨兵](PJ2026-01060508-web-probe-sentinel.md) |
| 规格治理索引 | [规格治理](spec-governance.md) |
本文承载 Workbench Realtime Authority v2 的长期裁决。GitHub issue 记录阶段执行和证据;本文定义长期稳定的实时权威、禁止路径、回归和调试工作台边界。
## 2. 目的和范围
### 2.1 目的
Workbench实时权威负责把 Workbench 主 timeline、session rail、turn card、Final Response、Trace detail 和 transport diagnostic 的实时输入收敛到同一 durable projection 语义。当前目标不是“永远不用 REST”,而是禁止前端多端点补洞成为 correctness pathSSE typed event 是 live 主路径,`/v1/workbench/sync` 只能作为与 SSE 同语义的统一 replay/deltainitial snapshot 只负责页面初始水位,detail/history 只服务用户显式查看。
本规格同时定义单步调试工作台。调试工作台必须能在不接触真实运行面、不触发 automatic recovery 和不污染生产 Workbench store 的前提下,用 fake SSE 数据逐条驱动同一 reducer,暴露每一步的 authority decision、entity version、state diff、DOM triad 和禁止请求。
### 2.2 范围内
- `/v1/workbench/events` typed event 的 authority metadata、cursor、entity family/id/version、projectionRevision 和 terminal seal 语义。
- `/v1/workbench/sync` replay/delta 的语义边界:它是 SSE replay 等价物,不是第二套 REST repair 事实源。
- initial snapshot、explicit detail/history、trace-detail-only payload、session list/detail/messages read model 和主状态投影之间的合并边界。
- 前端 reducer/adapter 的 authority gate、sealed guard、detail-only rejection、late stale rejection 和 diagnostic 输出。
- 自动恢复、SSE error、cursor gap、cross-tab projection signal 和 refresh/reconnect 的允许动作与禁止动作。
- fake SSE 单步调试页、fixture 来源、调试子标签页、请求禁用规则、可视化证据和 Playwright 回归口径。
- web-probe/OTel/analyzer 对 legacy fan-out、sync/replay authority、cross-page convergence 和 fresh submit terminal/final 的判定口径。
### 2.3 范围外
- AgentRun run/command/runner job、provider stream 和 Code Agent 执行事实仍由 Agent编排定义。
- Durable event stream、projection writer/finalizer、facts store、read model 的写侧事务细节由 [PJ2026-0104010803 Workbench唯一投影](PJ2026-0104010803-workbench-unique-projection.md) 定义。
- 浏览器内存、freeze blocker、RUM 和 web-probe 运行策略由 [PJ2026-0106050514 Workbench实时运行面](PJ2026-0106050514-workbench-realtime-runtime.md) 和 Web哨兵规格定义。
- 调试工作台不是用户业务入口,不拥有 session lifecycle、权限、Secret、真实 mutation 或运行面修复权。
## 3. 术语表
| 术语 | 定义 |
| --- | --- |
| Realtime Authority v2 | Workbench 主状态只能由 initial snapshot、SSE typed event、统一 `/v1/workbench/sync` replay/delta 和显式 detail/history 中各自被授权的 projection 输入更新的规则集合。 |
| 高纯度 SSE | Workbench live correctness 优先由 SSE typed event 驱动;断线、重复、乱序和缺口通过同一 durable cursor/replay 语义处理,不回退到多个业务 REST endpoint 自行补事实。 |
| sync replay | `/v1/workbench/sync` 或等价入口按 session/trace scope 与 cursor 返回的 typed delta。它必须能被转换为与 SSE 相同的 reducer event,并携带同一 authority metadata。 |
| 多源补洞 | 前端在 automatic recovery 中同时或顺序调用 `/turns``/sessions/:id/messages``/traces/:id/events`、旧 `/v1/agent/*` 等端点来推断 terminal/final/message/session 主状态。该模式禁止。 |
| detail-only | 只服务 Trace detail、历史页、诊断和审计的 payload。它可以展示过程,不能覆盖主 message/finalResponse/turn/session authority。 |
| terminal seal | 同一 durable projection revision 内写入的 message/part terminal status、turn terminal、Final Response、timing、session running=false 和 cursor checkpoint。 |
| cross-page convergence | control page、observer page、刷新页和多标签页在同一 durable event/sync 输入下收敛到同一 message count、trace ids、turn status 和 Final Response digest。 |
| 单步调试工作台 | Cloud Web 内的独立调试路由,用 fake fixture 逐条喂入 snapshot/SSE/sync/detail 输入,展示 reducer 决策和 UI 投影,不访问真实 Workbench mutation。 |
## 4. 权威输入和禁止路径
Workbench 主状态只接受以下输入:
- `initial snapshot`:页面初始加载或 explicit user refresh 的水位输入,只能写入其声明的 session/message/turn/trace bucket。
- `SSE typed event`live delivery 主路径,必须携带 `contractVersion``realtimeAuthority``entity.family/id/version`、cursor 和 `projectionRevision`
- `sync replay/delta`:与 SSE 同语义的 replay 输入,必须先转换为 typed event 或同等 reducer action,再进入主 reducer。
- `explicit detail/history`:用户显式打开的 trace detail、历史分页或详情读取,只能写 detail bucket 或 diagnostic bucket;声明 `detailProjection=true``authority=trace-detail-only` 时必须拒绝写主状态。
- `optimistic local echo`submit admission 前后的本地临时投影,必须有 stable id,对账后由 durable event/sync 覆盖;不得成为跨页面或刷新后的事实源。
禁止路径:
- automatic recovery、SSE error、cursor gap、submit reattach、terminal projection 或 cross-tab signal 直接调用 `/v1/workbench/turns/:id``/v1/workbench/sessions/:id/messages``/v1/workbench/traces/:id/events` 或旧 `/v1/agent/turns|traces|chat/result` 写主状态。
- 用 trace tail、trace detail 末行、message text、session list preview、elapsed timeout、localStorage、DOM active card 或 analyzer fallback 推断 `running`、terminal、Final Response 或 session active。
- 让 late session list/detail/messages、detail-only trace payload、transport diagnostic、requestfailed 或 stale running snapshot 覆盖 sealed entity version。
- 用 reload、切换 session、自动 repair helper、测试后门或 probe analyzer suppress 把已经分裂的页面补成通过。
## 5. 单步调试工作台
Cloud Web 应新增独立根导航入口,例如 `/workbench/debug`navId 可为 `workbench.debug`。该入口面向开发和验收,不是普通用户任务入口。它必须使用隔离的 debug store 或 reducer harness,默认不复用生产 Workbench store,不写真实 session,不调用真实 mutation,不读取 Secret,不通过 localStorage 伪造权威。
调试页应采用左侧 fixture/场景栏、顶部子标签页、右侧 evidence 面板的工作态布局。根侧边栏入口是合理方案,因为单步调试需要独立路由、深链恢复、Playwright 直达和与真实 Workbench 页面并列观察;不应继续塞进现有 topbar 诊断弹窗。
建议子标签页如下:
| 子标签页 | 目标 | 必备能力 |
| --- | --- | --- |
| 纯 SSE 单步 | 单独测试 fake SSE 数据且禁用补洞 | 加载事件序列,`Next`/`Back`/`Run`/`Reset`,每步只调用 reducer,禁止 `/sync` 和 detail REST,显示 state diff、message order、turn status、Final Response digest。 |
| Authority Gate | 验证 authority metadata | 输入缺 `realtimeAuthority`、缺 entity、旧 version、detail-only、乱序 cursor 等样本,展示 accept/reject reason 和 diagnostic。 |
| Terminal Seal | 验证 sealed guard | 先写 completed/final,再喂 late running、trace error、detail-only、transport error,断言主正文、status、duration 不回退。 |
| Reconnect/Cursor | 验证重复、断线和 replay | 模拟 duplicate、missing seq、out-of-order、reconnect afterSeq;默认只标 diagnostic,不自动补洞;显式打开 sync 模式时只允许 `/workbench/sync` 等价 replay。 |
| Cross Page | 验证 control/observer 收敛 | 两个隔离 reducer/page model 接收同一 durable SSE/sync 输入或不同到达顺序,比较 message count、trace ids、turn status、Final Response digest。 |
| Detail Only | 验证 Trace detail 隔离 | 显式展开 trace detail,喂 `trace-detail-only` payload,证明只更新 detail/diagnostic,不写主 message/final/turn/session。 |
| Snapshot Merge | 验证 initial snapshot 与 late read model | 构造 initial snapshot、late list/detail/messages 与 SSE interleave,证明 bucket 合并、stale rejection 和 active session 不变。 |
| Fake Provider | 验证 fake-echo/fake model 场景 | 使用 redacted fixture 或 fake provider 生成事件序列,检查 submit -> event -> terminal 的高纯度 SSE 路径,不以真实 provider 成败作为通过条件。 |
| Request Ledger | 验证禁止请求 | 记录 debug harness 中所有网络意图;纯 SSE 模式下任何 `/sync``/turns``/messages``/traces` 自动请求都必须红灯。 |
### 5.1 fake 数据来源
fake 数据优先来自真实受控样本脱敏后的 fixture。合成 fixture 只能补足边界条件,例如 authority 字段缺失、version 回退、duplicate cursor、late running snapshot、detail-only payload、cross-page 到达顺序差异和 provider stream disconnect。每个 fixture 必须记录 `fixtureId``schemaVersion``capturedFrom``derivedFrom``syntheticReason`、redaction 状态和 expected verdict。
纯 SSE 调试的 fixture 应使用 typed event 数组作为第一等输入,不要求启动 fake server。需要浏览器级验证时,可以用 fake EventSource adapter 或 Playwright route 提供 `text/event-stream`,但调试页 reducer 必须能脱离真实 Cloud API 独立运行。
### 5.2 判定输出
每个单步场景必须至少输出:
- event headereventName、type、sessionId、traceId、entity family/id/version、cursor、projectionRevision、detail-only 状态。
- authority decisionaccepted/rejected、reason、diagnostic code、是否写主状态。
- state diffsession rail、message list、turn status、Final Response digest、trace detail rows、transport diagnostics。
- request ledger:本步允许和实际发生的网络请求族;纯 SSE 模式必须显示 no-sync/no-backfill。
- triad verdictDOM-like projection、server-state projection、expected fixture verdict 三者是否一致。
- export:可复制为 Playwright fixture 或 issue closeout 的 bounded JSON/Markdown 摘要,不输出 Secret 或完整敏感 prompt。
## 6. 验收标准
静态验收:
- 自动恢复路径只允许 `SSE typed event``/v1/workbench/sync` replay 或 diagnostic;不得出现旧 `/turns``/sessions/:id/messages``/traces/:id/events` automatic fan-out 写主状态。
- `traceHydration*` 命名和运行时口径必须收敛为 `traceDetail*` 或显式 detail read;旧配置字段如保留,只能作为 deprecated alias。
- `detailProjection=true``authority=trace-detail-only` 和缺 authority metadata 的输入必须被 reducer gate 拒绝写主状态,并产生可见 diagnostic。
行为验收:
- fresh submit 能在真实入口中自然 terminal/final 收敛,或明确暴露 provider/adapter blocker;不能靠旧补洞掩盖。
- refresh/reconnect/cross-tab signal 后,control/observer/fresh page 不出现 persistent `cross-page-projection-divergence`
- `workbench-automatic-recovery-fanout-authority`、旧 agent read-through 和旧 Workbench detail fan-out 不回归。
- fake SSE 单步调试可以在无真实 API、无 `/sync`、无补洞的模式下完成 terminal seal、late stale rejection、detail-only rejection 和两页收敛测试。
## 7. 过程控制
本规格由 pikasTech/unidesk#1582 承载架构裁决入口,阶段 issue 包括 #1583 后端合同、#1584 前端接入、#1585 Colada key、#1586 web-probe/OTel 判定、#1587 原入口验收,以及后续 #1591/#1592/#1600/#1606/#1608 等 blocker 和残余收敛。后续代码实现和 closeout 必须引用本 SPEC 编号与实现引用版本,不得只写 issue 编号或“按最新方案”。
@@ -271,9 +271,9 @@ sequenceDiagram
U->>W: open workbench or select session tab
W->>W: start journey open/session_switch
W->>A: GET rail/detail/messages/turn/trace pages
A->>S: query session/conversation/message page
A->>T: query trace pages when needed
W->>A: GET initial snapshot or /workbench/sync replay
A->>S: query session/message/turn projection
A->>T: query trace detail only when explicitly opened
A-->>W: first authoritative payload
W->>W: first visible message/tool/loading body ack
W->>A: RUM first_visible
@@ -283,7 +283,7 @@ sequenceDiagram
A->>P: observe journey durations
```
session 切换必须分别记录 `first_visible``full_load`。first visible 用于用户体感;full load 用于发现 trace pagination、turn status、rail/detail 补洞和后端 read model 的尾部慢路径。
session 切换必须分别记录 `first_visible``full_load`。first visible 用于用户体感;full load 用于发现 trace detail pagination、sync replay、rail/detail 投影和后端 read model 的尾部慢路径。性能分析不得把 trace/session/turn 多端点 automatic fan-out 当作合法补洞路径。
## 5. 内部分工与规格索引
@@ -380,11 +380,11 @@ UniDesk CLI 应提供薄 domain 入口,例如 `hwlab nodes observability plan|
| --- | --- | --- | --- |
| OPS-WBPERF-REQ-007 | TurnStatus预算 | PJ2026-0106050507 TurnStatus预算 | [API契约](PJ2026-010403-api-contract.md)、[HWLAB接入](PJ2026-010205-hwlab-dispatch.md)、[Agent编排](PJ2026-0102-agent-orchestration.md)、[Web工作台](PJ2026-010401-web-workbench.md) |
`/v1/agent/turns/:traceId` 是 Workbench TraceTimeline 和消息运行态的用户可见 poll 权威入口。该入口必须优先返回当前已知 turn snapshot、runnerTrace、finalResponse 或结构化 degraded reason,不得把一次 Web poll 同步阻塞在 AgentRun result/events 刷新、provider trace 拉取或后台 read model 补洞上
`/v1/agent/turns/:traceId` 属于旧 compat/diagnostic 读取面,不再是 Workbench 主消息运行态的 poll 权威入口。Workbench 主状态的 live/恢复权威来自 initial snapshot、`/v1/workbench/events` typed event 和统一 `/v1/workbench/sync` replaytrace/detail 读取只能解释过程或诊断,不能覆盖 message finalResponse、turn terminal 或 session running
Cloud API 可以在 turn status 请求内尝试刷新 AgentRun result/events,但刷新必须有独立的短预算;预算、开关和后续调整必须来自 YAML/env 配置,不得把具体数值写成第二真相。预算耗尽时,API 应返回已有快照并标记 `turn_status_degraded``agentrun_result_poll_failed``trace_refresh_timeout` 或等价低基数错误,后续 poll、SSE 或后台刷新仍可继续补齐最新事件。除非权限不匹配、trace 不存在或请求参数非法,turn status 不应因为上游刷新慢而让 Web 客户端超时
Cloud API 如保留旧 turn status 或 trace detail 请求内的上游刷新尝试,刷新必须有独立的短预算;预算、开关和后续调整必须来自 YAML/env 配置,不得把具体数值写成第二真相。预算耗尽时,API 应返回已有快照并标记 `turn_status_degraded``agentrun_result_poll_failed``trace_refresh_timeout` 或等价低基数错误,后续 SSE、sync replay 或后台 projector 仍可继续补齐最新事件。除非权限不匹配、trace 不存在或请求参数非法,旧 diagnostic/detail 请求不应因为上游刷新慢而让 Web 客户端超时,也不得被前端用作 automatic repair authority
Workbench 前端、Cloud API 投影层和性能监控不得维护或展示“运行总时长达到某阈值即超时”的 Code Agent 前端超时状态。运行中的 turn 只允许按无响应空闲时间判断可见性退化:只要 `lastActivityAt``lastActivitySeq`、AgentRun events/result、trace sourceSeq、SSE 消息、REST snapshot 或 visible ack 仍在推进,就必须继续视为 active/running,不得发出 `projection_sync_timeout``frontend_timeout``backend_timeout` 或等价总时长超时结论。
Workbench 前端、Cloud API 投影层和性能监控不得维护或展示“运行总时长达到某阈值即超时”的 Code Agent 前端超时状态。运行中的 turn 只允许按无响应空闲时间判断可见性退化:只要 `lastActivityAt``lastActivitySeq`、AgentRun events/result、trace sourceSeq、SSE typed event、sync replay、initial snapshot 或 visible ack 仍在推进,就必须继续视为 active/running,不得发出 `projection_sync_timeout``frontend_timeout``backend_timeout` 或等价总时长超时结论。
当无响应空闲时间超过配置预算时,系统可以标记低基数 degraded reason,例如 `no_response_idle_timeout``runner_heartbeat_stale``provider_stream_inactive``projection_activity_stale`,并向用户展示“无响应 N 秒/分钟”和最近活动摘要。该 degraded 状态不能把 turn 改写为 terminal,不能取消 AgentRun command,不能替代 finalResponse,也不能创建另一条 polling、fallback 或 read-through repair 路径;后续只要同一权威投影出现新活动,idle 计时必须复位并继续从同一 turn/trace 展示进度。
@@ -19,28 +19,28 @@
| 短名 | Workbench实时运行面 |
| 层级 | L4 专项规格切片 |
| 状态 | 草稿 |
| 实现引用版本 | draft-2026-06-30-p0-1297-spec-first |
| 实现引用版本 | draft-2026-06-30-p0-1297-spec-first; PJ2026-010401080313 Workbench实时权威 draft-2026-07-08-p0-workbench-realtime-authority-v2 |
| 需求规格模板 | [ISO/IEC/IEEE 29148 需求规格模板](../../templates/iso-iec-ieee-29148-requirements-spec-template.md) |
| 上级规格 | [PJ2026-01060505 Workbench性能](PJ2026-01060505-workbench-performance.md) |
| 关联规格 | [PJ2026-010401 Web工作台](PJ2026-010401-web-workbench.md)、[PJ2026-0104010803 Workbench唯一投影](PJ2026-0104010803-workbench-unique-projection.md)、[PJ2026-010403 API契约](PJ2026-010403-api-contract.md)、[PJ2026-01060508 Web哨兵](PJ2026-01060508-web-probe-sentinel.md)、[PJ2026-010603 YAML运维](PJ2026-010603-yaml-first-ops.md) |
| 关联规格 | [PJ2026-010401 Web工作台](PJ2026-010401-web-workbench.md)、[PJ2026-0104010803 Workbench唯一投影](PJ2026-0104010803-workbench-unique-projection.md)、[PJ2026-010401080313 Workbench实时权威](PJ2026-010401080313-workbench-realtime-authority.md)、[PJ2026-010403 API契约](PJ2026-010403-api-contract.md)、[PJ2026-01060508 Web哨兵](PJ2026-01060508-web-probe-sentinel.md)、[PJ2026-010603 YAML运维](PJ2026-010603-yaml-first-ops.md) |
| 规格治理索引 | [规格治理](spec-governance.md) |
本文定义 Workbench 浏览器实时运行面的迁移边界:SSE、错误处理、限流/队列、缓存、timeline、storage、scroll、health、OTel 与 Web 哨兵冻结探测必须用同一组受控语义协作。Issue 只承载阶段状态和证据索引,稳定语义、字段族、状态机和验收口径以本文为准
本文定义 Workbench 浏览器实时运行面的迁移边界:SSE、统一 sync replay、错误处理、限流/队列、缓存、timeline、storage、scroll、health、OTel 与 Web 哨兵冻结探测必须用同一组受控语义协作。实时事实权威、禁止前端多源补洞和单步调试工作台以 [PJ2026-010401080313 Workbench实时权威](PJ2026-010401080313-workbench-realtime-authority.md) 为准;本文只定义浏览器运行面、队列、性能和 no-probe-masking 规则
## 2. 目的和范围
### 2.1 目的
Workbench实时运行面负责把浏览器侧多轮 Workbench 的实时输入、SSE 事件、REST snapshot、trace/message 投影、可见性诊断和 Web 哨兵采样收敛为可控、可观测、可回归的运行链路。目标是解除第二轮消息后页面卡死、浏览器内存无界上涨、SSE error 引发 REST 请求风暴、刷新后消息堆叠、跨 session/trace 串线和 monitor 只能看到症状看不到根因的问题。
Workbench实时运行面负责把浏览器侧多轮 Workbench 的实时输入、SSE typed event、统一 `/v1/workbench/sync` replay、initial snapshot、trace/detail 只读投影、可见性诊断和 Web 哨兵采样收敛为可控、可观测、可回归的运行链路。目标是解除第二轮消息后页面卡死、浏览器内存无界上涨、SSE error 引发请求风暴、刷新后消息堆叠、跨 session/trace 串线和 monitor 只能看到症状看不到根因的问题。
本专项采用 OpenCode serve 的边界作为对照:事件流负责 replay 和 orderingREST 负责初始加载、分页和受控 gap-fill,错误 envelope 统一诊断,队列/single-flight 限制重复工作,timeline row model 保持稳定 identity。可整模块迁移的 TypeScript 策略模块必须先按 OpenCode 原文件或原核心函数机械拷贝,再在拷贝件上做 Vue/HWLAB 适配;禁止只阅读 OpenCode 后重写一个薄 wrapper 或只覆盖 happy path。Solid/Effect/TanStack 等框架专属层不直接搬运,但其状态机、分支语义、错误处理和 row/queue/transport 边界必须在 HWLAB runtime 中逐项保真。
本专项采用 OpenCode serve 的边界作为对照:事件流负责 replay 和 ordering统一 sync replay 负责按 durable cursor 追平,REST detail/history 只负责显式详情和历史分页,错误 envelope 统一诊断,队列/single-flight 限制重复工作,timeline row model 保持稳定 identity。可整模块迁移的 TypeScript 策略模块必须先按 OpenCode 原文件或原核心函数机械拷贝,再在拷贝件上做 Vue/HWLAB 适配;禁止只阅读 OpenCode 后重写一个薄 wrapper 或只覆盖 happy path。Solid/Effect/TanStack 等框架专属层不直接搬运,但其状态机、分支语义、错误处理和 row/queue/transport 边界必须在 HWLAB runtime 中逐项保真。
所有可调参数必须来自 YAML/source-of-truth 或其前端 runtime projection。本文和源码只定义字段族、状态机、责任边界和验收读取方式;不得写入硬编码阈值、采样周期、重试次数、并发数、缓存容量、退避窗口、超时窗口或浏览器内存 kill 数值。
### 2.2 范围内
- Workbench 浏览器 typed error、diagnostic envelope 和用户可见错误归一化。
- SSE runtime、cursor replay、gap 分类、transport state 和 recovery action。
- SSE runtime、cursor replay、sync replay、gap 分类、transport state 和 recovery action。
- event coalescing、keyed single-flight、bounded refresh queue、prefetch/cache trimming 和 scoped key。
- message/part/timeline row model、刷新恢复、增量归一化和 DOM row identity。
- localStorage/sessionStorage 容错、namespace 迁移、quota/security exception 降级和可观测 storage failure。
@@ -64,8 +64,8 @@ Workbench实时运行面负责把浏览器侧多轮 Workbench 的实时输入、
| --- | --- |
| 实时运行面 | Workbench 浏览器侧从用户动作到 REST/SSE/read model/timeline/DOM 可见性/诊断的运行链路。 |
| typed error | 带 code、layer、category、route、traceId、requestId、retryable 和 diagnostic 的标准错误对象。 |
| transport state | SSE/EventSource 或 REST gap-fill 的连接、打开、错误、关闭、replay、degraded 和 blocked 状态。 |
| recovery action | transport state 变化后允许触发的受控动作,例如继续 replay、单次 delta gap-fill、标记 degraded 或直接 blocked;动作不得绕过 queue/single-flight。 |
| transport state | SSE/EventSource 与统一 sync replay 的连接、打开、错误、关闭、replay、degraded 和 blocked 状态。 |
| recovery action | transport state 变化后允许触发的受控动作,例如继续 SSE replay、请求 `/v1/workbench/sync`、标记 degraded 或直接 blocked;动作不得绕过 queue/single-flight,也不得回退到 trace/session/turn 多端点补洞。 |
| refresh queue | Workbench 前端聚合 REST refresh 请求的队列,按 scope/key 去重、限流、标记原因并输出诊断。 |
| scoped key | 由 node/lane/user/session/trace/message/part 等 authority id 归一化后的键,用于 cache、queue、storage、row identity 和 OTel 属性。 |
| timeline row model | 面向 Vue 渲染的稳定行模型,把 message、part、diagnostic、trace summary 和 gap placeholder 表达为可增量替换的 row。 |
@@ -73,6 +73,7 @@ Workbench实时运行面负责把浏览器侧多轮 Workbench 的实时输入、
| browser memory policy | YAML/source-of-truth 下发给 web-probe 或 runtime 的浏览器内存观测与 kill 策略;本文只定义字段语义,不定义数值。 |
| REST 风暴 | 同一用户动作或 transport 错误导致 `/sessions``/messages``/turns``/traceEvents``/health` 等请求族无界重复或互相触发的状态。 |
| 架构退化修复 | 为了让 smoke、dashboard 或哨兵变绿而改变探针、刷新策略、fallback、采样强度或错误等级,却没有修复 Workbench runtime/projection/read model 根因的行为;本专项禁止该模式。 |
| 单步调试工作台 | [PJ2026-010401080313 Workbench实时权威](PJ2026-010401080313-workbench-realtime-authority.md) 定义的独立 debug route,用 fake SSE/sync/detail fixture 单步驱动 reducer,不触发真实 Workbench mutation 或 automatic recovery。 |
## 4. 系统边界和接口
@@ -90,7 +91,7 @@ Workbench实时运行面负责把浏览器侧多轮 Workbench 的实时输入、
| 编号 | 内部模块 | 规格文档 | 主责边界 | 上游依赖 | 下游支撑 |
| --- | --- | --- | --- | --- | --- |
| PJ2026-010605051401 | ErrorRuntime | 本规格 6.1 | typed error、diagnostic envelope、用户可见错误和 OTel 关联 | API契约、Workbench唯一投影 | Workbench store、diagnostic panel |
| PJ2026-010605051402 | QueueRuntime | 本规格 6.2 | coalesced event queue、keyed single-flight、refresh queue 和 prefetch/cache trimming | YAML运维、Workbench性能 | SSE、REST gap-fill、请求风暴治理 |
| PJ2026-010605051402 | QueueRuntime | 本规格 6.2 | coalesced event queue、keyed single-flight、sync replay queue 和 prefetch/cache trimming | YAML运维、Workbench性能、Workbench实时权威 | SSE、sync replay、请求风暴治理 |
| PJ2026-010605051403 | SseRuntime | 本规格 6.3 | EventSource lifecycle、cursor replay、transport state、gap 分类和 recovery action | API契约、唯一投影 | timeline、web-probe、OTel |
| PJ2026-010605051404 | TimelineRuntime | 本规格 6.4 | message/part 增量归一化、row identity、刷新恢复和跨 session/trace 隔离 | Web工作台、唯一投影 | Vue timeline、scroll、probe analyzer |
| PJ2026-010605051405 | BrowserRuntime | 本规格 6.5 | storage 容错、scroll 稳定、pane 滚动、health 探测和 local state 诊断 | Web工作台、YAML运维 | 用户体验、freeze 复现 |
@@ -161,7 +162,7 @@ flowchart LR
flowchart TD
U[用户消息或 session 切换] --> K[scoped key]
K --> Q[refresh queue / single-flight]
Q --> R[REST initial snapshot or delta gap-fill]
Q --> R[initial snapshot or /workbench/sync replay]
Q --> E[SSE subscribe with cursor]
E --> C[event coalescing]
C --> S[server-state reducer]
@@ -176,7 +177,7 @@ flowchart TD
O --> P[web-probe / monitor evidence]
```
同一用户动作只能通过 scoped key 进入受控 queueSSE error、visibility change、health probe 或 storage failure 只能产生 diagnostic 和受控 recovery action,不能绕过 queue 直接触发 REST fan-out。
同一用户动作只能通过 scoped key 进入受控 queueSSE error、visibility change、health probe 或 storage failure 只能产生 diagnostic 和受控 recovery action。允许的 live recovery 是 SSE replay 或 `/v1/workbench/sync`,不能绕过 queue 直接触发 session/message/turn/trace 多端点 fan-out。
### 5.3 SSE gap 与阻塞时序图
@@ -197,7 +198,7 @@ sequenceDiagram
S-->>S: transport error or seq gap
S->>O: transport diagnostic
S->>Q: allowed recovery action
Q->>A: bounded delta read when policy allows
Q->>A: bounded /workbench/sync replay when policy allows
P->>W: sample responsiveness and browser memory
P->>O: freeze/memory evidence
P-->>P: kill browser and mark blocked when policy says blocked
@@ -209,8 +210,8 @@ sequenceDiagram
| --- | --- | --- |
| OPS-WBRT-REQ-001 | ConfigSource | Workbench realtime、refresh queue、cache、storage、health、browser memory、freeze、sentinel cadence 和 smoke policy 的所有可调参数必须来自 YAML/source-of-truth 或其前端 runtime projection;源码和 SPEC 不得保存第二份数值真相。 |
| OPS-WBRT-REQ-002 | ErrorRuntime | 浏览器侧所有 REST/SSE/storage/health/projection 错误必须归一化为 typed error,并保留 layer、category、route、retryable、traceId/requestId 和 redaction 状态。 |
| OPS-WBRT-REQ-003 | QueueRuntime | SSE event、REST gap-fill、session refresh、trace refresh、prefetch 和 health probe 必须通过 key 去重、single-flight 或 coalesced queue;同一错误路径不得产生无界 REST fan-out。 |
| OPS-WBRT-REQ-004 | SseRuntime | SSE runtime 必须以 server cursor/replay 为同步 authorityREST 只用于初始加载、分页和受控 delta gap-fill,不得在 SSE error 中全量刷新多个请求族来“补洞”。 |
| OPS-WBRT-REQ-003 | QueueRuntime | SSE event、sync replay、explicit snapshot/detail/history、prefetch 和 health probe 必须通过 key 去重、single-flight 或 coalesced queue;同一错误路径不得产生无界 REST fan-out。 |
| OPS-WBRT-REQ-004 | SseRuntime | SSE runtime 必须以 server cursor/replay 为同步 authoritylive recovery 只允许 SSE replay、统一 `/v1/workbench/sync` replay 或 diagnostic,不得在 SSE error 中全量刷新多个请求族来“补洞”。 |
| OPS-WBRT-REQ-005 | TimelineRuntime | timeline 渲染必须基于稳定 message/part/row identity;刷新、SSE snapshot、trace event 和 REST page 替换不能造成用户消息堆叠、agent message 重复或跨 session/trace 串线。 |
| OPS-WBRT-REQ-006 | BrowserRuntime | storage、scroll、pane layout 和 health probe 的失败只能写入 diagnostic 或受控 recovery action;不得通过 reload、fallback、localStorage truth 或 document 级无限滚动掩盖状态分裂。 |
| OPS-WBRT-REQ-007 | SentinelFreeze | web-probe 必须把浏览器无响应和浏览器内存超过受控 policy 的状态分类为 blocker red,并保存 memory sample、freeze sample、request family、trace/span 和截图/日志证据;触发 blocker 后必须杀死浏览器,不得自动刷新页面或降级为 fallback。 |
@@ -220,9 +221,10 @@ sequenceDiagram
| OPS-WBRT-REQ-011 | MechanicalCopy | 可迁移的 OpenCode 通用模块必须先机械拷贝再适配;closeout 必须记录 OpenCode 来源行号、HWLAB 拷贝后行号、适配 diff 范围、被保留/删除/改名的能力清单,以及不存在“参考后重写”的复查结论。 |
| OPS-WBRT-REQ-012 | ShrinkageReview | 缩水是独立 P0 缺口;如果迁移后模块缺少 OpenCode 原有状态机、错误处理、限流/防抖、SSE/reducer、scroll persistence、timeline row parity 或 root cause 输出,即使生产路径已经 import,也不得判定完成。 |
| OPS-WBRT-REQ-013 | AppFixBoundary | 请求风暴、浏览器卡死、内存上涨、final response 投影缺失和刷新堆叠的修复必须落在 Workbench runtime、projection writer/finalizer、read model、SSE/REST contract、Vue store/reducer 或本专项迁移的共享模块;web-probe/Playwright 只能增强证据、baseline、blocker red 和 report 可见性,不能作为业务修复点。 |
| OPS-WBRT-REQ-014 | RequestStormRegression | 所有 SSE error、visibility change、health probe、session refresh、message refresh、turn status read、trace page read 和 gap-fill 都必须携带 scoped key、原因、in-flight/dedup 诊断和 recovery action;同一 scoped key 的重复恢复必须被 queue/single-flight 合并或阻塞,不能跨请求族递归触发。 |
| OPS-WBRT-REQ-014 | RequestStormRegression | 所有 SSE error、visibility change、health probe、explicit snapshot/detail/history 读取和 sync replay 都必须携带 scoped key、原因、in-flight/dedup 诊断和 recovery action;同一 scoped key 的重复恢复必须被 queue/single-flight 合并或阻塞,不能跨请求族递归触发。 |
| OPS-WBRT-REQ-015 | EvidenceSplit | closeout 必须区分 provider/AgentRun 执行完成、Workbench projection 完成和浏览器可见完成。OTel 显示 completed 但 UI trace rows/final response 为空时,应归入 read model/projection/runtime 可见性问题继续调查;不得把 completed trace 的空 UI 重新归因成 provider 失败,也不得用 analyzer fallback 生成假的 final response。 |
| OPS-WBRT-REQ-016 | NoProbeMasking | web-probe smoke 的目标是用户指定业务入口。为通过 smoke 而减少轮次、禁用内存/freeze 检测、自动 reload、重建页面、关闭 network 采样、吞掉 submit/command 失败或把 blocker red 降级为 warning,都属于架构退化。 |
| OPS-WBRT-REQ-017 | SingleStepDebug | Workbench 必须支持隔离的 fake SSE/sync/detail 单步调试工作台,用于验证 authority gate、sealed guard、detail-only rejection、cursor/reconnect 和 cross-page convergence;纯 SSE 模式下任何 automatic `/sync` 或旧 detail fan-out 都必须红灯。 |
## 7. 过程控制和源码引用
@@ -265,8 +267,8 @@ Workbench 请求风暴或浏览器 freeze 类 issue 的关闭证据必须同时
| 编号 | 迁移模块 | OpenCode 对照能力 | HWLAB 目标接入点 | 完成口径 |
| --- | --- | --- | --- | --- |
| M01 | ErrorRuntime | typed error、diagnostic envelope、用户可见错误 | `web/hwlab-cloud-web/src/utils/workbench-error-runtime.ts`、Workbench store、消息面板 | REST/SSE/storage/health/projection 错误统一归一化并暴露 root cause/recovery action |
| M02 | RefreshQueueRuntime | queue、single-flight、cooldown、请求风暴抑制 | `workbench-refresh-runtime.ts``src/stores/workbench.ts` | session/message/turn/trace/health refresh 进入同一 keyed queue |
| M03 | SseTransportRuntime | EventSource lifecycle、cursor、gap 分类 | `workbench-stream-transport.ts`、Workbench store | SSE reconnect/gap-fill 不绕过 queue,不触发无界 REST fan-out |
| M02 | RefreshQueueRuntime | queue、single-flight、cooldown、请求风暴抑制 | `workbench-refresh-runtime.ts``src/stores/workbench.ts` | sync replay、explicit snapshot/detail/history、health probe 进入同一 keyed queue |
| M03 | SseTransportRuntime | EventSource lifecycle、cursor、gap 分类 | `workbench-stream-transport.ts`、Workbench store | SSE reconnect 只触发 SSE replay、`/workbench/sync` 或 diagnostic,不触发 REST fan-out |
| M04 | ScopedKeyRuntime | scope key、cache key、trace key | `workbench-key.ts`、store/runtime modules | session/trace/message/part 的 key 生成规则生产路径共用 |
| M05 | TimelineRuntime | 稳定 row identity、增量替换、跨 session 隔离 | server-state reducer、timeline composables、Vue message rendering | 刷新和第二轮消息不产生堆叠、重复或串线 |
| M06 | StorageRuntime | safe storage、namespace、quota/security 降级 | `workbench-storage-runtime.ts`、store、session rail | localStorage/sessionStorage 访问不再直接散落在组件和 store |