diff --git a/project-management/PJ2026-01/specs/PJ2026-010401-web-workbench.md b/project-management/PJ2026-01/specs/PJ2026-010401-web-workbench.md index 0506d761..b4f42f31 100644 --- a/project-management/PJ2026-01/specs/PJ2026-010401-web-workbench.md +++ b/project-management/PJ2026-01/specs/PJ2026-010401-web-workbench.md @@ -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-p1-view-local-timing-ticker; PJ2026-0104010803 唯一投影 draft-2026-06-20-p0-durable-facts-model; draft-2026-06-20-p1-zero-split-durable-realtime | +| 实现引用版本 | draft-2026-06-20-p0-long-running-workbench; draft-2026-06-20-p0-error-diagnostics; draft-2026-06-20-p1-view-local-timing-ticker; 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 | | 需求规格模板 | [ISO/IEC/IEEE 29148 需求规格模板](../../templates/iso-iec-ieee-29148-requirements-spec-template.md) | | 上级规格 | [PJ2026-0104 客户端](PJ2026-0104-client.md) | | 规格治理索引 | [规格治理](spec-governance.md) | @@ -219,6 +219,8 @@ Web工作台在本规格中只保留前端消费边界:Cloud Web reducer、sel 长程可靠 Workbench 的用户可见验收矩阵如下,后续实现和回归验证必须直接引用这些稳定场景,而不是用单次 canary 或局部截图替代。 +长程可靠 Workbench 还必须覆盖 AgentRun terminal outbox 恢复场景:同一 session 连续两轮提交后,即使 cloud-api 在中途重启、events page 没有 terminal event 或第 2 轮后 `lastTraceId` 已变化,用户仍应在第 1/第 2 轮 message card 看到 sealed terminal final response,并能打开每个历史 trace 的 events page。用户在上游 command 已 terminal 后点击 cancel,不得把 completed final response 改写为 canceled;Web 只能展示 already-terminal/no-op 或真实 terminal projection 的状态。 + | 场景 | 权威事实 | 用户可见结果 | 禁止路径 | | --- | --- | --- | --- | | 新建 session 并发送最小 prompt | admission 写入稳定 sessionId、turnId、traceId、userMessageId、assistantMessageId | 当前消息区立即显示用户消息、assistant pending 和可追踪 running 状态 | 用 workspace selected snapshot 或本地 stub 伪造 session/message | 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 a875e68a..ba32baac 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-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 | | 需求规格模板 | [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-010205 HWLAB接入](PJ2026-010205-hwlab-dispatch.md)、[PJ2026-0102 Agent编排](PJ2026-0102-agent-orchestration.md) | @@ -35,6 +35,8 @@ Workbench唯一投影负责把 AgentRun 执行事实收敛成 HWLAB 自有的 du 本专项的目标状态是:AgentRun run、command、event 和 result 只作为执行事实输入;HWLAB 只有 `WorkbenchProjectionWriter`、`WorkbenchProjectionFinalizer` 写入 Workbench facts;cloud-api boot/background scheduler 是 startup/resume 的 authority,负责扫描 durable open checkpoint 和 running/projecting turn 并恢复追平;所有 `GET /v1/workbench/*` 和兼容读路径都只通过 `WorkbenchReadModel` 读取,不在读取时调用 AgentRun、Code Agent manager、trace polling、result polling 或 workspace repair 推进事实。Workbench 必须满足 0repair:页面、GET、SSE、fake-server 和 web-probe 都不得通过 reload、切换 session、`sessionRepair`、`realignFreshSession`、localStorage truth 或 read-through repair 把已经分裂的 route/session/message/trace 状态补成看起来正确。 +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 主正文。 ### 2.2 范围内 @@ -79,6 +81,8 @@ Terminal final response 是 Workbench 投影的 sealed 用户结果。Projection | result sync state | AgentRun `/result` 归档或终态补全的低优先级状态;它不得阻塞运行中 events projection。 | | sourceLatestSeq | finalizer 在本次或最近一次拉取中观察到的 AgentRun command 最新 sourceSeq,用于诊断投影 lag;它不得替代 lastSourceSeq 成为已提交事实。 | | 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。 | | 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。 | @@ -378,6 +382,10 @@ Finalizer 的 cursor 必须以 command 和 checkpoint 为边界追平。对同 运行中投影必须以 AgentRun events 增量接口为主读源:finalizer/resume loop 使用 durable `lastSourceSeq` 调用上游 `events?afterSeq=&limit=`,再按 commandId 和 seq window 过滤并提交 Workbench facts。running loop 不得为了恢复或刷新进度同步等待 `/result`,也不得在每次拉取时从 run seq 0、长 trace、result envelope 或历史 events 数组重新扫描。发现 terminal evidence 后,finalizer 可以把 projection 标记为 terminal/pending-result,并把 `/result` 同步交给后台低优先级归档;`/result` timeout 只能更新 `resultSyncState`、`lastError/blocker` 和 retry/backoff,不得阻止 events cursor 继续推进。 +Projection resume 对 `projectionStatus=projecting/degraded/stalled` 或等价 running turn 超过受控阈值的 trace,必须周期性读取 AgentRun command result authority,不能只等待 events page 中出现 terminal event。Command result 已 terminal 时,finalizer 必须写入同一 terminal commit;若 result 拉取失败或超时,`resultSyncState=timed_out/failed/pending` 必须进入 projection state 并按 backoff 重试。长期 `terminal=0 failed=0` 的 resume pass 不能被记录成无问题完成;它必须暴露候选数、result sync 状态和下一次 retry 信息。 + +Cancel 是受控 mutation,不是终态仲裁。`/v1/agent/chat/cancel` 或 Workbench cancel mutation 在转发 AgentRun cancel 前,必须先检查本地 sealed terminal projection,并在可能时读取当前 run/command result 或 command state。若上游 command 已 `completed`、`failed`、`blocked`、`canceled` 或等价 terminal,cancel 不得把 Workbench turn 改写为 canceled;应返回 already-terminal/no-op 或触发 terminal projection,使 Workbench 保持真实 terminal final response。 + Finalizer 对 sealed turn 的后续重试只能推进 trace detail completeness、result archive、diagnostic health 或受控 replay/reprojection;不得把 `/result` timeout、events gap、provider transport error、poll idle timeout 或 hydration failure 映射成新的 assistant 主正文、finalResponse、message status 或 turn terminal status。 ### 6.4 WB-PROJ-REQ-004 read model and pure GET @@ -398,6 +406,10 @@ Projection health 必须只解释 durable diagnostic。`projectionStatus=project Trace event page 必须按 `projectedSeq` 单调返回,响应范围必须合法。非空页满足 `range.fromSeq <= range.toSeq`;空页必须用明确空集合和空 range/null range 表达,禁止出现 `fromSeq=5/toSeq=4` 这类非法区间。`projectedSeq` 是 Workbench 读取 cursor,`sourceSeq` 只作为来源审计字段;二者不得混用。 +Trace event page 的可见性必须以 durable turn/message/session 关系和 owner visibility 判断,不能只依赖 session 当前 `lastTraceId`。同一 session 后续产生新 trace 后,旧 trace 只要对应 turn、message 或 projection facts 对当前 actor 可见,`GET /v1/workbench/traces/{traceId}/events` 仍应返回 200 和 trace page;若 trace facts 缺页或 projection 未追平,应返回 projection blocker/degraded,而不是 actor 不可见 404。真正 archived/deleted/not-found 的生命周期必须来自 canonical lifecycle projection。 + +Workbench facts/read model 的 PostgreSQL pool connect/query timeout 必须被转换为结构化 degraded/readiness/blocker 响应和日志,不能逃逸为 cloud-api 进程未捕获异常。Liveness 不应因单次 Workbench read query timeout 变为 EOF;readiness 或 route 响应应暴露 route、store method、timeout kind、runtime readiness、projection candidate count、相关 trace/run/command 摘要和 `valuesRedacted=true`。 + ### 6.5 WB-PROJ-REQ-005 compat wrapper | 编号 | 短名 | 主责模块 | 关联模块 | @@ -446,6 +458,10 @@ fake-server Playwright 还必须覆盖 timing display 红灯:在固定 project AgentRun rolling recovery 回归必须覆盖上游 stale ledger 与不可恢复 blocker 样本:manager rolling 后 AgentRun diagnosis 仍处于 pending observation 时,Workbench list/detail/messages/turn/trace 应一致展示 projecting/degraded/stalled diagnostic;terminal facts 后续提交后,同一 durable projection writer/finalizer 应自然推进 sealed final response。测试不得通过 GET read-through、Web reload、切换 session、result fallback 或 fake-server 后门把 pending 状态改成 completed。 +Terminal outbox recovery 回归必须覆盖 events page 缺 terminal、command result 已 completed 且含 final response 的样本。测试应断言 projection resume 会强制读取 command result authority、写入 completed terminal commit,并让 session/detail/messages/turn/trace events 全部从 durable facts 看到同一 terminal;随后用户 cancel 应返回 already-terminal/no-op,不得把 completed 改成 canceled。历史 trace 可见性回归必须覆盖同一 session 后续新 trace 成为 `lastTraceId` 后,旧 trace events 仍以 turn/message 关系返回 200 或明确 projection blocker。 + +Read model degradation 回归必须覆盖 PostgreSQL pool connect/query timeout:Workbench facts query 应返回结构化 503/degraded 或 readiness/blocker,并记录脱敏诊断;测试不得通过未捕获异常、进程退出、liveness EOF 或 GET read-through repair 判定通过。 + 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 主消息卡片、不重置用户控制状态。 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 自动点击修正页面后再判通过。 diff --git a/project-management/PJ2026-01/specs/PJ2026-010403-api-contract.md b/project-management/PJ2026-01/specs/PJ2026-010403-api-contract.md index 7be4a466..f5d1e52e 100644 --- a/project-management/PJ2026-01/specs/PJ2026-010403-api-contract.md +++ b/project-management/PJ2026-01/specs/PJ2026-010403-api-contract.md @@ -19,7 +19,7 @@ | 短名 | API契约 | | 层级 | L2 课题 | | 状态 | 已生效 | -| 实现引用版本 | draft-2026-06-20-p0-workbench-pure-read-api; draft-2026-06-20-p0-error-diagnostics; draft-2026-06-20-p1-view-local-timing-ticker; PJ2026-0104010803 唯一投影 draft-2026-06-20-p0-durable-facts-model; draft-2026-06-20-p1-zero-split-durable-realtime; PJ2026-01050105 Web鉴权 draft-2026-06-18-p0-auth | +| 实现引用版本 | draft-2026-06-20-p0-workbench-pure-read-api; draft-2026-06-20-p0-error-diagnostics; draft-2026-06-20-p1-view-local-timing-ticker; 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; PJ2026-01050105 Web鉴权 draft-2026-06-18-p0-auth | | 需求规格模板 | [ISO/IEC/IEEE 29148 需求规格模板](../../templates/iso-iec-ieee-29148-requirements-spec-template.md) | | 上级规格 | [PJ2026-0104 客户端](PJ2026-0104-client.md) | | 规格治理索引 | [规格治理](spec-governance.md) | @@ -209,6 +209,12 @@ Trace resource 必须支持按事件位置继续读取,例如 `sinceSeq` / `cu 所有 GET path 必须无业务副作用。需要 repair、finalize、sync 或 migration 时,应由后台 finalizer、显式 mutation 或持久化投影入口执行,不能隐藏在 workspace、conversation、turn 或 trace GET 中。所有分页响应必须包含稳定 cursor 或 seq 边界;trace event page 的分页 cursor 使用 HWLAB `projectedSeq`,并保留 AgentRun `sourceSeq/sourceEventId` 作为来源审计字段,二者不得重叠或互相替代。所有错误响应必须包含 route、status、code/blocker、redacted actor、target resource 和下一步建议。 +`GET /v1/workbench/traces/{traceId}/events` 的可见性必须以 trace 所属 turn/message/session durable facts 和当前 actor visibility 判断。历史 trace 不再是 session 当前 `lastTraceId` 时仍应可读;缺页或投影滞后应返回 projection blocker/degraded,而不是 `workbench_trace_not_found` 的 actor 不可见误导。 + +Cancel mutation 在转发 AgentRun cancel 前必须核验本地 sealed terminal 或上游 run/command state。若 command 已 terminal,API 应返回 already-terminal/no-op 或触发 terminal projection,不得把 completed/failed/blocked 等真实 terminal 改写为 canceled。Workbench read paths 仍保持纯读,不在 GET 中执行该核验或修复。 + +Workbench read-model dependency timeout 必须返回结构化 degraded/readiness/blocker 响应;PostgreSQL pool connect/query timeout 不得作为未捕获异常杀死 cloud-api 进程,也不得让 liveness 因单次 Workbench read query 变成 EOF。 + 新增或重构的核心后端文件头部必须标注遵循的 SPEC 编号、短名和实现引用版本,例如 `SPEC: PJ2026-0104010803 唯一投影 draft-2026-06-20-p0-durable-facts-model; draft-2026-06-20-p1-zero-split-durable-realtime; PJ2026-010403 API契约 draft-2026-06-20-p0-workbench-pure-read-api; PJ2026-010205 HWLAB接入 draft-2026-06-17-r0`,并简述文件职责。实现文件不得只写 issue 编号、`latest` 或 `current` 作为规格引用。 ### 6.4 CLIENT-API-REQ-004 资源与管理接口