# Cockpit 外壳与面板布局(执行索引) Status: open Labels: wayfinder:map 本目录是 `/to-spec` + `/to-tickets` 的产物,不是 wayfinder effort:`spec.md` 是规格,`issues/01`–`23` 是实现票(01、02、03、04、05、06、07、08、09、10、11、12、**14 已关闭**、**15 已关闭**、**16 已关闭**、**17 已关闭**、**18 已关闭**、**19 已关闭**、**13 已关闭**、**20 已关闭(2026-09-29)**)—— **至此 01–23 的实现票全部关闭,16 块二级面板全部接上真实视图**(`PanelPlaceholder` 成为未注册 panelId 的兜底)。`issues/24`–`30` 是调研、待人工处理项与后续新增票(24 调研已关闭;25 待人工;26 watcher 崩溃已修复;**27 接入 Cesium 常驻 Viewer**,是票 03 的前置,已关闭;28 外壳填满容器已关闭;29 底图影像(配置驱动)已关闭;30 进入引导与视角预设已关闭)。 本 `map.md` 的唯一作用是把这个 effort 暴露给 issue tracker 面板——面板只枚举含 `map.md` 的 `.scratch//`,没有 `map.md` 的目录连同它的票都不会被列出。 `Labels: wayfinder:map` 在这里只用于让面板按**地图视图**渲染本 effort 的子票状态、进度与依赖分层;不代表本 effort 由 `/wayfinder` 驱动——推进仍走 `/implement`。 ## Destination 按 `spec.md`、`CONTEXT.md` 与 `docs/adr/0001`–`0008` 实现 `/cockpit` 外壳:单路由、四个一级模式、每个模式左右两栏共 4 个二级面板、中央地图常驻。语义已由 `.scratch/web-cockpit-decision-map/` 锁死,本 effort 不再发明 `mode`、`panelId`、相机或鉴权的含义。 ## Notes - 执行清单是 `issues/01`–`23`,按各票 `Blocked by` 的依赖顺序推进,一票一个全新上下文。 - **`issues/20`、`issues/22`、`issues/23` 此前只有前言的编号范围、没有逐票索引**:现按本目录既有的逐票引用写法补上 —— [20: 总体概览模式面板](issues/20-overview-panels.md)(依赖 17/18/19,**已关闭**)、[22: Cesium 地理管线渲染](issues/22-cesium-pipeline-rendering.md)(依赖 21)、[23: Three.js 管线增强](issues/23-threejs-pipeline-enhancement.md)(依赖 22)。本次只补索引引用,不改票面、状态或依赖边。 - `issues/24`(resolved)是 AFK 调研,产物在 `research/panel-field-coverage.md`,不改业务代码。**票 18 实施时发现该表把 `metricFreshness` 记错了端点**(记在「监测概览」名下,实际在 `/overview/monitoring` 上),已在票 18 与票 25 的 I2/BT-8 里记正。 - `issues/25`(ready-for-human)是后端补契约与业务定义缺口,需要人或后端负责人处理;不阻塞 `01`–`23` 的 mock 阶段。**票 18 实施时新增两条**(I1 `map-snapshot` 补 `pointStatus`、I2 最近采集时间归属),对应交付包草案 **BT-7/BT-8**。 - `issues/26`(resolved)是 dev server watcher 的 EBUSY 崩溃修复:忽略清单须同时覆盖 `.scratch` 面板原子写与 DSH 编辑审阅临时文件。 - `issues/27`(resolved)是引擎接入票:装 `cesium` + 静态资源/`CESIUM_BASE_URL` + 服务层创建唯一 Viewer;**票 03 依赖它**(相机契约必须落在真实引擎上)。已关闭:走查 20/20、`yarn build` 亦已跑通并产出 cesium 独立分包,验证证据与收口项见票面。 - `issues/21`(claimed / **90%**)是地形与 3D Tiles 基线:**已接上维护者本机 GIS 数据** —— `D:\GIS\GIS\yuanlingDEM`(量化网格 DEM)与 `D:\GIS\GIS\yuanlingPic3`(cesiumlab TMS 影像切片),dev 由 `.env.local` 的 `COCKPIT_GIS_MOUNTS` 挂载、地址写在 `public/cockpit/config.json`;全局粗糙层用 Cesium 自带离线影像(本机外网瓦片实测不可达,在线源只作可选项,维护者明确「能用内网离线就不用在线」)。实测沅陵拉近 4.6km:DEM 192 块 + 影像 199 块全 200、零 404。**地面/管线视图切换已完成**(决策地图票 05 的语义:管线压低相机 + 放宽俯仰 + 地形与模型半透明;**构造性保证不重置中心**、三键不变、状态归 `filterStore` 且切模式不重置),逻辑 + 真机走查 10/10。**唯一阻塞**:`D:\GIS\GIS\yuanlingSHP` 那份模型数据**缺根 tileset.json 且分块是局部坐标** —— 2026-09-18 已按「CGCS2000 地理源 + 目标坐标锚定县城稠密簇」**补出根文件并渲染成功**(分块中心映射全部落在影像 bounds 内、簇中心与目标差≈5m、`tilesetErrors` 为空),**等维护者看截图判定采用与否**;不采用则删掉那个 `tileset.json` 即可,或改走轻量建模。生产部署手册在 `docs/cockpit-deployment.md`。 - **真实启动路径是 `yarn dev`**(= `fes dev` → `.fes.js`:`layout.navigation: null`,无页头);**真实应用环境不使用 predev 启动**(维护者 2026-09-16 明确)。`yarn pre.dev` / `pre.dev.one` 是 TvT 预演站布局(`navigation: 'top'` + `isFixedHeader`,顶部多 54px 固定页头),那里出现的布局异常(如 A12 红)属环境差异。A12 证据里的 `pageHeaderH` 用来分辨这一点(见票 28)。 - **复跑走查前重启 dev server**:长跑实例可能仍给浏览器发旧模块(懒加载的 `cesiumEngine.js` 曾因此把 A15 判红一次:`widgetChrome: 1`;重启后同一份代码 20/20、`widgetChrome: 0`)。涉及引擎、懒加载模块的走查尤其要先重启。 - 相关技能:`implement`、`tdd`、`code-review`;TvT.js 侧基线是 `skills/tvt-js-plugin-builder/`。 - 验收清单就在每张票正文里,完成时勾上并同步该票的 `Status:` 行。 - **`issues/36`–`40` 是「主场景区限位、视距与过渡」决策地图(`../cockpit-primary-area-guardrails/`)收口后的执行票**:36 主场景区配置 → 37 进区锁定与相机区域限位 → 38 区内视距雾气 → 39 粗→精影像交接 → 40 全程质量收口。依赖为 36→37→38→39→40 顺序推进,一票一个全新上下文;契约以该图的 `spec.md` / `spec-fog.md` 与决策票 07、08 的 Answer 为准,原型存在 `src/modules/cockpit/prototypes/` 且只作参考。**编号说明**:31 空号、32–35 已被其它票占用,故本线取 36 起连续编号。 - **`issues/41`–`48` 是监测点定位/设备联动决策地图收口后的执行链**:41 定位与面板锚定只依赖已完成的 37,可立即认领(**2026-09-29 状态:实施完成 + 纯逻辑 37/37、变异 15/15,待真机走查,未 close** —— 走查是收口唯一缺口,详见票面);完成后 42 关联设备临时图层;42 完成后 43 面板/三维双向对应、44 设备位置符号与静态状态词表、46 模式图层计划可并行;45 重叠呈现依赖 43/44/46;47 动态状态特效依赖 38 雾气、44 静态词表、45 重叠呈现及 46 模式计划;48 端到端验收依赖 40 主场景区质量收口和本链全部实现。规格来源分别是 `cockpit-point-locate-and-device-link/issues/14`–`18`、票 03/04/07/12 的已决策结论;后端 #69 已发布,设备坐标无可信校核时必须清单-only 降级。 - [重做沅陵县城体块模型](issues/35-city-massing-redo.md) 仍在执行,2026-09-30 据实更新为95%:找到权威建筑来源——本地 1:500 测绘 CAD(`沅陵2024*.dwg` 的 JMD 居民地图层),经 ArcGIS arcpy 转出 **主场景区内 60,744 个房屋轮廓**,1:1 影像对照确认精确贴合屋顶(保留 L 形/天井/组合体量),取代影像分割。BIM 管线(足印→ENU→DEM 座落→Blender 空间 LOD)已跑通,**9 个批次全部通过 `verify-lod-district.mjs`,全场景去重导出 48,021 / 48,425 = 99.2%**(城区 29,755 栋 100%、乡村 18,266 栋;LOD0 合计约 923MB;`select-cad-bim-batch.mjs` 确定性切批并跨批去重)。主场景区外 0。仍未完成:层高校准(测绘注记无层数)、运行态性能实测与维护者视觉验收。 ## Decisions so far - [spec.md](spec.md) — 二级面板改为同屏左右两栏加标题栏展开/收起,替代 `mode/subview` 切换。 - [决策地图](../web-cockpit-decision-map/map.md) — `mode`/`panelId` 英文词典、三键相机契约、地面与管线视图合成、Cesium 集成方式、无登录页鉴权。 - [ADR 0003](../../docs/adr/0003-web-page-information-architecture.md) — 页面信息架构与 `mode`/`panelId` 词典。 - [01: Shell 与模式/面板映射](issues/01-shell-mode-panel-mapping.md) — 已关闭(第二轮):外壳铺满视口、每模式 4 面板同屏且收放真正改变高度、范围/行业接入 filterStore 且切模式不重置、一张图用临时影像铺满背景;浏览器走查 16/16 + 维护者目测确认。 - [26: Dev server 文件监视被 `.scratch` 面板原子写击穿](issues/26-devserver-watch-scratch-crash.md) — 已修复(含重开补验):`.fes.js` 的 dev server watch 忽略 `**/.scratch/**` 与 `**/.dsh-*`(Vite 默认 ignore 合并不覆盖);两个触发源——面板原子写与 DSH 编辑审阅临时文件——均实测不再击穿。 - [02: MapCanvas 常驻挂载](issues/02-mapcanvas-persistent-mount.md) — 已关闭:新增 `services/mapService.js` 持有实例生命周期(幂等挂载、尺寸上报、离开才销毁)+ 常驻宿主组件 `MapCanvas.vue`;逻辑单测 + 走查 A14 证明「切模式与收放面板都不重建」。 - [06: 驾驶舱自有静态资产与视觉占位](issues/06-cockpit-static-assets.md) — 已关闭:token 唯一来源 `styles/tokens.css` 挂 `.cockpit-shell`(不挂 `:root`,免漏插件样式)、`public/cockpit/` 自持资产、占位/图标/状态标签三件套(`PanelPlaceholder` / `icons.js`+`CockpitIcon` / `statusTones.js`+`StatusTag`,标签带文字不吃纯色);零依赖脚本 15/15 + 走查 19/19。副产:查清 A12「铺满」的 predev 环境差异(并立票 28),走查脚本加 `pageHeaderH` 与前置条件说明。 - [27: 接入 Cesium 常驻 Viewer](issues/27-cesium-viewer-integration.md) — 已关闭:`cesium@1.145.0` 固定版本(不进 federation `shared`、进 `manualChunks` 单独分包)+ `src/common/cesiumStaticPlugin.js`(dev/pre.dev 中间件与 build 拷贝四个静态目录,`pre.dev.one` 的 `copyPublicDir:false` 同样生效)+ `services/cesiumAssets.js`(`CESIUM_BASE_URL` 在 import 之前写入,取 `publicPath: './'` / qiankun 注入路径)+ `services/cesiumEngine.js`(唯一 Viewer:控件与默认相机输入全关、`baseLayer:false` 不伪造配准)+ `mapService` 持有引擎生命周期与宿主状态浮标;`yarn dev` 走查 20/20(含强制失败的兜底断言)、`pre.dev.one` 19/20(仅 A12 环境差异红)、`yarn build` exit 0 且产物里 `cesium.*.js` 为 3.97MB 独立懒加载分包、四静态目录进 `dist/cesium/`。连带修掉 build 阻塞:装 `leaflet` 与四个已发布的 `@takram/three-*`,未发布的四个用 `src/common/vendoredPluginLibAliases.js` 别名回插件自带副本(判据是 API 是否对得上,不是有没有包),并给 `build` 脚本补 8GB 堆。 - [28: 外壳填满所在容器,而不是硬用 100vh](issues/28-shell-fill-container.md) — 已关闭:`.cockpit-shell` 由 `height: 100vh` 改为 `absolute + inset: 0`,贴着最近的定位容器(没有定位祖先时按规范落回初始包含块=视口);氛围层同步由 `fixed` 改 `absolute`,不再盖到宿主页头那一带。零依赖脚本 7/7(旧实现复现「越界 54px + 宿主滚动条」,交付实现容器等高、底边贴合、真·视口 resize 跟随),走查 A12 仍绿。附带确立:真实启动路径是 `yarn dev`,predev 预演站不是真实应用环境。 - [04: 配置与凭据注入](issues/04-config-and-credentials.md) — 已关闭:四层运行时配置(内置默认 → `VITE_COCKPIT_*` → `cockpit/config.json` → 宿主注入 `__COCKPIT_CONFIG__`)由 `services/cockpitConfig.js` 唯一读取,三态 `ready/missing/failed` 在顶栏可演示(`data-config-state`);`services/cockpitApi.js` 是 mock/real 的唯一出口与唯一附加 `Authorization` 的地方(mock 分支不带凭据),失败一律回 `{ok:false, reason}` 且全仓零登录跳转;默认 mock、real 由部署配置显式开启;`map.cesium.enabled/baseUrl` 已被真消费。逻辑脚本 + 真机走查 9/9,回归 t01 20/20、build exit 0。坑:dev 的 HTML 回退只认 `Accept: text/html`,所以「默认探测配置文件」会刷 404 并打红 A11 —— 改为没有显式 URL 就不发请求。 - [29: 底图影像(配置驱动)](issues/29-basemap-imagery-config.md) — 已关闭:`map.basemap.source` 四选一 `none`(默认,维持票 27 的不伪造配准)/ `natural-earth`(Cesium 自带离线影像,无需账号外网,实测取到 17 块瓦片 200)/ `tianditu`(tk 由 `map.tianditu.token` 给,开发走 `/tianditu.map` 代理、生产 `t{s}.tianditu.gov.cn`,缺 tk 为明确失败态)/ `xyz`(任意 `{z}/{x}/{y}` 模板,甲方本地瓦片走这条);计划层 `domain/basemap.js` 是纯函数,逻辑脚本 + 真机走查 6/6;默认行为与 T01 20/20、T04 9/9、build 均不回退。维护者「怎么配出基础地图场景」的答案就是改这一个字段。 - [03: 相机控制契约](issues/03-camera-control-contract.md) — 已关闭(维护者人工验收「T03 验收完毕」):三键契约落在服务层(`domain/camera.js` 纯数学 + `services/cameraControl.js` 状态机与事件绑定 + `cesiumEngine.createCameraEngine()` 翻译 Cesium 方言),逻辑闸门 25/25 + 真机走查 10/10。四次复验共改五处:**平移方向反转为「抓着地图拖」**、**三类交互加帧率无关阻尼**(目标态/实际态双状态,`DAMPING_TAU_MS` 与 DEV 把手 `setCameraDamping` 可现场拖调)、**松手保留惯性**(不再用拾取点改写目标)、**极点保护**(中心硬限 ±85°,承认「经线在极点收敛、东西向失去意义」,避免在奇点上做特判)、**经度走最短弧**(`approachLongitude`;`lerpPivot`/`isSettled`/`pivotDistanceMeters` 同步,消除跨 ±180 的抽搐与绕地球一圈)。工具副产:走查新增「先关进入引导」「HMR 重载即判本轮作废」,并修掉 `projectPivot()` 的坐标口径(Cesium 返回的是画布局部坐标,需加画布原点才是窗口坐标)。 - [30: 进入系统的引导与视角预设](issues/30-intro-gate-and-view-presets.md) — 已关闭:进 `/cockpit` 先看**自转星球 + 闪烁「点击进入系统」**,点击后飞到沅陵县城预设视角(实测落点误差 1.4e-14°),顶栏「归位」一键回该视角;坐标与开关由 `map.home` / `map.intro` 给(组件不硬编码)。顺带把**地形感知的拾取**做掉:`pickGroundAtCenter()` 改按地形、预设高程用 `sampleTerrainMostDetailed` 实测(沅陵 111.6m),**解决了拉到距离下限 120m 时的黑屏**(旧实现中心高程按椭球 0 米,相机会落进山体)。走查 7/7 + 全量回归(t01 20/20、t04 9/9、t21 9/9、t29 6/6、t28 7/7、8 个逻辑脚本、`yarn build` exit 0)。 - [32: 生产环境界面降噪](issues/32-production-ui-cleanup.md) — 已关闭:正式环境不再把一大块状态文本压在中央 3D 场景上。形态判定 `map.ui.debug`(显式优先,缺省**跟随构建模式**:dev = 调试形态、生产构建 = 干净形态)。干净形态收起中央图占位标题/图层占位/实例提示与顶栏配置标签、「当前模式」读数;引擎状态退成**贴底 2px、无底色、9.5px、alpha 0.5** 的一行小字(故障转淡红,颜色语义不丢);**点位状态图例、状态灯、图层控件与归位按钮一律保留**。走查 9/9(同一 dev server 注入配置切换两形态)+ 9 个浏览器套件全绿 + `yarn build` exit 0。踩到并修掉的真 bug:浮标在 `booting` 就创建(那时配置还没加载),导致生产形态永远回落 dev —— 改为配置到手后按 `data-ui-mode` 重刷样式。 - [33: 一张图浮层布局](issues/33-overlay-layout.md) — 已关闭:**图例移到一张图最上方**(距画布顶 11px,颜色语义任何形态都在),调试形态的标题/图层占位收进其下方的抬头;**地图摘要收成小胶囊**(去掉「地图摘要」文字标题,状态点 + 短文案 + 动作按钮,宽 230px,`role=status` 保留语义);并修掉票 32 引入的**引擎状态与底部摘要行叠字**(生产小字 bottom 2→26px、调试药丸 top 74→96px)。三条都按**真实几何**断言(`getBoundingClientRect` 相交判定),走查 8/8 + 全量回归 13 个浏览器套件全绿。**追补(维护者看渲染图后指出)**:真正挡屏幕的是「无地图位置」浮块 → 收起形态收成**一行小胶囊 160×26px**、展开上限 340→280px,两条口径与清单改为「常驻 DOM、视觉收起」以保住票 07 的断言面;走查 **12/12**。 - [34: 沅陵县城轻量体块模型](issues/34-lightweight-city-massing.md) — 已关闭(**已交付但被否决**):用真实经纬度原料(OSM 轮廓/路网/水系 + 1:5 万居民地面 + 本地 DEM)程序化生成县城体块,`tools/city-massing/` 下留了可重跑生成器与回验脚本。**地理配准是对的**(投影到目标点 ENU 再写 `transform`;回验证伪整条坐标链:最大 8/8 个真实轮廓外接盒角点在 GLB 里 2m 内命中、原点回算误差 0.000m),**但维护者目测否决**:① 屋顶是漏的(顶面绕序与法线不匹配 + 材质 `doubleSided:false` → 背面剔除)② 尺寸与卫星图不搭边 ③ **模型压在地形之上、不被遮挡**(高程取自 17×17 粗网格插值,逐栋没按 DEM 采样 → 有的飘有的陷)。**教训:能加载 + 坐标对 ≠ 看起来对**;重做转 [35](issues/35-city-massing-redo.md),`map.tilesets` 已置空。 - [14: 事件详情与设备定位](issues/14-event-detail-and-locate.md) — 已关闭(**2026-09-24 维护者人工验收通过**):详情是**列表行的纯函数投影**(等级/状态直接沿用行上那一份,同表由"取同一份数据"保证;阈值半开区间、缺失走占位符、真实 0 照实);定位复用票 17/18 的 `mapLocate` 四结果(只认后端 `renderable`),设备码优先回联;高亮**不新建实体**(`highlightEntities` 只改已存在实体的 scale/width,`emphasisBase` 保证**空目标=全部还原**,判定抽到 `domain/mapEmphasis.js` 以便脱浏览器断言);返回保留上下文是构造性的(`closeEvent` 零取数)。逻辑闸门 **44/44** + 两个变异测试有牙 + 真机 A/B/C/E 通过(相机真飞到设备点位、返回后列表逐位相同、`data-map-emphasis` 1→0)。两处真 bug 已修:详情把行上 `{tone,label}` 当等级数字、以及收起详情不还原高亮。**边界**:管线侧强调等票 22(按设备码接缝已就绪,无需回头改本票)。 - [15: WebSocket 状态更新](issues/15-websocket-status-updates.md) — 已关闭(**真机 A–F 六组逐项通过**):连接/鉴权/订阅/断线重连集中在 `services/runtimeSocket.js`(握手照后端真实协议:`authenticate`→`authenticated{success}`→**成功才** `cockpitSubscribe{scope:"all"}`;鉴权失败只标推送不可用、**不订阅不重连**,且运行时服务不认识任何数据 store,结构上不可能牵连数据分区);增量合并是纯函数 `domain/statusPush.js`(**按时刻**而非字符串比 `serverUpdatedAt`,跨偏移判同一时刻;时间认不出的不采纳;丢弃时原样返回同一引用);**点位状态只按后端指名的 `monitoringPointCode` 改写**,绝不由设备状态推导(CONTEXT.md:前端只渲染不重算);就地更新(未命中元素保持同一引用、已打开详情只换状态字段不重取数)。四态可演示(`disabled`/`open`/`retrying`/`unauthorized`,退避 1/2/5/10/15s 封顶),由脚本化假 socket 三场景驱动、与真实环境同一条代码路径。逻辑闸门 **45/45** + 三个变异测试有牙 + 19 个逻辑闸门全量回归绿。真机抓出两个真 bug:**推送早于分页快照导致翻页后旧状态复活**(已缓存推送状态并在翻页合并后补写;`refresh()` 换新快照时丢弃缓存让快照说了算)、**`disconnect()` 漏取消待重连定时器**(离开驾驶舱后仍会重连)。**边界**:增量不带坐标/行业,不会让范围外的点位冒出来;`monitoringPointCode` 的真实可用性依赖后端未提交工作树(票 25 的 BT-1/G4,是数据缺口非实现缺陷)。解锁票 16。 - [16: 数据新鲜度与过期状态](issues/16-freshness-and-stale-state.md) — 已关闭(**真机 A–E 五组逐项通过**):统一新鲜度模型落在 `domain/freshnessModel.js`(纯函数)—— **连续两个**配置周期未成功更新即判过期(有旧值)或不可用(从未取到),阈值 2 与后端 `missedCycles >= 2` 同口径,**连续而非累计**(任何一次成功清零);「过期」与「不可用」严格分开(前者保留旧值、后者没有旧值可留)。周期由 `services/freshnessService.js` 的轮询执行、长度取配置 `runtime.refreshSeconds`(默认 30 秒 + 正数校验),**一个周期取数成功且 WS 可用才算成功**(维护者 2026-09-24 定:WS 断线也算失败周期);时钟与定时器全注入,故"两个周期"可同步断言、可现场演示。顶栏一枚胶囊说「更新 HH:mm:ss · 四态」,过期转琥珀并加 `is-stale`;**部分失败是跨分区性质**,由 `overallFreshness()` 汇总后在同一枚胶囊里补一句。缺失一律占位符:时间缺失显示「—」(**不编** `00:00:00`)、年龄缺失不回「0 秒前」,后端真给 0 照实显示。逻辑闸门 **43/43** + 四个变异测试有牙 + 20 个逻辑闸门全量回归绿。**走查抓到「部分失败」原本是没人消费的死代码**(纯函数绿着、值班人员看不到 → 验收第 2 条名存实亡),已补界面消费与 G9/G10 守卫;另修一个"假的通过"(两个年龄入口只测了一个,变异全绿)。**边界**:`tick` 当前只重取地图摘要,四块面板的周期重取留接口;后端逐设备 freshness 与前端统一模型并存、用 `viewFromBackendFreshness()` 桥接。 - [05: Mock 领域适配层](issues/05-mock-domain-adapter.md) — 已关闭:驾驶舱的领域适配层落地。**交付边界经维护者裁定为「接缝 + 共享详情域」**(面板级聚合留给 17–20)。分区状态词汇收在 `domain/sectionState.js`:后端 `CockpitSectionState` 四态 1:1 + 前端特有 `UNAVAILABLE`(凭据/链路类失败),**`STALE`(有值但过期,`retained`)与 `EMPTY`(确实没值)严格分开**;判定一律走 `sectionFromResult()` 收口。`domain/` 全是纯函数(不 import service、不自己发请求),`services/{mapApi,detailApi,runtimeApi}.js` 只做「拼路径 → 走 04 的 `requestCockpitJson` → 收成一个分区」,**每个分区各自成败**(票 11「任一分区失败不遮盖其它」的契约层保证)。`REQUEST_REASON` 提到 domain 以免层次倒挂,04 的脚本与调用点零改动。契约化 mock 的字段名照抄后端演示载荷(`docs/cockpit/*.html`),五类样例 + 401 用宿主全局键切换、不改代码即可演示。逻辑闸门 178/178 + 真机走查 10/10 + 全量回归 exit 0。**真机走查抓到两条纯逻辑漏掉的真 bug**(红→绿):分区标签被 `labelOverride` 顶掉了状态词(成功/失败态肉眼几乎一样)、界面漏出内部原因码 `unavailable` —— 两条都已修并补了静态断言。下游 07–16 可直接消费 `section()`,不必再自造分区成败模型。 - [08: 地图摘要分页与快照](issues/08-map-summary-pagination.md) — 已关闭:分页装载器落在 `domain/mapSummary.js`(纯函数 + 注入取数)+ `stores/mapStore.js`(ADR 0004 的 mapStore),语义照抄后端实现:**分页窗口是「监测点+未关联设备」一条总序列**、第 2 页起必须带第 1 页锁定的 `snapshotAt`、快照不一致即 `expired` 不拼接;组件零分页参数(静态断言守着),外壳有「地图摘要」指示器 + 加载下一页/刷新快照按钮,mock 按真实语义分页并可现场演示过期。逻辑闸门 44/44 + 真机走查 16/16 + 回归(t05 179/179、t28 7/7、t05 走查 10/10)全绿。**走查复盘修正两处**:部分翻完的分区不得标 `STALE`(票 16 词汇里那是"过期",没翻完是新鲜的 → 标 SUCCESS)、新增控件曾把 1280 宽筛选条撑溢出 202px(`min-width:0` + `flex-shrink:1` 修掉,走查守卫 F1 常驻防回归)。 - [07: 坐标校验与无地图位置](issues/07-coordinate-validation.md) — 已关闭:坐标准入判据**收成唯一一处** `domain/coordinate.js` 的 `isRenderable()`,**只认后端 `coordinateValidation.renderable`**,前端不重算经纬度范围(那是第二套判据,分歧时错的会画到地图上);后端**全量**汇总与前端**已加载**口径**分开报**(读后端源码确认 `CockpitMapSnapshotService` 用整份快照算完再切片,故不随翻页跳动;原 `toMapBatch` 漏读这段,已补);`splitByCoordinate()` / `blockedEntries()` 保住「不上图 ≠ 不存在」,地图上新增 `MapCoordinateBlocked.vue` 作可见清单(真列表归票 10/18);顺带修掉票 08「已上图 N 个」把被拦下对象也算进去的虚报。逻辑 39/39 + 走查 20/20 + 回归(t08 44/44·16/16、t05 179/179·10/10、t01 20/20、t28/t29/t30 全绿)。**跑走查才抓到的假绿**:`data-map-blocked-backend` 同名挂在宿主与面板上,`querySelector` 命中宿主拿回整页文本使断言恒真 → 面板内选择器 + 改名后证据才为真。渲染归票 09/22。 - [09: 点位状态图例与标记](issues/09-point-status-legend.md) — 已关闭:点位状态→视觉表现收成**唯一一张表** `domain/pointStatus.js`(六个后端状态照抄 `CockpitMonitoringPointStatus` 的标签与优先级,各带**色键+形状+文字**;另加 `UNKNOWN`「状态未发布」与 `UNASSOCIATED`「无关联设备」),地图标记(Cesium billboard,形状 SVG + 贴地)与地图图例同源于它,所以「图例与标记一致」是结构性的;语义色仍**只在 tokens.css 声明**,服务层从 `.cockpit-shell` 计算样式读出后烘焙进图片;认不出的后端状态如实落「状态未发布」,**不猜成正常**。逻辑 38/38 + 走查 28/28 + 16 个既有闸门全绿。**两个只有真机才暴露的 bug(视觉探针截图抓到,走查当时反而绿)**:① Cesium `DataSourceCollection.add()` 是**异步**的,同一 tick 先写后读会建出两个数据源(写进 A、从空 B 读回 0),表现为模块/dataset 说 8、组件渲染「点位标记 0」、DEV 把手又读回 8 → 用 `WeakMap` 缓存数据源实例;② 补画重试封顶 10 秒,引擎冷启动更慢时第一批点位永远画不上,只有点「加载下一页」才重画 → 退出判据改成**引擎终态**,并新增走查块 H(一次都不点,要求标记自己出现)。**假绿复盘**:走查第一版 C2 比的是「标记图片是否不同」,而图片含形状 → 颜色全撞也一样绿(对抗式复验抓到 25/25 假绿),已改成把颜色单独从 SVG 抠出来比、并要求逐个等于 token 实测值。地图上原**事件四级图例已移除**(本票正文禁止混用),事件图例归票 13/19。遗留:点位聚合不在本票内。 - [12: 工单状态时间线](issues/12-work-order-timeline.md) — 已关闭(100%):逻辑 **52/52** + 真机走查 **21/21**。状态路径与动作轨迹分两段,`domain/workOrderTimeline.js` 是纯函数判定层:**「验收驳回」由状态码 4→3 判成 `rollback` 动作**而不是终态(`TIMELINE_ACTION` 里**没有**「已驳回」这类伪状态;字典外的码落 `UNKNOWN` 且状态名留空);**当前态与"走到过的最远处"分开记**(`stateCode` vs `reachedCode`,否则回退在界面上会消失);**「轨迹没取到」≠「这张工单没有动作记录」**(模板上两条不同分支,F9/F10 钉住)**但状态路径不跟着一起丢**(当前状态码在工单列表分区里、与轨迹成败无关,走 `pathOnlyTrack()` 照铺)。**没有新增第七块面板**:轨道挂在工单面板内每张工单下面(票 11 的 F5 与措辞已同步,t05 守卫面已扩到本票新增的 1 个 domain + 1 个组件);已取消画成**路径外**节点(不接在已结案后面冒充下一步)。mock 新增场景(`work-order-timeline` 四张工单四种形状、`work-order-timeline-failed` 只有轨迹坏),`/work-orders/*/timeline` 改为按编码回显、认不出的编码抛错(缺数据不画成"没有动作")。**本轮最值得记的教训**:真机走查第一轮 17/21 逮到 `pathOnlyTrack is not defined`(组件调了没 import,整块面板打没),而**三处闸门当时全绿**——静态守卫只查"有没有出现被禁的东西"、逻辑闸门测 domain 层不加载 `.vue`、模板级引用检查看不见脚本体;据此补 F11/F12 两条引用完整性守卫并**用变异测试确认有牙**(F11 第一版对本案完全无效、F12 第一版被注释里的 `O(n²)` 判成假红,都实测修掉了)。**能加载 ≠ 能渲染;模板与脚本体要分别守。** **2026-09-29 由票 20 重开复核**(非重做):维护者裁定以后端 `WorkOrderStatusEnum`(0–7)为唯一真理,本票字典按此对齐(主线 `1→6` 改为 `0→5`,新增 `BYPASS`「延期中是**处理旁路**」);本票原验收「**不展示延期**」与后端权威枚举(6 延期中是正式状态、`ACTIVE_CODES` 含它)**直接冲突,已作废**,ADR 0003 同步更正。闸门 49 → **52 项**(回退语义一字未改)。 - [11: 设备详情聚合与分区状态](issues/11-equipment-detail-aggregate.md) — 已关闭:设备详情六个分区(档案/指标/预警/报警/工单/图片)按设备编码加载。**取数收进 `selectDevice`/`openDevice` 两个唯一进级入口**——票 10 票面写明「设备详情聚合的加载是票 11 的契约」,所以「进了设备级却没取数」在结构上不可能发生(**t10 的 C4 断言据此改写**);竞态沿用票 10 的 token 纪律并加两条:已答过的同一台不再重取、失败/不可用**不算已答**(留重试路径)。分区视图 `domain/equipmentDetailView.js` 是纯函数(接口词汇一律翻人话:认不出的 `stateReason` 回空串、字段缺失只报条数、路径进 `title`——票 05 因同类泄漏付过学费);`workOrders` 面板 kind 改用自有词 `orders`,否则它会撞上 t05 的后端裸字段守卫。**「整包失败」与「分区级失败」严格分开**:前者 `data` 为 null、六块一起没数据,逐块渲染会让每块都谎报「其它分区不受影响」,故改说一句实话 + 重试入口;后者(`aggregateState: PARTIAL`)才逐块渲染。mock 原来那段分区级失败样例挂在 `failed` 分支下**永远走不到**(`failed` 在注册层就抛错),即验收第 3 条此前根本无法演示 → 新增独立 `equipment-partial` 场景。逻辑 **61/61** + 真机走查 **29/29**(恰好 3 坏 3 好,故「好块仍在」不是恒真)+ 12 个既有闸门回归全绿。**t05 的静态守卫面已扩到本票新增文件**(不补就是检查不到的荒地);两条新泄漏守卫都做变异测试确认有牙(F4b 第一版只查引号字面量会放过模板裸文本,已修)。遗留:`timelines` 归票 12、`alarmWarningRelations` 归票 13,事件四级配色归票 13/19(本票只把后端色名透传到 `data-event-level-color`)。 - [10: 监测点详情与设备卡片](issues/10-monitoring-point-detail.md) — 已关闭:**两级详情落地**。层级是一条纯函数导航状态机 `domain/detailFlow.js`(`none → point → equipment`,换点位清设备、返回/关闭收口),取数只在点位级、且收口三件事(同点位不重复取数、A→B 迟到响应丢弃、选设备只换层级不取数——**设备详情聚合的加载归票 11**);地图标记点选 = 「观察型」DOM 绑定(`services/pointPick.js`,拖拽不误触发、票 03 手感不变)+ 只认 `pointCode` 的引擎拾取(`pickPointMarkerAt()` 回身份不回实体)+ `mapService.onPointPick()` 订阅;关联设备从不进标记计划,所以「不重复上图」是结构性保证。顺带修:整段 `deviceStatistics` 缺失记 `missing`,不再把契约缺口画成「0 台」。逻辑 **57/57** + 真机走查 **28/28**(真实鼠标点标记;走查脚本记下两个只有真机才暴露的坑:阻尼飞行途中点中心必空、贴地标记与高度 0 中心差 30px 地形高程)+ 回归 12 个逻辑闸门与 t01/t05/t09/t28 走查全绿。遗留:`readCoord(null, 默认)` 会得到 0(无配置文件时归位飞到 (0,0)),属票 30 的域,记在票面未改。 - [17: 基础设施模式面板](issues/17-infrastructure-panels.md) — 已关闭:基础设施模式四块面板(基础档案 / 运行信息 / 管理信息 / 应急保障)各接一个后端**面板级聚合读模型**端点,**四块各自成败**(一块坏了其余三块照常画出真实内容)。契约适配 + 视图计划收在一层纯函数 `domain/infrastructurePanels.js` 里由四块共用(组件按 `block.kind` 分派 kpi/bars/rows/list 四种画法,票 18/19/20 只加计划、不加第二套渲染);「点面板里的可定位对象联动地图」做成**共享接缝** `domain/mapLocate.js`:按点位编码回联已加载的地图对象 → **只认后端 `renderable`** 的坐标准入(票 07 的判据)→ 票 03 的 `setCameraFocus()` + 票 10 的 `openPoint()`,三种"点不动"分得开(不在已加载范围 / 后端判不可上图 / 地图未就绪)且不假装成功。三条边界如实划清:**「责任区域」在后端管理信息契约里根本没有字段**(逐类读过响应类与快照类),以缺口说明呈现、不拿行业冒充 —— 这条**修正了票 25「17 的 D/E/F 已交付可对接」的说法**;行业筛选只接行业一维(`gas/water/drainage` 1:1 映码,**井盖在后端正式枚举里没有契约位**故不传参),范围/区域归票 18;基础档案与管理信息**没有可定位对象**(聚合分布口径、后端不返回逐设施坐标),如实说明而非造假按钮。逻辑 **57/57** + 真机走查 **28/28** + 15 个逻辑闸门与 4 个既有走查全绿。**走查逮到两处纯逻辑发现不了的问题**:① **真泄漏** —— 后端 `scale` 是**数据源透传的自由字符串**(契约手册与后端测试都只当值),漏译则英文枚举 `medium` 直接印上大屏,已补直译并加"登记码不得等于原码"的归纳守卫(原泄漏守卫只查状态码、不查枚举值,是它全绿而真机露馅的原因);② 组件里一段**死代码**(段状态挂在与其互斥的 `v-else-if` 上,永不执行)。**连带的测试根因修正**:t01 的 A5 原本**从占位文案反推 panelId**,本票把基础设施四块换成真面板后假红 —— 断言钉在了实现细节上,已改为读外壳新给的稳定 `data-panel-id`(`CockpitPanel` 新增 `panelId` prop),t01 恢复 20/20。 - [18: 运行监测模式面板](issues/18-monitoring-panels.md) — **已关闭(100%):逻辑闸门 71/71、真机走查 38/38**。运行监测四块(监测概览 / 监测分布 / 运行概况 / 监测点台账)接三个后端面板级聚合端点(`monitoring/overview`、`monitoring/distribution`、`monitoring/situation`),**三块各自成败**;台账与监测分布**共用一次取数**并按端点 `pagination` 渐进加载,卡片徽标走**按需 N+1**。本票把票 17 立的两条接缝**提出来共用**:「块怎么构造」搬进 `domain/panelBlocks.js`,「哪个 panelId 用哪套视图计划」收进 `domain/panelViews.js` 的**唯一注册表** —— 于是票 17 的共享渲染层改名 `InfrastructurePanel.vue` → **`PanelBlocks.vue`** 并改成**吃算好的 `view`**、**不认识任何 panelId**(票 19 接进来时未改它,H2 断言 + 变异确认)。**筛选真的落到地图**(票 25 第 3 节冻结的路径):`map-snapshot` 的 `industry`/`region` 入参接线 + `filterStore` 的三个条件 + 筛选变化**从第 1 页换新快照重载**。**2026-09-28 复核逮到两条自己交付面里的真缺陷并修掉**:① **井盖已可筛选而我仍不传它** —— 后端已把 `MANHOLE` 定为第四个正式行业且 11 个服务(含 `map-snapshot`)全收,我按票 17「无契约位」的旧事实不传,导致「井盖」按钮对面板与地图完全不生效(修:接上映射 + 样例补井盖数据 + F0d/D2c/D2d,并同步改票 17 那条守旧假设的 E2 断言);② **`map.filters.regionMap` 是死代码** —— 归一化器不认这个键,外壳永远读到 `{}`,**范围筛选端到端从未生效**,而我的 E4 只单测了映射函数、手喂参数(修:补进配置契约 + 从原始载荷出发的 E4c/E4d/E4e 端到端断言)。两条仍是「守卫比它要守的东西宽」同一族,变异 M20–M24 全部打红;期间又逮到两处自己的判据缺陷(E4d 漏坏键、F0d 会抛异常而非干净判红)。**收口口径(维护者 2026-09-28 裁定)**:本票交付面是**前端页面**,两处后端契约边界(状态筛选不落图 = 票 25 的 I1/BT-7;最近采集时间不在本端点 = I2/BT-8)**已转交票 25**,后端接口联调由其他票完成、**不阻塞后续页面开发** —— 故本票按交付面收口,票 20(`Blocked by: 17, 18, 19`)随之解锁。边界在界面上**如实呈现**(状态筛选不影响一张图的常驻说明;采集时间以「数据时间」呈现并写明区别),不假装生效、不伪造数据。另修走查里一处钉在**别人票**上的断言(A8b 原断言「预警处置 4 块占位」,票 19 接上真面板后假红)。 - [13: 事件列表与预警/报警切换](issues/13-event-list-and-mode-switch.md) — 已关闭(100%):预警/报警切换 + 只读事件列表落地。**字段标签与状态标签从同一个切换视图取**(`domain/eventList.js` 的 `eventListSwitchView` —— 同步变化是结构保证,不是两边各自小心);切换状态在 `stores/eventStore.js`(`mode` 共享,票 19 四面板复用同一个 `setMode()`);等级 1-4 色带文字走 `statusTones.LEVEL_TAGS`(与点位状态色分离是结构性的);默认展示最新 12 条(落在验收「10-15 条」区间);事件**不建独立地图标记**(只带设备编码供票 14 回联);**不判断预警真伪/误报**(后端契约就没有准确率/误报字段,状态照原样带出)。数据源是**跨设备** `/events/current`(`CockpitCurrentEventsResponse`,mock 按 mode 真过滤、切族真的换内容),挂载在预警处置模式的 `currentEvents` 面板(`EventListPane.vue` 可复用组件)。逻辑 **49/49** + 变异测试有牙 + 真机走查 **15/15**(维护者授权执行;手动验证亦通过)+ 全量回归绿。**走查第一轮 14/15 抓到两个断言自身的假绿/误判**(不是产品 bug):A6 把分段切换按钮误算进「写操作」、B4 用 `getPointMarkers().length`(该句柄返回 `{markers, onMap}`)恒 `undefined` 而弱绿 —— 已改为排除切换按钮、读 `onMap` 并在取不到时判红,证据从 `undefined→undefined` 变为真实 **2→2**。遗留:`alarmWarningRelations`(预警-报警关联)仍未在界面呈现(聚合契约里已有该分区,但前端未画——事件四级配色已在本票接手)。 - [36: 主场景区配置(`map.primaryArea`)](issues/36-primary-area-config.md) — 已关闭(100%):新增 `map.primaryArea.bounds`(`[西,南,东,北]` 度,与 `map.overlays[].bounds` 同口径)作区域限位与自建雾的**唯一开关**。判定收进新 `domain/primaryArea.js`(形状 + `west altitudeFullM` 守卫(写反等于关掉该保护,只报 issue 不替改值)。**变异 10/10 + 改写后 6/6 全部打红**。**最终时长由维护者在真机调定并裁定"以我为准"**(`fadeInMs 900 / fadeOutMs 300 / altitudeFullM 14000 / altitudeZeroM 3000000 / altitudeSlewMs 600`)—— 部署层取值与代码默认(800/400、20/42 km、400 ms)**有意分离**,代码默认保持"雾先淡完"的取向供别的部署沿用。 - [39: 粗→精影像交接](issues/39-coarse-to-fine-handover.md) — 已关闭(100%):**从全球飞到县城视角不出现肉眼可辨的矩形色块跳变**。新增 `domain/imageryHandover.js`(交接状态机·相位/alpha 推进且**落定必为 0 或 1**、**揭示闸门**〔全球不揭示 / 雾保护成立 / 目标视野瓦片可显示 / 边缘风险 < 阈值 / 瓦片失败〕、返回同步判据、**边缘遮蔽率**、过期取消、配置归一化、读数)+ 引擎侧 `applyOverlays` 改为返回**句柄**(`setAlpha()` 是 **alpha 的唯一写入点**)+ `createOverlayReadiness()` + 服务层策略/生命周期 + `map.handover` 配置 + 部署手册 2.12。**四条关键取舍**:① **边缘判据不照抄原型的 38 km 魔数**,改为把相机到矩形四边的最小距离代入**同一条雾衰减曲线**要求遮蔽率 ≥ 阈值(实算证明区内最坏点距边 35.8 km、遮蔽率 0.967 —— 照抄 38 km 会把它误判为不安全而挡住本可显示的影像);② **"隐藏"必须 `alpha = 0` + 保持 `show = true`** —— 瓦片是否被请求由 `GlobeSurfaceTile.js:691` 按 **`layer.show`** 决定、**不看 alpha**,用 `show = false` 会真停掉预取、揭示时才下载,正好造成"先空白再跳方块"(读数 `layerProbe` 可核对);③ **返回拆两个并行进程**(按下返回那帧同时开始收影像与飞镜头;雾延后到 alpha=0 才淡出)—— 决策 08 同时要求"影像在失去保护前收起"与"不能收完→停顿→才飞走",字面上矛盾,这样解;④ **瓦片就绪只是代理信号**(`globe.tilesLoaded` 只表示加载队列为空),读数如实标 `tilesReadyIsProxy`。**维护者裁定两处**:**(a)** 原型的删除与归档**归 40**、39 只做隔离(生产与原型**显式互斥** —— 两套都写 `layer.alpha`,同时跑必互相覆盖;原型挂载时 `setHandoverPrototypeActive(true)` 接管、卸载交还);**(b)** 2026-09-30 在真机调定全部时长/阈值并裁定"目前的效果就行""以我为准"(`fadeMs 500 / minEdgeOcclusion 0.7 / requireTilesLoaded false`;代码默认 380/0.9/true **有意分离**)。**两条有意放宽已如实记录**:`requireTilesLoaded: false` 牺牲的正是决策 08 设它要防的"先空白再跳方块";退出时「相机已高于雾的全遮蔽端点而雾仍可见」的窗口 ≈ **387 ms**(相机 ~413 ms 越过 42 km,雾到 800 ms 才淡完)。**闸门不掩盖**:原第 12 节硬断言("雾淡完时相机须仍在 42 km 以下")在 `T=1000` 下**不可能成立**,留着就是永远红的假门 ⇒ 改写为**把窗口算出来并钉住已接受上限 400 ms**(缓动的慢起/两端精确/单调/真走 `flightMs` 仍硬断言),超限即红、逼一次"这是有意的吗"确认。**同时修掉一处会在源码里说谎的注释**(`camera.js` 的 `RETURN_FLIGHT_MS`/缓动 JSDoc 仍写着旧的"指数 7 反解"推导,而缓动已被改成 `smoothstep^6.5` + 0.1% 线性项且约束已放宽)。闸门 t39(相位/闸门四类拦截/降级/边缘同源/返回同步/过期幂等/静态守卫)+ **变异 15 处全红**,改写后第 12 节另 **6/6**。**环境限制如实记录**:本会话工具清单已无浏览器工具(`browser_refresh` 报 unknown tool),故"观感通过"是**维护者真机自行确认**、非我采集。 - [40: 全程质量收口](issues/40-quality-closeout.md) — 已关闭(100%):**维护者 2026-09-30 真机验收通过,前三票的"方案通过"收成"生产可用"**。九条验收全部收口:① LOD 换级、② 四条降级路径、③ 管线视图、④ 性能 P95、⑤ 走查链、⑥ 规格同步、⑦ **原型归档**(`throwaway/cockpit-prototypes` @ `81932a0`,git 临时 index 归档 **未动工作树里并发在途改动**;生产树已删 `prototypes/`、`/cockpit` 入口只挂 `CockpitShell`;还原 `git checkout 该分支 -- <路径>`)、⑧ 运维自检(手册第 5 节全表 + 补「闲置回引导」行 + 修 `data-map-fog-phase` 笔误)、⑨ 三类结论分账。**本票修掉维护者实报的「闲置回引导态看不出来」缺陷,三个根因三层修**:① 相机不回位 → 新增 `resetToIntroView()`;② 自转起不来且失败被伪装成功 → 删粘滞开关 `spinPaused`(没人清、命中即 `return true`),改 **`spinWanted` 意图 + `resumeSpinIfWanted()` 落定接上**;③ **兑现接错路径** → 自转兑现同时挂 `notifyCameraStateChange` 与相机控件 `onStateChange`(飞行逐帧路径,落定那一帧必到)。**引导视角契约定夺(维护者裁定)**:初始引导页 = **Cesium Viewer 默认相机**(挂载时 `syncFromEngine()` 反读,存 `introView` 基准);「返回全球」「闲置回引导」的**角度/高度**都回到该基准,**中心保持在当前位置**(不回大西洋/北美);`INITIAL_PANORAMA` 只作缺席兜底。**闲置时长 5 秒 → 10 秒**(维护者裁定,契约五处同步:ADR 0009/票 37/手册/注释/t37;`GLOBAL_IDLE_MS` 本会话被并发三次改写均按裁定恢复)。**规格同步三处全部经维护者确认**,无静默漂移。逻辑闸门 33 通过(收口时点),本轮新增 t40 闸门(**变异 10/10**)+ t39 §12 改写后(6/6)。**已知代价**:交接 `requireTilesLoaded: false`、退出雾仍可见窗口 ≈387 ms(票 38/39 记录)。**遗留移交**:收口时点 2 个红闸门 `t04-config`(并发会话 `cockpitApi.js` 硬编码地址)与 `t10-point-detail`(并发会话 `PointDetailCard.vue` 碰标记层),均与本票无关、已如实记录。 - [41: 监测点定位与面板锚定接入](issues/41-monitoring-point-locate-and-panel-anchor.md) — 已关闭(100%):**逻辑闸门 46/46 + 变异 20/20 全部打红 + 真机走查已执行**。决策票 03 的两档定位与票 04/12 的锚定跟随接进外壳:新增 `domain/pointLocate.js`(`map.locate.point` 缺省 `0/−55/500`、`device` 缺省 `150`,配置逐项兜底 + `isStaleLocate()` 竞态判据)、`domain/pointAnchor.js`(四向右→左→下→上、12px 间距不压图标、**拥挤时允许裁切而不消失**、只在完全离屏时隐藏 —— 规格 R-2/R-4 取消了 `no-space`/`icon-covered`,实现里不存在这两条死代码)、`pointSelection.js`(**统一选择入口**,放接线模块而非 `detailStore`:两侧都不许自己长出对方,票 10 的 F4 与票 02 的层次纪律同时保住)、`domain/coordinate.js` 的 `locationVerdict()`(把「**不在已加载快照里**」与「**后端判不可上图**」分开)。**定位走完整视角预设**(连 hpr 一起换),不是 `setCameraFocus` 的"只换中心"——后者会把 range 留在县域 12000 m,正是票 03 要解决的问题。**真机走查抓出并修掉 6 处纯逻辑闸门不可能发现的真缺陷**:① `/monitoring-points/*` 的 mock 只回显编码、**名称永远取默认载荷**(走查点 MP-001 显示成 MP-002 的名字,被本轮改名暴露);② `refreshAnchor()` 把 `document.hidden` 写成无条件早退 → 后台标签页里**连首次布局都不做**,面板永远停在 `canvas-missing`(改为只有逐帧重算跳过、显式重算照常);③ 投影要求"配了地形就必须取到高度" → 相机飞到瓦片未加载区时面板整块消失而标记明明可见(改为回退椭球面,与 Cesium 无地形时的画法一致);④ 暂隐面板只设 `visibility:hidden`,而它是 `pointer-events:auto` 的绝对定位元素 → **看不见却吃掉地图点击**;⑤ `applyViewPreset`(定位/归位/返回全球/自转)不通知锚定 → "相机已回到点位、面板仍显示隐藏";⑥ 锚定只挂 `scene.postRender`,不渲染就完全停更(新增 `onCameraStateChange()`)。**实测**:选中 → `range=500`/`pitch=−55`/`heading=0`/中心=点位坐标/高程 120.22 m(实测地形);面板 `visible`/`right`/`z-index=1`/**距标记中心 21 px**(=9+12 规格下限)/不覆盖图标/完全在画布内/短线在位;真实拖拽拖出画面 → `hidden`/`marker-offscreen`/`pointer-events:none`/详情仍在 DOM,顶栏出现「MP-001 · 点位标记已离开地图画面 重新定位 关闭」,点「重新定位」相机回到点位。**维护者本轮指定两处走查点位**:`MP-001` →「城南测试监测点」、`MP-002` →「太常测试监测点」,**两处坐标源同时改**(`map-snapshot` 与 `/monitoring/distribution`,只改一处会让同一点飞到两个地方)。**未覆盖项如实记录**:走查时标签 `document.hidden===true`(rAF 冻结、阻尼不推进,票 37 记过同一环境事实),故"四向翻转观感"与"固定详情"两条留待前台标签页复跑脚本补验,此二者已由 t41 闸门纯逻辑覆盖。**改写既有断言两处**(`t18` 的 H12 与走查 C2:「台账不动相机」被本票验收第 1、2 条正式取代,均保留对照组)。回归全绿(t05 守卫面 248、t18 71),唯一红是 `t28` 的 puppeteer 既有环境缺陷。 - [42: 关联监测设备临时图层](issues/42-associated-device-layer.md) — 已关闭(100%):**逻辑闸门 37/37 + 变异 19/19 全部打红 + 真机走查已执行**。按规格 14 / 决策票 05 接入**选中态临时图层**:新增 `domain/deviceLayer.js`(**六类互斥归类** `drawn`/`no-location`/`hidden`/`pending`/`inconsistent`/`unavailable` + **总数只认当前快照去重 `deviceCodes`** + 快照独有→待确认、详情独有→不一致且禁止上图 + 独立图例 + 跨端点风险提示)、`stores/deviceLayerStore.js`(**合成点**:读 `mapStore` 的快照清单与 `detailStore` 的设备卡片,两侧都不为自己长出对方)、引擎侧**独立数据源** `cockpit-associated-device-markers`(与摘要 `cockpit-point-markers` 分开,设备实体 id 形如 `device|MP-001|EQ-001`、属性带 `deviceCode`+`monitoringPointCode` 且**不含 `pointCode`**)、`pickDeviceMarkerAt()` 独立拾取入口、宿主读数 `data-map-devices`(`-plan`) 与摘要 `data-map-markers*` **分开**。三条关键取舍:① **总数不拿详情统计冒充**(详情不携地图快照 `snapshotAt`、可能复用缓存,两边可能不同版);② **坐标准入只认设备自身 `renderable`**,绝不用监测点坐标代替、不人工偏移(CONTEXT.md 禁止),现网无可信校核源 → **正式边界是清单-only**;③ **数据可用性先于模式可见性**(实现时抓到并修掉的真问题:先判模式会把"坐标被否决"说成"当前模式隐藏",让值班人员以为切模式就能看到它,而它任何模式都上不了图)。**真机走查抓到 1 处真缺陷并修掉**:UI 把**清单行数**当成**关联总数**(总数显示 2 而快照只有 1 台,多出来的是"详情独有"那一行)→ store 单独持有 `total` 与 `rows.length` 分开,两个数都显示且各有含义。**实测**:受控有效坐标样例两台设备真的画在独立图层(`onMap=2`)、摘要标记仍 2;现网降级边界下清单保留并标 `no-location`、**引擎 0 台**、总数 1、跨端点提示出现;关闭即撤下(设备层归零而摘要标记仍 2);换点后 A 不残留。**边界**:设备**状态词表/形状资产**归 44、**完整四模式图层矩阵**归 46、**状态特效**归 47、**双向联动**归 43(本票只实现单点位模式可见性 `isVisibleInMode` 与中性占位符号)。**未覆盖项**:走查标签 `document.hidden===true`(rAF 冻结、Cesium 不渲染),`scene.pick` 连监测点标记也拾不到(已用对照确认是环境非本票缺陷),"真实鼠标点设备点"的观感留票 43 或前台标签页复跑。 - [20: 总体概览模式面板](issues/20-overview-panels.md) — 已关闭(100%):四块面板(态势总览/基础设施/运行监测/预警设置)接三个概览端点 + **借票 17 的 `/infrastructure/base-archives`** 取区域分布(概览端点自己不含区域字段),**三块各自成败**;**态势总览不自己取数**、由另外三块合成,故它与预警设置的数字**按构造同源**(同一对象引用)。四条边界如实划清:① **「故障统计」后端没有该桶**(只有 online/offline/stale/unknownOrMissing),按维护者 2026-09-29 裁定**不合成故障数**、四态如实呈现并印缺口说明("滞后"≠故障);② **「联动地图」在概览数据上做不到**(三端点都不含点位编码/坐标)→ 改走两条**真实存在**的路(指标读数切到预警处置模式落到真实事件列表,那里的事件带 `deviceCode` 可再跳点位;行业行写全局 `filterStore` 真的驱动地图与各面板),**不伪造定位**;③ **分母为 0 不是 0%** —— 在线率与处置率**共用同一条判据**(`handlingRateView` 导出复用),空范围显示 `—`;④ 「近 7 日趋势」**真的声明 `LAST_7_DAYS`**(不声明时后端是无界 `CURRENT_BASELINE`,那句话就是空的)。**真机走查逮到一个逻辑闸门看不见的真缺陷**:两块面板读数全是 `—`,而闸门 59/59 全绿 —— 因为**测试与实现犯了同一个错**(都以为 `data` 是 `{monitoring}` 包装,而外壳传的是裸 `section.data`);已修并补三条护栏,其中 **D8 走外壳真实入口端到端逐块读数字**、**F6c 钉住外壳逐块的数据源分派** —— 教训是"手喂 data 的单元断言证明不了外壳喂进来的形状是对的"。变异测试又逮到两条自己的守卫缺陷(F6b 只查词出现过 → 改为断言两处模板绑定;F3 裸字段词表比 t05 的 D1 窄 → 改为同宽)。闸门 **63/63**、变异 **14/14**;全量回归 29 闸门绿(`t04`/`t28` 的失败是既有环境问题、与票无关)。**跨票改动已重开三票记录**:票 12(时间线对齐后端 `WorkOrderStatusEnum` 0–7,主线 `1→6`→`0→5`,新增 `BYPASS`「延期中是处理旁路」;其原验收「不展示延期」与后端权威枚举冲突,**已作废并同步改 ADR 0003**)、票 19(查出两处真缺陷:工单状态分布**标签整体错位一格**、处置率"分母为 0"画虚假 `0.0%`;并发现**票 19 从未把新文件补进 t05 守卫面**,实测干净后补入,t05 255→**270**)、票 11(仅码值跟随,行为未变);另按 Q9=(a) 修掉票 17 走查里**钉在别人票进度上**的 `A8b`("总体概览恰好 4 块占位",票 18 在相同处境下已栽过一次)。**本票是 16 块面板全部接上真实视图的最后一块**(`PanelPlaceholder` 现为未注册 panelId 的兜底)。 - [43: 面板与三维设备点双向对应](issues/43-panel-device-correspondence.md) — 已关闭(100%):**逻辑闸门 40/40 + 变异 26/26 全部打红 + 真机走查已执行**。按规格 15 / 决策票 06 实现**反应语言**:新增 `domain/deviceCorrespondence.js`(四档互斥反应 `selected`/`hovered`/`dimmed`/`normal` + 镜头联动三段 `device-tier`/`center-only`/`point-tier` + 拾取优先级 `resolvePickOrder` + 引线退化判据 + 反应量 `scale`/`alpha`/`ring`);引擎侧 `applyDeviceReactions`(逐实体写 scale/alpha/outline)、`syncDeviceLead`/`clearDeviceLead`(设备点连**监测点标记中心**的归属引线,随锚点走)、`pickDeviceMarkerForHover`;宿主新增三个可断言字段 `data-map-device-picked` / `data-map-panel-selection` / `data-map-device-hovered`(**不动既有 `data-map-picked`**);hover 节流 60ms(规格未锁数值,交实现定)。四条关键取舍:① **反应不用颜色**(色键已被设备状态占用,反应量里连 `color` 字段都没有);② **hover 与选中是两档、互不干扰**(hover 只作用于非选中设备、不叠外环/引线;hover 到已选中的那台不改变其选中态);③ **弱化阈值=设备数 ≥ 3 台**;④ **返回点位是层级回退不是取消选中**(监测点仍选中、回点位档视角),与票 03「取消选中不回退」并存。**真机走查抓到并修掉 1 处真缺陷**:设备选中态有**两套真相** —— 原先把 `setSelectedDevice` 直暴露到 DEV 把手,直接调服务层后 store 的 `selectedDevice` 仍读空串(界面高亮与三维强化给出两个答案);改为按票 41 成例由外壳把 **store 方法**递给 DEV 把手(`publishDeviceSelectionHandle`),走查驱动的是用户那条路径(断言 `F14` 钉住)。**实测**:选中进设备级(`picked=EQ-001`、`detailLevel=equipment`、store 与服务层读数一致)、hover 不干扰选中、`panel-selection` 随层级变化、返回点位后监测点仍选中且设备选中清空、`applyDeviceReactions` 对实体各写一次(`written=2`)。**边界**:设备形状/资产与状态词表归 44;**重叠聚合、展开环与逐台拾取归 45**(本票只做**正常单点**的反应与拾取优先级;「**未摊开(地理重合)时引线退化不画**」按规格边界**属本票**,展开态的引线照画归 45);四模式矩阵归 46;状态特效归 47。**未覆盖项**:走查标签 `document.hidden===true`(rAF 冻结、Cesium 不渲染),「真实鼠标 hover/点选三维设备点」的**画面观感**未能逐帧验证(判定与接线已由闸门 D/B/F 段覆盖),建议前台标签页复跑或由票 48 一并覆盖。 - [44: 设备位置符号与可替换资产接缝](issues/44-device-position-assets.md) — 已关闭(100%):**逻辑闸门 47/47 + 变异 30/30 全部打红 + 真机走查 11/11(连跑两次稳定)**。按规格 16 / 决策票 07·10 交付:新增 `domain/deviceAssets.js`(**唯一可替换资产接缝** —— `ASSET_MODE` 刻意**不含** `model`、`planDeviceAsset` 是唯一 `markerSvg` 调用点、降级链「未来真模型 → 正式图片 → 设备专属图标 → 通用位置符号」每级各记各的原因)+ `domain/deviceStatus.js`(**平行于** `pointStatus` 的独立设备词表:五态**无 `EMPTY`** + `UNKNOWN`,认不出**不猜正常**)+ `domain/deviceLayer.js` 新增 `terrain-pending` 档(**「等地图资源」与「数据有问题」分成两件事**);`MARKER_SIZE` 建立尺寸层级(点 18 > 设备 14),任意允许缩放下不反转。**修掉两处既有越界**:设备卡片原先**借点位状态表**配色配形(改走 `deviceStatusView`);`deviceLabel` 原是**死代码**、清单行还是"编码在前"(与规格"名称为主"相反)—— 现由接缝驱动并加 `F12` 钉住。另补引擎观测面:设备实体本就带形状/资产模式/降级原因,但 `readAssociatedDevices` 把它们吞了,走查只能读"打算画什么"(`F13`/`F14` 钉住)。副产 `verify/cdp-driver.mjs`:本机 `puppeteer-core` 已消失,改用 Node 24 自带 `WebSocket`/`fetch` 直讲 CDP 的**零依赖驱动**,**下游票可直接复用**;实测 `document.hidden=false`,比票 41/42/43 当时的真机条件更好。走查自身也修掉两处写法问题(图例断言看错了图例;`W6` 曾写成恒真空转却显示 PASS)。遗留:`deviceLegend()` 无生产调用点,去留请票 47 明确。 - [46: 一级模式图层计划与选择态矩阵](issues/46-mode-layer-visibility.md) — 已关闭(100%):**逻辑闸门 28/28→**29/29**(补回归)+ 变异 8/8 全部打红 + 真机走查 10/10(连跑两次稳定)+ 回归走查 5/5**。按规格 18 / 决策票 13 交付:新增 `domain/modeLayerPlan.js` —— **「一张图什么时候显示什么」的唯一领域接缝**(`planModeLayers()` 收口五态矩阵/异常点回联/图例/回联目标/降级说明/选择态;`planModeSelectionTransition()` 定总览↔分项保留、分项↔分项清空;`isAssociatedDeviceVisible()` 是关联设备模式判据的唯一住址,`deviceLayer.isVisibleInMode` 改为别名转发)。**结构性变化**:`mapService.syncPointMarkersForSummary(objects)` 改为 `syncPointMarkersForPlan(plan)` —— 服务层只消费计划、不再重判;外壳是**唯一**计算计划的层(握有 mapStore/设备行/异常设备/选择态全部输入),MapCanvas 只消费 `plan` prop(引导态门控图例与计数浮标、新增 `data-map-degraded-note` 断言面);详情浮层按 `selection.visible` **暂停/恢复**(隐藏但保留选择身份,恢复=计划重新判可见,无额外状态机)。**异常点只认设备状态事实**(`WARNING/ALARM`;工单/离线不混入;`/monitoring/situation` 未取到时预警处置不画点 + `abnormal-devices-unready` 降级说明,不猜)。**degraded ≠ hidden ≠ empty**:设施/管线契约未交付传 `null` → degraded + 说明;管线未就绪四模式都是 degraded 而非 hidden(常驻 ≠ 数据就绪才常驻)。自查修掉 2 处 + **人工走查后补修 1 处回归**:设备级选择判据先查 pointCode 会误判暂停(F4 逮到,改为优先看设备);走查 W6 曾恒真空转却显示 PASS(改为默认样例另跑一趟);**默认样例(现网边界 `no-location`)点设备卡片会卸载详情面板** —— `selectionVisible` 曾要求 `placement === 'drawn'`,而设备级详情与能否上图无关,已改为按模式可见性判(`F6` + `t46-no-location-detail-walkthrough` 5/5 钉住)。设施/管线数据到位后只需把真实集合传进计划,矩阵无需再动。 - [45: 重叠设备聚合与展开](issues/45-overlapping-device-points.md) — 已关闭(100%):**逻辑闸门 18/18 + 变异 6/6 全部打红 + 真机走查 15/15(连跑两次稳定)**。按规格 17 / 决策票 11 交付:新增 `domain/overlapDevices.js` —— 同点位近距分组(K=可逐台绘制数、N=快照关联总数)、聚合符号**无 deviceCode**(不冒充设备)、屏幕圆环 `fanLayout()`(**不改经纬度**,纯渲染层 pixelOffset 分离)、hover 预览 / click 粘性 / 三条件收起的 `transitionOverlapState()`、设备优先 `resolveOverlapPick()`。引擎 `syncAssociatedDevices` 按组画无身份聚合实体 + 逐台实体(`show:false` 折叠);`pickDeviceMarkerAt` 先普通 `scene.pick`、落空用投影近邻补偿(**不用 drillPick**)。服务层按 groupKey 管理展开/收起(同点位多组互不干扰),展开子设备先收起环再走票 43 原有选中/详情;`map.overlap.maxFan` 部署配置(默认 10);面板 hover 聚合行只弱提示不摊开(经 store 转发,维护票 10 F4)。**自查修掉 3 处真问题**:`resolveOverlapPick` 判 `deviceCode` 而引擎回 `code`(恒 none,同一次 evaluate 铁证);`readOverlapTargets` 用渲染态 `isShowing` 导致 headless 下展开设备可见性反转(改逻辑 `entity.show`);预览态隐藏聚合导致无法继续点击(preview 保留聚合、sticky 才隐藏)。**真实 CDP 鼠标走查**:hover 预览/离开收起、click 粘性展开、真实 click 展开设备 EQ-001 普通拾取选中进设备级、返回点位/空白收起、既有 `data-map-picked/device-picked/panel-selection/device-hovered` 字段保留。遗留:预警处置模式下聚合只反映该模式可见设备的最高状态(票 46 模式过滤已就位,本票 K 只认 drawn 行)。 - [47: 设备状态视觉特效与门控](issues/47-device-status-effects.md) — 已关闭:中心图标常亮,外围能量环表达设备状态,设备详情随所选设备换锚;主场景 MP-001 K=2 / MP-002 K=11 已同步五态样例,维护者人工确认及本轮相关回归通过,代码 `09ef651` 已推送(本轮按项目规则未跑 build/debug/浏览器)。 - [45: 重叠设备聚合与展开](issues/45-overlapping-device-points.md) — 已关闭:监测点名称显示,聚合计数无底板;hover 预览、click 固定,所选设备持续作为聚合入口并可切换同组设备。设备相机定位保留地形高程。名称 13px、计数 11px(维护者调整);后续科技感设计交蓝湖素材替换票。连续交互19项及14组脱浏览器验收通过;按 AGENTS.md 未运行 build/debug/浏览器。 ## Not yet specified - 甲方本地瓦片、地形与 3D Tiles 的实际交付物清单 —— **2026-09-18 更新**:本地 DEM(`yuanlingDEM`,量化网格)与影像切片(`yuanlingPic3`,TMS)已接上并通过走查;**甲方那份 cesiumlab 3D Tiles 经目测否决**(错位 + 白模),合成根文件已删除;**轻量体块模型第一版也被否决**(漏顶 / 飘在地形上 / 尺寸不搭边,见 `issues/34` 的 Answer),重做在 **`issues/35`**(含三条缺陷的根因与验收)。在重做达标之前 `map.tilesets` 留空。正式部署的操作手册在 **`docs/cockpit-deployment.md`**。 - **地图图例的进一步优化**(维护者 2026-09-18:「图例可能在之后需要进行优化,目前这个状态先保持即可」)—— 图例已按上一轮要求移到一张图最上方(`issues/33`),**暂不再改**;待维护者提具体要求后再立票。 - 设计公司视觉终稿如何替换驾驶舱占位 token。 - 后端补契约的落地时间与责任人(见 [后端契约缺口待办(跨领域两项 + 补契约清单)](issues/25-backend-contract-gaps.md) 的「现行补契约清单」;当前前端面板新增 I1/I2/E1 与新模型 J1/J2,历史关闭票不是现行联调验收)。 ## Out of scope - 登录、权限管理、工单处置等写操作。 - UE5、Pixel Streaming、Docker、国产化认证。 - CAD 转换、坐标精化、高精度模型生产。 - 重写 `issues/01`–`23`。