From 663a8c7a0233d4e3d36d1ae5cfb4d6c9d2b20c3f Mon Sep 17 00:00:00 2001 From: Lyon <88232613+pikasTech@users.noreply.github.com> Date: Thu, 25 Jun 2026 04:33:20 +0800 Subject: [PATCH] docs: update workbench projection spec for message-part authority (#851) Co-authored-by: Codex --- ...-0104010803-workbench-unique-projection.md | 69 +++++++++++++++---- 1 file changed, 57 insertions(+), 12 deletions(-) diff --git a/project-management/PJ2026-01/specs/PJ2026-0104010803-workbench-unique-projection.md b/project-management/PJ2026-01/specs/PJ2026-0104010803-workbench-unique-projection.md index b20718c2..76cf54bc 100644 --- a/project-management/PJ2026-01/specs/PJ2026-0104010803-workbench-unique-projection.md +++ b/project-management/PJ2026-01/specs/PJ2026-0104010803-workbench-unique-projection.md @@ -19,7 +19,7 @@ | 短名 | Workbench唯一投影 | | 层级 | L4 专项规格切片 | | 状态 | 已生效 | -| 实现引用版本 | draft-2026-06-20-p0-durable-facts-model; draft-2026-06-20-p1-view-local-timing-ticker; draft-2026-06-20-p1-zero-split-durable-realtime; draft-2026-06-20-p2-terminal-outbox-recovery; draft-2026-06-22-p1-workbench-redis-derived-cache; draft-2026-06-24-p0-no-ui-timing-fabrication; draft-2026-06-24-p0-aggregate-event-stream | +| 实现引用版本 | draft-2026-06-20-p0-durable-facts-model; draft-2026-06-20-p1-view-local-timing-ticker; draft-2026-06-20-p1-zero-split-durable-realtime; draft-2026-06-20-p2-terminal-outbox-recovery; draft-2026-06-22-p1-workbench-redis-derived-cache; draft-2026-06-24-p0-no-ui-timing-fabrication; draft-2026-06-24-p0-aggregate-event-stream; draft-2026-06-24-p1-opencode-message-part-authority | | 需求规格模板 | [ISO/IEC/IEEE 29148 需求规格模板](../../templates/iso-iec-ieee-29148-requirements-spec-template.md) | | 上级规格 | [PJ2026-010401 Web工作台](PJ2026-010401-web-workbench.md) | | 关联规格 | [PJ2026-010403 API契约](PJ2026-010403-api-contract.md)、[PJ2026-01060505 Workbench性能](PJ2026-01060505-workbench-performance.md)、[PJ2026-010205 HWLAB接入](PJ2026-010205-hwlab-dispatch.md)、[PJ2026-0102 Agent编排](PJ2026-0102-agent-orchestration.md) | @@ -37,10 +37,14 @@ Workbench唯一投影负责把 AgentRun 执行事实收敛成 HWLAB 自有的 du Workbench aggregate event stream 是上述唯一投影的提交脊柱。所有 admission、AgentRun event/result、cancel、replay/reprojection 和 diagnostic transition 先归一化为带 `eventSeq`、`aggregateId`、`aggregateSeq`、`turnId`、`traceId`、`sourceRunId`、`sourceCommandId` 和来源幂等键的 append-only 事件,再由同一 projector 写入 message、part、turn、trace、checkpoint、outbox 和 read model。Web、SSE、CLI、fake-server 和 probe 只能消费该 event stream 的投影结果或 cursor replay,不能分别从 trace tail、result envelope、message cache、session list 和浏览器本地状态重新排序。 +Workbench message/part authority 必须采用类似 OpenCode serve 的职责分离:prompt admission、session status、message page、message part、event stream 和 abort/cancel 分别有清晰 authority,最终由同一投影提交序收敛。用户消息、assistant 文本、tool/command 行、diagnostic、steer/cancel 控制事件和 final response 必须是稳定 `messageId/partId/order/status/sealedAt` 的 part 事实;禁止把同一 trace 的全部 assistant 输出压成单个 trace-level assistant message,再由 read model、前端 store、CLI renderer 或 analyzer 根据文本内容重新选择“最终回答”。 + 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 主正文。 +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 事实作为需要修复的可见症状。 + D601 v0.3 可以在 `hwlab-v03` namespace 内为 `hwlab-workbench-runtime` 使用 Redis 派生读缓存,但该缓存只能保存 WorkbenchReadModel 从 durable facts 组装出的短 TTL 快照。Redis 不写入 Workbench facts,不推进 checkpoint,不生成 lifecycle、terminal、final response 或 projectionStatus;缓存 miss、stale 或 unavailable 只能改变读路径性能诊断,不能改变唯一投影事实。 ### 2.2 范围内 @@ -54,7 +58,7 @@ D601 v0.3 可以在 `hwlab-v03` namespace 内为 `hwlab-workbench-runtime` 使 - Terminal final response 的 sealed 字段、diagnostic 分仓和 sealed 后不可被读侧失败覆盖的显示语义。 - `GET /v1/workbench/*` 纯读、`/v1/agent/*` compat wrapper 降级和 projection diagnostics 输出要求。 - WorkbenchReadModel 内部的 Redis 派生读缓存 key、payload、失效、TTL 和降级边界。 -- Web reducer、CLI renderer、fake-server fixture 和 Playwright 回归对同一 durable projection 的消费规则。 +- Web reducer、CLI renderer、web-probe analyze/collect、fake-server fixture 和 Playwright 回归对同一 durable projection 的消费规则。 - 本专项范围内新增或重构源码文件的 SPEC 头部引用规则。 ### 2.3 范围外 @@ -77,6 +81,7 @@ D601 v0.3 可以在 `hwlab-v03` namespace 内为 `hwlab-workbench-runtime` 使 | WorkbenchProjectionFinalizer | 以 checkpoint 为驱动的幂等追平组件;可由 submit 后、event 到达、后台恢复或显式 checkpoint replay/reprojection 触发,但所有入口调用同一 finalization 逻辑。 | | WorkbenchFactsStore | durable facts 存储接口,负责幂等 upsert、terminal commit、checkpoint、diagnostic、分页查询和 owner visibility 过滤所需的持久化读写。 | | Workbench aggregate event stream | Workbench 投影自己的 append-only 事件流,承载 admission、source event/result、terminal、diagnostic、replay/reprojection 和 cancel transition;它是 trace/timeline 顺序、SSE replay cursor 和 projector revision 的唯一提交序。 | +| message/part authority | Workbench timeline 的消息和片段权威。每个 user/assistant/tool/diagnostic/final response 片段都有稳定 `messageId`、`partId`、`order`、`status` 和 sealed 状态;trace-level 文本、DOM 行、result envelope 或 analyzer 不能替代它选择最终内容。 | | WorkbenchReadModel | 唯一读取 Workbench facts 并组装 session rail、session detail、message page、turn snapshot、trace event page 和 projection diagnostics 的读模型。 | | checkpoint replay/reprojection | 受控管理入口按已持久化 sourceRun/sourceCommand/checkpoint 重放投影逻辑,用于恢复投影 lag 或阻塞;它只能调用同一 finalizer/writer,不由 GET、Web 页面、SSE 订阅或测试 helper 触发,也不得改变 active session 或 route。 | | projection commit | writer/finalizer 对一组 message、part、turn、trace、session summary 和 checkpoint 的一次幂等持久化提交;terminal commit 必须保持用户可见事实一致。 | @@ -94,6 +99,7 @@ D601 v0.3 可以在 `hwlab-v03` namespace 内为 `hwlab-workbench-runtime` 使 | projection diagnostic | 暴露 projection lag、blocker、lastProjectedSeq、sourceRunId、sourceCommandId、updatedAt 和恢复状态的可诊断字段。 | | terminal outbox | AgentRun runner 在 command 结束时写出的 durable terminal envelope,包含 runId、commandId、terminalStatus、failureKind、final response 或 terminal error 等终态输入;它必须被 Workbench finalizer 幂等消费。 | | 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 写权。 | | projection health | 对 projection diagnostic 的只读解释,状态至少区分 `projecting`、`caught-up`、`degraded` 和 `stalled`;它不推进 facts,也不得把 stale projecting 标为 caught-up。 | @@ -106,6 +112,7 @@ D601 v0.3 可以在 `hwlab-v03` namespace 内为 `hwlab-workbench-runtime` 使 | Redis派生读缓存 | WorkbenchReadModel 内部可选的 Redis 缓存层,只保存从 durable Workbench facts 组装出的 session summary、terminal turn snapshot 或 terminal trace page 快照;它可丢弃、可过期、可重建,不是 Workbench facts、checkpoint 或 lifecycle authority。 | | cache authority input | 构成缓存 key 与有效性判断的权威输入,至少包含 schema version、actor visibility input、session/turn/trace/cursor、projection revision/seq 或等价字段;这些输入必须来自 durable facts/read model。 | | cache diagnostic | `cacheStatus`、`cacheAgeMs`、cache key class、projection revision/seq、dbQueryAvoided 和 Redis unavailable/stale 等读路径诊断;它只能解释性能和新鲜度,不拥有主 timeline 写权。 | +| CLI trace 视图 | `web-probe analyze` 从采样事实渲染的文字版 trace 视图,包含多 turn 的 `turn-summary` 和单 turn/单 frame 的 `trace-frame` 两层;它用于人工复核自动 finding,不保存新事实,不从 DOM 重新推断。 | ## 4. 系统边界和接口 @@ -134,8 +141,15 @@ D601 v0.3 可以在 `hwlab-v03` namespace 内为 `hwlab-workbench-runtime` 使 | PJ2026-010401080308 | CodeReference | 本规格 6.8 | 源码文件头部 SPEC 引用和实现版本追溯 | 规格治理 | 后续实现审计 | | PJ2026-010401080309 | DerivedReadCache | 本规格 6.9 | ReadModel 内部 Redis 派生缓存的 authority input、payload、失效和降级边界 | Workbench性能、API契约 | 高频读降尾延迟 | | PJ2026-010401080310 | AggregateEventStream | 本规格 6.10 | Workbench append-only aggregate event stream、cursor replay、projector revision 和顺序权威 | ProjectionWriter、ProjectionFinalizer、FactsStore | ReadModel、SSE、Web、CLI、fake-server、web-probe | +| PJ2026-010401080311 | CliTraceView | 本规格 6.11 | web-probe 采样事实的 turn-summary/trace-frame 渲染、final response 展示和 analyzer 判据 | ReadModel、AggregateEventStream | 工测复核、自动 finding、issue 证据 | -### 5.1 目标架构图 +### 5.1 OpenCode serve 对照原则 + +本专项采用 OpenCode serve 的架构边界作为对照,但不复制其实现。`/root/opencode/packages/server/src/groups/session.ts` 将 prompt admission 与等待/上下文读取分离;`groups/message.ts` 和 `handlers/message.ts` 通过 message page、cursor 和稳定顺序提供投影读取;`groups/event.ts`、`event-v2-bridge.ts` 和 `bus/global.ts` 把 location-scoped event 与单调 event id 作为同步基础;`session/status.ts` 与 `session/run-state.ts` 集中表达 idle/busy/retry/cancel;`session/message-v2.ts` 与 `session/processor.ts` 以 message/part 追加、delta 和 cleanup 形成最终 assistant message;instance HTTP API 将 prompt、async prompt 和 abort 作为不同 route。 + +HWLAB 的实现必须吸收这些边界:admission 只创建 stable turn/message/part/control ids;status/cancel 不由 trace tail 推断;message page 和 trace page 只读 read model;event stream/outbox 负责 replay 和 ordering;assistant 最终内容来自 sealed assistant part,而不是从 trace 行、result envelope、DOM、`message.text` 或 analyzer fallback 重新挑选。凡是需要保留旧 compat route 的地方,都只能包装正式 mutation 或 WorkbenchReadModel,不能保留第二套 prompt/result/trace 仲裁。 + +### 5.2 目标架构图 ```mermaid flowchart LR @@ -183,7 +197,7 @@ flowchart LR 目标架构还要求彻底禁止读侧推理。`turn.status`、`message.status`、`session.running`、`trace terminal`、`finalResponse` 和 `projectionStatus` 必须是 projection writer/finalizer 已经写入 durable facts 的字段;read model、REST route、SSE consumer、compat wrapper、Web reducer、CLI renderer、fake-server 和测试只能读取和重放这些字段。AgentRun facts、trace events、message parts、result envelope、session summary、list row 和 workspace snapshot 只能作为 writer/finalizer 输入或诊断字段,不得在读取链路中通过优先级、fallback、最后事件、空文本、超时或 UI heuristic 生成生命周期事实。 -### 5.2 目标数据流图 +### 5.3 目标数据流图 ```mermaid flowchart TD @@ -211,7 +225,7 @@ flowchart TD 数据流必须保证:admission 只生成稳定标识和初始 facts;AgentRun facts 必须先归一化,再由 writer/finalizer 进入 projection commit;REST snapshot 和 SSE event 只能重放 durable facts;diagnostic 明确表达 lag/blocker,不能让读取路径、页面路径或测试路径隐式修复事实。 -### 5.3 terminal commit 关键时序图 +### 5.4 terminal commit 关键时序图 ```mermaid sequenceDiagram @@ -236,7 +250,7 @@ sequenceDiagram terminal commit 必须在同一幂等提交内更新 assistant final text、message/part terminal status、turn terminal、trace terminal event、session running=false、summary updated、projection checkpoint 和 SSE cursor。若提交失败或暂时无法完成,只能暴露 projection lag/blocker;不得让 session rail 显示 completed、message page 显示 running、turn snapshot 显示 failed 或 trace page 缺 terminal 这类互相矛盾状态。 -### 5.4 cloud-api 重启恢复时序图 +### 5.5 cloud-api 重启恢复时序图 ```mermaid sequenceDiagram @@ -259,7 +273,7 @@ sequenceDiagram 重启恢复要求 finalizer 不依赖进程内 90s 轮询作为唯一推进机制。进程内任务丢失、cloud-api 重启或慢任务超过短轮询预算后,boot/background scheduler 必须能从 durable checkpoint 和 running/projecting turn 找回需要追平的 sourceRun/sourceCommand,并以同一 writer 逻辑提交或记录 blocker。GET、SSE、Web 页面、fake-server 和 probe 只能观察恢复状态,不能触发恢复。 -### 5.5 durable Workbench facts 对象模型 +### 5.6 durable Workbench facts 对象模型 Workbench durable facts 是唯一投影的持久对象模型。后续实现可以选择关系表、文档表或 runtime store 等价结构,但必须保留下列对象、稳定键和职责边界;旧 `agent_sessions.session_json`、result snapshot、trace tail、compact snapshot 和 workspace/conversation metadata 只能作为迁移输入或诊断来源,不得成为读取路径 lifecycle authority。 @@ -276,10 +290,12 @@ Workbench durable facts 是唯一投影的持久对象模型。后续实现可 对象模型必须支持下列 DTO 投影:session rail summary、session detail、message/part page、turn snapshot、trace event page、projection diagnostic 和 transport diagnostic。DTO 可以裁剪字段,但不得合并 authority;例如 trace detail 可展示 `sourceSeq/sourceEventId` 审计信息,主 timeline 的 finalResponse 只能来自 sealed message/part/turn facts。`workbench_aggregate_event` 是 projector 和 replay 的提交输入,不允许 Web/API/CLI 直接把事件 payload 拼成另一套 timeline。 +Assistant 输出必须按 message/part authority 建模。一个 turn 内可以有多个 assistant text/tool/diagnostic/final response part,每个 part 有稳定 order 和 status;final response 是 terminal commit sealed 的 final part,不是“最后一个非空文本”。同一 trace 只生成 `msg__agent` 这类单一 assistant 容器而缺少 part 身份、order 和 sealed 状态的实现,不能作为本规格的完成形态。 + 运行中和终态 timing DTO 必须保持可审计:`elapsed`、`recent update`、轮次完成耗时和 trace 首尾耗时的用户可见差异只能来自 durable projection 或上游事件时间戳,不能由 Web reducer、组件、ticker、fake-server、web-probe 或 CLI renderer 在读侧修正。前端只允许把 `now - startedAt`、`now - lastEventAt` 或 sealed `durationMs` 格式化成中文文案;不得保存 per-message timing floor、recent age cache、sealed duration cache、monotonic repair state 或后台 tab resume 补偿状态。若这些值在采样中出现归零、非单调、跳秒或和 terminal commit 不一致,系统必须暴露 projection/timing diagnostic 并修 writer/finalizer/read model、SSE/outbox 或 AgentRun source event,而不是在 UI 层滤波。 -### 5.6 durable projection outbox 与 reconciler +### 5.7 durable projection outbox 与 reconciler Projection outbox 是 durable commit notification log,与 projection facts 和 aggregate event stream commit 在同一数据库事务提交。outbox 的作用是让 SSE、CLI 和其他消费者从 durable cursor 恢复实时通知,不依赖进程内内存 listener。aggregate event stream 表达“发生了什么”和顺序权威;outbox 表达“某次投影 commit 可以通知消费者”,二者不得分叉成两套 lifecycle truth。 @@ -306,7 +322,7 @@ Outbox 每行至少包含 `outboxSeq`(全局单调)、`eventSeq`、`aggregat Reconciler 是后台 worker,消费 outbox 和 checkpoint 驱动 finalizer 追平。它取代旧的请求/轮询式 `syncAgentRunChatResult` 推进模式:AgentRun result sync 只负责拉取/写入 projection source state,事件推进由 outbox/reconciler 管道统一处理。 -### 5.7 SSE cursor replay 架构 +### 5.8 SSE cursor replay 架构 ```mermaid sequenceDiagram @@ -326,7 +342,7 @@ sequenceDiagram SSE handler 不再直接订阅内存 `traceStore.subscribe`。它只从 durable outbox 读取:连接建立时执行 replay(按 `afterSeq` 查询历史 commit notification),replay 完成后切换到 live tail(poll 或 LISTEN/NOTIFY 等价机制)。cloud-api 滚动、多副本和多用户同看同 trace 时,SSE 从同一 durable outbox 恢复,不丢 terminal/sealed 状态。 -### 5.8 DB 唯一约束与幂等保障 +### 5.9 DB 唯一约束与幂等保障 `workbench_trace_events` 表必须有 `(trace_id, source_event_id)` 唯一约束,防止并发或历史数据写入重复 source event。allocator 查询 sourceEventId 复用 projectedSeq 时,数据库层必须硬约束阻断重复写入。 @@ -336,7 +352,7 @@ Projection outbox 的 `outboxSeq` 必须全局单调递增,可通过数据库 Aggregate event stream 必须同时具备 `(eventSeq)` 全局单调约束、`(aggregateId, aggregateSeq)` aggregate 内单调唯一约束和来源幂等唯一约束。来源幂等键至少覆盖 `sourceRunId`、`sourceCommandId`、`sourceEventId` 或等价 source result id;admission、cancel 和 replay/reprojection 这类本地事件必须有稳定 deterministic eventId。任何需要重排、补 terminal 或修 timing 的逻辑都必须追加受控事件并重新投影,不得直接改 read model 或前端 store。 -### 5.9 0读侧推理验收矩阵 +### 5.10 0读侧推理验收矩阵 | 层 | 禁止模式 | 验收方式 | | --- | --- | --- | @@ -344,10 +360,11 @@ Aggregate event stream 必须同时具备 `(eventSeq)` 全局单调约束、`(ag | 后端 SSE handler | `traceStore.subscribe` / 内存 trace snapshot | 源码扫描 + cloud-api 重启后 SSE cursor 恢复验证 | | 后端 trace snapshot | `events.at(-1)` 推导 status/lastEvent | 源码扫描 + 矛盾 events 样本测试 | | 后端 compact result | result/traceSummary/agentRun/memoryTrace 混合 | 源码扫描 + GET 路径不访问旧 compact path 验证 | +| 后端 message projection | trace-level assistant id 或 firstNonEmpty/fallback 选择 final response | 源码扫描 + 多 assistant part / running final 空值样本 | | 前端 store | `events.at(-1)` 推导 activity/label/status | 源码扫描 + fake-server Playwright | | 前端 subscription | turn poll + trace poll + event tail 合并 lifecycle | 源码扫描 + SSE gap/timeout Playwright | | 前端 timing | `lastEventAgeMs` 作为相对时间权威 | 源码扫描 + fake clock Playwright | -| CLI renderer | `lastEvent.status` 推 trace lifecycle | 源码扫描 + CLI 同源语义验证 | +| CLI renderer | `lastEvent.status` 推 trace lifecycle 或 DOM 行顺序选择 final response | 源码扫描 + CLI 同源语义验证 | 每层验收必须先用负向 fixture 形成红灯,再用修复后代码证明通过。负向源码扫描是辅助证据,不能替代 fake-server Playwright 与 D601 v0.3 web-probe。 @@ -379,6 +396,8 @@ AgentRun events 进入 Workbench trace facts 时,FactsStore 必须把 filtered Facts store 必须支持幂等 upsert、terminal commit、projection checkpoint、diagnostic update、open checkpoint 扫描、running/projecting turn 扫描和按 owner visibility 过滤的分页查询。`agent_sessions.session_json` 可作为迁移期 metadata 来源,但不得继续承载业务 projection 真相。 +Facts store 必须把 assistant 文本、tool/command 行、diagnostic 和 final response 持久化为 message/part facts。对同一 turn/trace 的 assistant 输出,必须能按 part order 恢复 running 中间态、steer 后续输出和 terminal final part;不得只持久化 trace-level assistant message id,再让读取端从 `text/content/message/finalResponse` 的 first-non-empty/fallback 链条重建主消息。 + ### 6.2 WB-PROJ-REQ-002 projection writer | 编号 | 短名 | 主责模块 | 关联模块 | @@ -389,6 +408,8 @@ WorkbenchProjectionWriter 应作为唯一写入口,把 admission facts 和 nor Writer 必须使用稳定幂等键写入 messageId、partId、turnId、traceId、sourceRunId、sourceCommandId、sourceSeq/sourceEventId 和 projectedSeq。重复 event、重放 result、重启恢复或 checkpoint replay/reprojection 不得创建重复 assistant message、重复 terminal event 或冲突 session summary。 +Writer 必须在 admission 阶段为 user message、assistant message 和初始 part 分配稳定身份,并在后续 event/result/steer/cancel projection 中追加或封存对应 part。assistant 文本增量、工具输出、backend diagnostic、steer 确认、cancel 结果和 final response 不得写入同一个无 order 的文本字段;terminal final response 必须是明确 partKind/status/sealedAt 的 sealed part。 + Writer 的 terminal commit 必须同时写入 assistant final text、finalResponse part、message/turn terminal status、trace terminal fact、session running=false、checkpoint 和 `sealedAt` 或等价 sealed 标记。Completed、failed、canceled、blocked 都是 terminal result;若 AgentRun 或 projection 给出可读 final error/blocker,它必须作为 sealed final response 的一种结果写入,而不是作为后续 transport diagnostic 写入主正文。 ### 6.3 WB-PROJ-REQ-003 projection finalizer @@ -431,6 +452,8 @@ Read model 严禁读侧推理。它不得从 `result?.status ?? trace?.status` 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 输出 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。 + Projection health 必须只解释 durable diagnostic。`projectionStatus=projecting` 且 `lastProjectedSeq < sourceLatestSeq`、checkpoint stale 超过受控阈值、存在 `lastError/blocker` 或 attempt 正在退避时,不得返回 `caught-up`;应返回 `projecting`、`degraded` 或 `stalled`,并暴露 `blocker`、`lastProjectedSeq`、`sourceRunId`、`sourceCommandId` 和更新时间。`caught-up` 只能表示对应 command checkpoint 已追平可见 source facts,或 source 明确仍 running 且无新可投影 facts。 Trace event page 必须按 `projectedSeq` 单调返回,响应范围必须合法。非空页满足 `range.fromSeq <= range.toSeq`;空页必须用明确空集合和空 range/null range 表达,禁止出现 `fromSeq=5/toSeq=4` 这类非法区间。`projectedSeq` 是 Workbench 读取 cursor,`sourceSeq` 只作为来源审计字段;二者不得混用。 @@ -461,6 +484,8 @@ Web server-state 必须分仓表达 `messageProjection`、`traceDetail`、`sessi active session 只能由 route sessionId 或用户显式选择产生。REST detail、session list、workspace summary、SSE event、trace page 或 turn snapshot 晚到时,只能更新其声明 sessionId 对应的 server-state bucket;不得重新选择 session、替换 URL、覆盖当前消息区、改变 composer 或把另一个 session 的 running/terminal 状态投射到当前页面。缺少 sessionId 或 session/trace 不匹配的 event 必须进入 authority mismatch diagnostic,不得猜测、fallback 或应用到当前 active session。 +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`。 Web reducer 必须提供 sealed guard:对已经有 `sealedAt` 或等价 sealed 标记的 assistant message/turn,turn 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,而不是读侧失败。 @@ -493,6 +518,8 @@ Read model degradation 回归必须覆盖 PostgreSQL pool connect/query timeout Sealed final response 回归必须构造 completed assistant 主正文已经展示后,`/v1/workbench/turns/{traceId}`、`/v1/workbench/traces/{traceId}/events`、SSE 或 realtime diagnostic 发生 timeout/500/gap/close 的样本,断言主消息正文、finalResponse、message status 和 turn terminal 保持 sealed 不变,诊断只出现在 trace detail、transport diagnostic、session health 或消息详情入口。回归还必须覆盖 trace detail 延迟返回、分页缺口和用户手动展开/折叠状态,确保 late update 不 remount 主消息卡片、不重置用户控制状态。 +多 turn / running steer 回归必须覆盖连续 prompt、运行中 steer、terminal 后再次 prompt 和 cancel no-op 的组合。用例应断言 `turn-summary` 的每个用户消息绑定正确 traceId/runId/commandId;`trace-frame --turn N` 与 `trace-frame --trace-id ` 读取同一 turn;running frame 的 `Final Response` 为空;terminal frame 的 final response 是 sealed assistant final part;后续 steer/cancel 不会让 analyzer 把普通 prompt 标成 `prompt-routed-to-steer`,也不会让 trace-frame 误选用户 prompt、backend diagnostic 或 steer accepted 中间文案作为最终回答。 + 0repair 验收必须先在 fake-server + Playwright 中形成红灯:fresh context 直达 session A、普通点击切到 session B、A 的 delayed detail/turn/trace/SSE 晚到、用户没有提交 B 的新消息时,B 的 URL、active tab、message card、composer 和 trace detail 不得出现 A 的 user/agent 文本、running 状态或 terminal trace。web-probe 必须连续截图观察提交前、running、中间态、terminal 和晚到事件;不得用 reload、切换 session、`sessionRepair`、`realignFreshSession`、localStorage 修改或 helper 自动点击修正页面后再判通过。 ### 6.8 WB-PROJ-REQ-008 code reference rule @@ -541,6 +568,22 @@ Timing 字段必须由 aggregate event stream 和 projector 统一产生。`star 迁移实现必须删除旧的多来源仲裁路径。`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 收敛。 +### 6.11 WB-PROJ-REQ-011 CLI trace view and analyzer contract + +| 编号 | 短名 | 主责模块 | 关联模块 | +| --- | --- | --- | --- | +| WB-PROJ-REQ-011 | CLI视图 | PJ2026-010401080311 CliTraceView | [Web工作台](PJ2026-010401-web-workbench.md)、[Workbench性能](PJ2026-01060505-workbench-performance.md)、[API契约](PJ2026-010403-api-contract.md) | + +`web-probe collect/observe` 是唯一采样保存入口,负责按 5 到 10 秒级别采集 DOM/API/console/network 等事实,并把 frame、sample、request、traceId、runId、commandId 和时间戳写入采样目录。`web-probe analyze` 的 trace 截图是从这些采样事实或同源 read model 渲染出来的文字视图,不得另存一份“截图事实”,也不得在 analyze 阶段重新访问页面来补采样。 + +CLI 视图必须提供两层:第一层 `turn-summary` 展示多 turn 摘要,至少包含用户消息摘要、turn index、traceId、runId、commandId、status、elapsed、recent、warning 和最终内容摘要;第二层 `trace-frame` 按 turn、traceId、sample seq 或时间点渲染单 turn 的有序事件行。`trace-frame` 必须固定包含 `Final Response` 分区;running 或未 sealed 时显示 `(空内容)`,terminal sealed 后显示 sealed final part 的实际内容。该分区不得从用户 prompt、backend diagnostic、中间 assistant text、steer accepted 文案或 DOM 最后一行回退。 + +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 补偿吞掉。 + +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 和相关 ids;analyzer 不得因为同一时间窗内出现 steer 请求就把此前 prompt 判定成 `prompt-routed-to-steer`。 + ## 7. 过程控制 本规格的执行证据保留在对应 GitHub 执行 issue、PR、web-probe 报告和 Playwright artifact 中;规格正文不承载长证据、运行日志或一次性排障流水。 @@ -548,3 +591,5 @@ Timing 字段必须由 aggregate event stream 和 projector 统一产生。`star 本专项关闭前,执行 issue 必须回写以下证据:专项 SPEC commit、后端写点/读点清单、旧路径最终状态、PR、D601 v0.3 发布证据、fake-server Playwright 结果、public origin web-probe 的 script SHA/runDir/report SHA/截图 SHA,以及父级 Web/API/HWLAB接入/Agent编排规格是否仍与实现一致。Workbench Redis 派生读缓存执行 issue 为 [#1870](https://github.com/pikasTech/HWLAB/issues/1870),P0 SPEC-first 子 issue 为 [#1871](https://github.com/pikasTech/HWLAB/issues/1871),后续实现必须引用 `draft-2026-06-22-p1-workbench-redis-derived-cache`。 web-probe 历史窗口 trace 乱序、完成行非最后、耗时不一致和红色 finding 的架构修复执行 issue 为 [#2055](https://github.com/pikasTech/HWLAB/issues/2055)。阶段跟踪必须以子 issue 为准:P0 SPEC-first [#2057](https://github.com/pikasTech/HWLAB/issues/2057)、P1 event schema/projector [#2058](https://github.com/pikasTech/HWLAB/issues/2058)、P2 read/SSE seq replay [#2059](https://github.com/pikasTech/HWLAB/issues/2059)、P3 Cloud Web trace/timing 补偿清理 [#2060](https://github.com/pikasTech/HWLAB/issues/2060)、P4 web-probe/OTel 验收固化 [#2061](https://github.com/pikasTech/HWLAB/issues/2061)、P5 D601/v03 长程验收与旧路径删除 [#2062](https://github.com/pikasTech/HWLAB/issues/2062)。后续实现必须引用 `draft-2026-06-24-p0-aggregate-event-stream`,并在关闭各阶段 issue 时说明是否仍存在读侧 repair、到达顺序排序、UI timing fabrication 或 probe 侧补偿。 + +Workbench turn/message/trace 投影 authority 与 CLI trace 视图收敛的架构修复执行 issue 为 [#2079](https://github.com/pikasTech/HWLAB/issues/2079),正式 20 轮长程采样父 issue 为 [#2072](https://github.com/pikasTech/HWLAB/issues/2072),运行中 steer 与前 1-5 轮采样子 issue 为 [#2078](https://github.com/pikasTech/HWLAB/issues/2078)。后续实现必须引用 `draft-2026-06-24-p1-opencode-message-part-authority`,并在各阶段子 issue 中说明是否已经消除 final response 误选、turn-summary/trace-frame traceId 误归因、completion row 非最后、duration 不一致、prompt/steer/cancel 边界误判和 analyzer-only 误判。