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 0e731f61..ab124e22 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; PJ2026-0104010803 唯一投影 draft-2026-06-20-p0-durable-facts-model | +| 实现引用版本 | 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 | | 需求规格模板 | [ISO/IEC/IEEE 29148 需求规格模板](../../templates/iso-iec-ieee-29148-requirements-spec-template.md) | | 上级规格 | [PJ2026-0104 客户端](PJ2026-0104-client.md) | | 规格治理索引 | [规格治理](spec-governance.md) | @@ -80,6 +80,7 @@ Web工作台负责 HWLAB 登录后的浏览器主入口,使用户能够在同 | 正式公开入口 | 目标 node/lane 在 YAML 中声明的 Cloud Web public URL,用作用户访问、Playwright 验收、文档说明和故障复现入口。 | | Workbench Server State | Web 工作台从 REST snapshot、SSE event、trace page 和 submit optimistic 归一化得到的服务端事实缓存。 | | Timeline Projection | 只从 messages、parts、turn status 和 trace events 派生用户可见 timeline row 的渲染投影,不发请求也不写事实状态。 | +| 显示层本地 now | 浏览器组件为了展示“最近 X 秒前”和运行中“耗时 X 秒”而读取的当前时间;它只能作为渲染输入,不能写回 Server State、投影、session/message/turn lifecycle 或诊断事实。 | | 唯一投影 | [PJ2026-0104010803 Workbench唯一投影](PJ2026-0104010803-workbench-unique-projection.md) 定义的状态链路:AgentRun 执行事实只经 HWLAB projection writer/finalizer 写入 durable Workbench facts,REST、SSE、CLI、fake-server 和 Web 前端只消费该投影。 | | sealed final response | 唯一投影 terminal commit 写入的 assistant 主正文、finalResponse、message/turn terminal status 和 sealed 标记;它是主消息区用户结果的唯一权威,不被后续读侧诊断、trace hydration 失败、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 可以改变这些事实。 | @@ -214,7 +215,7 @@ flowchart LR [PJ2026-0104010803 Workbench唯一投影](PJ2026-0104010803-workbench-unique-projection.md) 是 Workbench 状态投影的唯一专项规格,集中定义 durable facts schema、`WorkbenchProjectionWriter`、`WorkbenchProjectionFinalizer`、`WorkbenchFactsStore`、`WorkbenchReadModel`、terminal commit、sealed final response、cloud-api 重启恢复、GET 纯读和代码引用规则。 -Web工作台在本规格中只保留前端消费边界:Cloud Web reducer、selectors、session rail、timeline、composer 和 Trace detail 只能消费该专项定义的 durable projection;SSE 只是 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。详细架构图、数据流图、关键时序图、0repair 和严禁读侧推理约束以专项规格为准,本文不再维护第二份投影架构正文。 +Web工作台在本规格中只保留前端消费边界:Cloud Web reducer、selectors、session rail、timeline、composer 和 Trace detail 只能消费该专项定义的 durable projection;SSE 只是 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 计算当前渲染字符串,不产生新事实。详细架构图、数据流图、关键时序图、0repair 和严禁读侧推理约束以专项规格为准,本文不再维护第二份投影架构正文。 长程可靠 Workbench 的用户可见验收矩阵如下,后续实现和回归验证必须直接引用这些稳定场景,而不是用单次 canary 或局部截图替代。 @@ -321,6 +322,8 @@ Web 工作台在会话恢复、session 切换、turn running、turn completed 工具调用和命令输出行必须正文优先:时间、状态、tool 名称和耗时只作为紧凑 header,不得占用独立左列或固定半屏宽度;stdout、stderr、error、patch、命令结果和助手 Markdown 应获得完整可用宽度。终端完成行和 Agent 最终消息应显示真实轮次耗时,耗时来源应使用 trace 事实、首尾事件时间或服务端 elapsed 字段,不得在已完成运行中显示 `00:00:00` 这类伪完成耗时。 +运行中的 Agent 消息头部可以用浏览器本地 now 推进“最近”和“耗时”文案,但计算输入只能是唯一投影返回的 `startedAt` 与 `lastEventAt` 时间戳。`lastEventAgeMs`、本地 `updatedAt`、trace tail、events 数组长度或轮询间隔不得进入用户可见相对时间显示路径。消息进入 completed、failed、canceled、blocked 等终态后,耗时必须使用 sealed projection 的 `durationMs` 或等价 sealed `finishedAt - startedAt` 口径,且停止随浏览器 now 增长。 + Trace阅读视图属于工作态高密度界面。重复 trace row、message card、trace panel、warning 和 inline code 的圆角、间距和 padding 应保持紧凑;默认视觉层级以内容可扫读为主,不使用大圆角、大留白、嵌套卡片或装饰性布局制造工作区浪费。原始 trace 仍必须可审计,但只能作为消息详情入口中的主动展开内容,不得成为默认阅读面或 Trace 展开体的一部分。 Trace阅读视图必须跟随 Code Agent turn 生命周期自动调整展开状态:任务开始、pending 转 running 或首次出现运行中 trace 时,工作台应自动展开对应 trace,使用户能立即看到进展;运行中的 turn 进入 completed、failed、canceled、timeout、blocked 或出现 final response 等终态时,工作台应自动折叠对应 trace,回到正文和最终结果优先的阅读状态。自动折叠不得删除 trace、隐藏错误或阻止用户手动重新展开查看详情。若会话或 trace 在补拉前已经处于 final/terminal 状态,后续历史分片补拉完成不得触发自动折叠,也不得覆盖用户手动展开或折叠状态。 @@ -353,6 +356,8 @@ Timeline Projection 只能从 messages、parts、turn status 和 trace events Web 工作台严禁读侧推理。`turn.status`、`message.status`、`session.running`、`trace terminal`、`finalResponse` 和 `projectionStatus` 只能来自唯一 durable projection。Trace event row 的 `completed` 只表示该事件或工具行完成,不能终结 turn;message text 为空只表示无可展示正文,不能生成占位 final response;session list summary、workspace selected state、localStorage mirror、轮询耗时或 elapsed timeout 不能改写 running/terminal。若 API 返回缺字段、投影滞后或多字段矛盾,Web 只能展示 loading、degraded、unknown 或 blocker,并把问题暴露给投影层修复,不得在 reducer、selector、组件或测试 helper 中合成“看起来正确”的状态。 +浏览器本地 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/turn,turn 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。 错误诊断中的 `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 错误只能追加诊断,不得把主消息正文改成“连接上游失败”“等待超时”或同类读侧错误态。 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 4470b9b6..3fb82f54 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-p0-durable-facts-model; draft-2026-06-20-p1-view-local-timing-ticker | | 需求规格模板 | [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) | @@ -82,6 +82,7 @@ Terminal final response 是 Workbench 投影的 sealed 用户结果。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。 | +| timing display input | Workbench ReadModel 输出的 `startedAt`、`lastEventAt`、`finishedAt` 和 sealed `durationMs` 等投影时间字段;它们是浏览器相对时间文案的唯一事实输入。浏览器本地 now 只能参与渲染计算,不成为 Workbench facts。 | | TraceEventPage | 由 WorkbenchReadModel 返回的 trace 分页事实,必须按 `projectedSeq` 单调排序,并保持 `range.fromSeq <= range.toSeq`;空页用显式 empty range 表达,不生成非法区间。 | | compat wrapper | 旧 `/v1/agent/*` 或 conversation path 的兼容包装层;只能调用 `WorkbenchReadModel` 或正式 mutation,不拥有第二套事实写入或 read-through repair。 | | read-through repair | GET 路径为了让页面“看起来正确”而同步调用 AgentRun、Code Agent manager、workspace repair、trace/result polling 或 billing finalizer 推进事实;本专项禁止该模式。 | @@ -242,7 +243,7 @@ Workbench durable facts 是唯一投影的持久对象模型。后续实现可 | `workbench_session` | `sessionId` | `owner`, `projectId`, `workspaceId`, `status`, `running`, `currentTurnId`, `summary`, `updatedAt` | session rail、detail 和 composer 可继续操作的用户态会话事实。 | | `workbench_message` | `messageId` | `sessionId`, `role`, `turnId`, `status`, `sealedAt`, `createdAt`, `updatedAt` | timeline message 容器;assistant terminal message sealed 后不被 diagnostic 覆盖。 | | `workbench_part` | `partId` | `messageId`, `type`, `status`, `text`, `tool`, `error`, `finalResponse`, `traceRef`, `order`, `sealedAt` | 主正文、工具、错误、final response 和 trace 摘要的组合事实。 | -| `workbench_turn` | `turnId` | `sessionId`, `traceId`, `status`, `terminal`, `failureKind`, `sourceRunId`, `sourceCommandId`, `startedAt`, `finishedAt`, `sealedAt` | 一次 prompt、steer、retry 或 cancel 的生命周期事实。 | +| `workbench_turn` | `turnId` | `sessionId`, `traceId`, `status`, `terminal`, `failureKind`, `sourceRunId`, `sourceCommandId`, `startedAt`, `lastEventAt`, `finishedAt`, `durationMs`, `sealedAt` | 一次 prompt、steer、retry 或 cancel 的生命周期事实;时间字段只由投影写入。 | | `workbench_trace_event` | `traceId` + `projectedSeq` | `sourceSeq`, `sourceEventId`, `type`, `status`, `timestamp`, `redactedPayload` | Trace event page 的唯一分页事实;`projectedSeq` 是 Workbench cursor。 | | `workbench_projection_checkpoint` | `sourceRunId` + `sourceCommandId` + `turnId` | `lastSourceSeq`, `lastProjectedSeq`, `sourceLatestSeq`, `status`, `attempt`, `nextRetryAt`, `lastError`, `blocker` | finalizer/resume 追平位置、幂等边界和阻塞恢复状态。 | | `workbench_projection_diagnostic` | `sessionId` + `turnId` + `traceId` | `projectionStatus`, `health`, `lag`, `blocker`, `sourceRunId`, `sourceCommandId`, `lastProjectedSeq`, `updatedAt` | GET、CLI、Web 和运维可见的投影健康诊断;不推进主事实。 | @@ -266,7 +267,7 @@ Facts schema 至少包含以下事实: | session | sessionId/conversationId/threadId/projectId/workspaceId/owner/status/running/currentTurnId/summary/updatedAt | 会话可见性、rail 摘要和当前 turn 指针;不保存 provider Secret 或完整 prompt 原文之外的敏感负载。 | | message | messageId/sessionId/role/status/turnId/sealedAt/createdAt/updatedAt | timeline message 事实;assistant final text 通过 part 表达,sealedAt 或等价字段表示主消息结果已封存。 | | part | partId/messageId/type/status/text/tool/error/finalResponse/sealedAt/traceRef/order | 文本、工具、错误、final response 和 trace 摘要的可组合展示事实;final response part sealed 后不得被读侧 diagnostic 覆盖。 | -| turn | turnId/sessionId/traceId/status/terminal/sealedAt/failureKind/sourceRunId/sourceCommandId/messageIds/startedAt/finishedAt | 一次用户提交、steer、retry 或 cancel 的生命周期事实。 | +| turn | turnId/sessionId/traceId/status/terminal/sealedAt/failureKind/sourceRunId/sourceCommandId/messageIds/startedAt/lastEventAt/finishedAt/durationMs | 一次用户提交、steer、retry 或 cancel 的生命周期事实;运行中相对时间显示只消费这些投影时间戳。 | | trace event | traceId/projectedSeq/sourceSeq/sourceEventId/type/status/timestamp/redactedPayload | Workbench trace page 的分页事实;projectedSeq 是唯一 cursor。 | | checkpoint | turnId/traceId/sourceRunId/sourceCommandId/lastSourceSeq/lastProjectedSeq/sourceLatestSeq/status/attempt/lastAttemptAt/lastError/blocker/updatedAt | finalizer 追平位置、幂等边界、恢复状态和退避诊断。 | | diagnostic | sessionId/turnId/traceId/projectionStatus/health/lag/blocker/sourceRunId/sourceCommandId/lastSourceSeq/lastProjectedSeq/sourceLatestSeq/updatedAt | GET 和运维可见的 projection 状态,不推进事实。 | @@ -353,6 +354,8 @@ SSE 只作为 projection commit 后的通知和加速,不是事实来源。SSE 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,而不是读侧失败。 +运行中相对时间显示必须保持单一权威: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 继续增长。 + ### 6.7 WB-PROJ-REQ-007 regression and validation | 编号 | 短名 | 主责模块 | 关联模块 | @@ -369,6 +372,8 @@ Durable recovery 红灯必须覆盖长 run、多 command 和大量旧事件场 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 还必须覆盖 timing display 红灯:在固定 projection timestamp、禁止 SSE/API 后续更新、使用浏览器 fake clock 推进的条件下,running 消息头部的“最近”和“耗时”应随 fake clock 前进;completed/failed/canceled/blocked 等终态消息的 sealed 耗时不得随 fake clock 前进。该测试不得读取内部 store,也不得通过更新 fixture、重新请求 API 或触发 projection repair 判定通过。 + 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。 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 主消息卡片、不重置用户控制状态。 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 9f9d0b87..be4ad301 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; PJ2026-0104010803 唯一投影 draft-2026-06-20-p0-durable-facts-model; 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; 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) | @@ -69,6 +69,7 @@ API契约负责定义 Cloud Web、HWLAB CLI 和自动化脚本共同使用的 RE | TurnSnapshot | 按 turnId 或 traceId 查询一次用户提交/steer/retry/cancel 生命周期、终态、message 指针和执行引用的 RESTful snapshot。 | | TraceEventPage | 按 traceId 和 cursor/seq 分页查询 trace events 的 RESTful page;只表达 trace 明细,不决定主 timeline 的 final response。 | | Message/Part | Web 和 CLI 共同渲染会话 timeline 的持久消息事实;message 表达角色、父子关系和终态摘要,part 表达文本、工具、文件、错误、trace 摘要等可组合内容。 | +| Timing metadata | Workbench DTO 中用于显示消息最近事件和轮次耗时的投影时间字段,至少包括 `startedAt`、`lastEventAt`、`finishedAt` 和 sealed `durationMs`;相对 age 只能由浏览器显示层用本地 now 临时计算。 | | EventStream | Cloud Web origin 下的 SSE 实时入口,用于推送 server.connected、session、turn、message、part、trace 和 workspace lifecycle 事件。 | | WorkbenchProjectionWriter | [PJ2026-0104010803 Workbench唯一投影](PJ2026-0104010803-workbench-unique-projection.md) 定义的唯一写入组件,把 AgentRun run、command、event 和 result 转换为 HWLAB durable Workbench facts。 | | WorkbenchReadModel | [PJ2026-0104010803 Workbench唯一投影](PJ2026-0104010803-workbench-unique-projection.md) 定义的唯一读模型,从 durable Workbench facts 组装 session detail、message page、turn snapshot、trace event page 和 session rail summary。 | @@ -123,6 +124,8 @@ API 契约严禁读侧推理。`GET /v1/workbench/*`、`/v1/agent/*` compat wrap | ProjectionDiagnostic | `sessionId`, `turnId`, `traceId`, `sourceRunId`, `sourceCommandId` | 无主 timeline 写权 | `projectionStatus`, `health`, `lag`, `lastProjectedSeq`, `sourceLatestSeq`, `blocker`, `updatedAt` | 把 diagnostic 文案覆盖 sealed final response。 | | TransportDiagnostic | `sessionId`, `turnId`, `traceId` 可空 | 无主 timeline 写权 | SSE、poll、hydration、result sync 或 network health | 把 transport failure 改写成 message/turn terminal 状态。 | +Workbench message/turn DTO 可以输出 timing metadata,但字段权威必须保持单一:`startedAt`、`lastEventAt`、`finishedAt` 和 sealed `durationMs` 来自 Workbench durable projection;running 消息的“最近/耗时”由浏览器以这些时间戳和本地 now 计算;terminal 消息使用 sealed `durationMs` 或等价 sealed `finishedAt - startedAt`,不得继续增长。`lastEventAgeMs` 若在迁移期保留,只能作为服务端观测时刻的快照或诊断字段,不能作为 Cloud Web 用户可见相对时间的显示权威,也不能参与 lifecycle、terminal、projectionStatus、session sorting 或 final response 选择。 + API 禁止路径清单如下:GET read-through repair、session JSON lifecycle 推理、result cache lifecycle 推理、trace tail lifecycle 推理、workspace/conversation authority、billing finalizer side effect、Code Agent manager shortcut、frontend status priority 和 compat wrapper second authority。需要推进 projection 时必须由 writer/finalizer、后台 scheduler、显式 mutation 或受控 replay/reprojection 入口执行;GET、SSE、CLI render 和 fake-server 只能观察结果。 ## 6. 原子需求