09: real 生命周期与 mock 同构启动规格(来自决策票 04)
Type: spec
Status: ready-for-agent
Label: ready-for-agent
Blocked by: 04
Category: spec
本规格固化决策票 04的维护者确认结果,作为同一张 map的子规格单。它是 real 数据源生命周期的实施输入;不覆盖 .scratch/cockpit-shell-panel-layout/spec.md。
进度:100%
规格文档已发布为 ready-for-agent 并挂到同一决策 map;100% 仅指规格交付,不表示 real 分支启动逻辑已实现。实现按本规格接线;联调基线与证据登记归决策票 05。
Problem Statement
服务层已有 mock/real 统一出口(planCockpitRequest / requestCockpitJson),但外壳的自动初始化、面板取数、WebSocket 与新鲜度流程全部只写在 mock 分支里:dataSource=real 时外壳只加载配置,不取任何数据、不连推送、不启动新鲜度。后果是:
- real 模式是空壳:切到 real 只能证明单个 service 能发请求,页面不会真正进入运行状态(地图不加载、面板不取数、无推送、无新鲜度)。
- 失败语义无法验证:real 下的失败路径(不可达、未配置地址、快照过期、字段缺失)没有统一的呈现契约,容易被「静默无数据」掩盖。
- 刷新调度分散:地图摘要自动加载(决策 02)与数据时间刷新(决策 03)都指向「刷新时机」,若无唯一权威会各自实现、行为打架。
- mock 与 real 的分界不清:什么情况下允许回退 mock、配置缺失如何表现,缺少明确边界。
Solution
从部署与值班视角:dataSource 只决定 transport,不决定页面生命周期。real 模式下 /cockpit 与 mock 一样自动进入运行状态——地图摘要自动加载并渐进取页、当前模式面板自动取数、WebSocket 直订并接收增量、新鲜度周期自动重算;失败只影响对应分区并如实标注,不回退 mock、不整页遮罩。
核心语义(详见决策票 04 的 Answer):
- 完全同构启动:real 照搬 mock 分支的全部自动初始化;差别只在 transport。
- 同一套失败语义:失败分区
UNAVAILABLE(或对应原因),保留成功分区与地图骨架。
- WS 只定生命周期:连接 / 直订 / 重连 / 退避归本规格;增量帧字段解析归决策 01 的执行票。
- 新鲜度 tick 全套:地图刷新 + 当前模式面板取数,统一记账(连续两周期失败=过期/不可用)。
- 刷新调度唯一权威:决策 02、03 的执行票引用本规格,不另实现自动刷新。
- 严格不回退 mock:real 失败即
UNAVAILABLE;未配接口地址同样如实报不可用(NO_BASE_URL)。
- 验收是端到端证据:证明「生命周期在跑」,不只是「service 能发请求」。
User Stories
- 作为值班人员,我想在 real 模式进入
/cockpit 后地图自动加载并渐进取页,以便不需要手动触发就能看到点位。
- 作为值班人员,我想当前模式的面板自动取数,以便进入系统就能看到业务读数。
- 作为值班人员,我想在四个一级模式之间切换时各模式面板按需取数,以便切换后内容即时可用。
- 作为值班人员,我想 real 模式下 WebSocket 自动直订并接收状态增量,以便点位状态实时更新。
- 作为值班人员,我想断线后自动退避重连,以便网络抖动不会让推送永久失效。
- 作为值班人员,我想新鲜度周期自动重算地图与当前模式面板,以便界面上的时间与状态持续可信。
- 作为值班人员,我想某个分区取数失败时只看到该分区标「数据不可用」,其它分区照常显示,以便不被一处故障遮住全屏。
- 作为值班人员,我想 real 失败时界面不会偷偷换成假数据,以便我知道看到的是不是真数据。
- 作为值班人员,我想未配置接口地址时界面如实说明不可用,以便部署问题能被立刻发现。
- 作为值班人员,我想快照过期这类可恢复失败被单独识别并提示刷新,以便知道该做什么。
- 作为部署人员,我想 real 与 mock 的差异只在配置(数据源与地址),以便演示与生产走同一套代码路径。
- 作为部署人员,我想不提供运行时切换数据源的入口,以便「当前看的是真数据还是假数据」始终明确。
- 作为维护者,我想刷新调度只有一个权威来源,以便自动加载与数据时间刷新不会各自为政。
- 作为维护者,我想失败不做无限自动重试,以免制造请求噪音,恢复靠周期刷新与手动入口。
- 作为维护者,我想走查能证明 real 生命周期确实运行(自动取数、推送、周期刷新),以便不把「service 能发请求」当成接线完成。
Implementation Decisions
启动编排
- 外壳的启动逻辑抽出可注入的启动计划:输入
dataSource 与配置,输出启动动作清单(地图装载器初始化与首批取数、共享详情分区、四模式面板取数、WS 启动、新鲜度启动);real 与 mock 产出同一套动作,仅 transport 不同。
- real 分支不注册契约化 mock;mock 分支行为保持不变。
- 启动顺序保持「先把筛选交给地图,再取第 1 页」的既有纪律。
失败与边界
- 失败一律收成分区状态(
UNAVAILABLE 或对应原因码),保留其它分区与地图骨架;不回退 mock、不整页遮罩、不跳登录。
- real 未配
api.baseUrl:所有分区 UNAVAILABLE、原因 NO_BASE_URL,界面如实说明。
- 无运行时切换数据源入口;切换数据源=改配置重启。
- 失败不自动无限重试;恢复走新鲜度周期与手动刷新。
WebSocket 与新鲜度
- WS 沿用现有运行时服务入口(连接 / 直订 / 重连 / 退避);本规格不新增推送协议,不改增量帧字段语义(归决策 01)。
- 新鲜度 tick 扩展为:地图摘要刷新 + 当前模式面板取数;成败计入同一新鲜度模型(连续两周期失败分类)。
- 刷新调度是唯一权威;决策 02(自动分批与筛选变化刷新)、决策 03(数据时间)的执行票引用本规格。
职责边界
- 组件不直接拼 URL、不读凭据、不读后端裸字段;地址与数据源只由配置服务与适配层持有。
- 走查/演示钩子沿用既有全局键且只在 mock 分支生效;real 失败路径用真实网络中断模拟。
Testing Decisions
- 好测试的定义:只测外部行为与生命周期契约——real 与 mock 是否产生同一套启动动作、失败是否只影响对应分区、是否不存在回退 mock 的路径、未配地址是否如实不可用;不测组件内部实现。
- 主接缝(零依赖逻辑闸门):启动计划纯函数(输入配置 → 断言动作清单在 real/mock 下一致、real 不注册 mock)+ 服务层出口(
planCockpitRequest/requestCockpitJson 的失败收敛与 NO_BASE_URL)。先例:.scratch/cockpit-shell-panel-layout/verify/t04-config.mjs、t05-adapter.mjs、t15-websocket.mjs。
- 守卫与变异:静态守卫断言不存在「real 失败回退 mock」的代码路径与运行时切换入口;对守卫做变异测试确认有牙(先例 t05/t18)。
- 浏览器走查(
-walkthrough.mjs + data-*):配置 real 后进入 /cockpit → 地图自动加载、面板取数、WS 直订收帧、新鲜度 tick 执行;构造一个失败分区断言只有它标不可用;未配 baseUrl 断言全分区如实不可用。
- 验收与 #05 分工:本规格证明生命周期运行,环境/提交/数据快照登记归决策票 05。
Out of Scope
- 报警—工单增量的字段解析与合并(归决策票 01 的执行票)。
- 数据时间字段的语义与展示(归决策票 03 / 规格 08)。
- 地图摘要的状态筛选、自动分批与命中设备子集(归决策票 02 / 规格 07)。
- 联调基线、环境、提交哈希与证据登记(归决策票 05)。
- 后端接口实现与真实适配器(后端执行票)。
- 移动端、UE5、Docker、国产化认证及非驾驶舱领域。
Further Notes
- 本规格依赖决策票 04(已 resolved);它是后续基础设施、预警处置与总体概览共用的生命周期基线。
- 与决策 02、03 的衔接:两者的执行票引用本规格的刷新调度结论,不重复实现自动刷新。
- 未运行 build/debug 验证(遵循仓库 AGENTS.md 规则);本规格只交付实施输入。