map.md 74 KB

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/<effort>/,没有 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: 总体概览模式面板(依赖 17/18/19,已关闭)、22: Cesium 地理管线渲染(依赖 21)、23: Three.js 管线增强(依赖 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 降级。
  • 重做沅陵县城体块模型 仍在执行,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 — 二级面板改为同屏左右两栏加标题栏展开/收起,替代 mode/subview 切换。
  • 决策地图 — mode/panelId 英文词典、三键相机契约、地面与管线视图合成、Cesium 集成方式、无登录页鉴权。
  • ADR 0003 — 页面信息架构与 mode/panelId 词典。
  • 01: Shell 与模式/面板映射 — 已关闭(第二轮):外壳铺满视口、每模式 4 面板同屏且收放真正改变高度、范围/行业接入 filterStore 且切模式不重置、一张图用临时影像铺满背景;浏览器走查 16/16 + 维护者目测确认。
  • 26: Dev server 文件监视被 .scratch 面板原子写击穿 — 已修复(含重开补验):.fes.js 的 dev server watch 忽略 **/.scratch/** 与 **/.dsh-*(Vite 默认 ignore 合并不覆盖);两个触发源——面板原子写与 DSH 编辑审阅临时文件——均实测不再击穿。
  • 02: MapCanvas 常驻挂载 — 已关闭:新增 services/mapService.js 持有实例生命周期(幂等挂载、尺寸上报、离开才销毁)+ 常驻宿主组件 MapCanvas.vue;逻辑单测 + 走查 A14 证明「切模式与收放面板都不重建」。
  • 06: 驾驶舱自有静态资产与视觉占位 — 已关闭: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 — 已关闭: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 — 已关闭:.cockpit-shell 由 height: 100vh 改为 absolute + inset: 0,贴着最近的定位容器(没有定位祖先时按规范落回初始包含块=视口);氛围层同步由 fixed 改 absolute,不再盖到宿主页头那一带。零依赖脚本 7/7(旧实现复现「越界 54px + 宿主滚动条」,交付实现容器等高、底边贴合、真·视口 resize 跟随),走查 A12 仍绿。附带确立:真实启动路径是 yarn dev,predev 预演站不是真实应用环境。
  • 04: 配置与凭据注入 — 已关闭:四层运行时配置(内置默认 → VITE_COCKPIT_* → <base>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: 底图影像(配置驱动) — 已关闭: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: 相机控制契约 — 已关闭(维护者人工验收「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: 进入系统的引导与视角预设 — 已关闭:进 /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: 生产环境界面降噪 — 已关闭:正式环境不再把一大块状态文本压在中央 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: 一张图浮层布局 — 已关闭:图例移到一张图最上方(距画布顶 11px,颜色语义任何形态都在),调试形态的标题/图层占位收进其下方的抬头;地图摘要收成小胶囊(去掉「地图摘要」文字标题,状态点 + 短文案 + 动作按钮,宽 230px,role=status 保留语义);并修掉票 32 引入的引擎状态与底部摘要行叠字(生产小字 bottom 2→26px、调试药丸 top 74→96px)。三条都按真实几何断言(getBoundingClientRect 相交判定),走查 8/8 + 全量回归 13 个浏览器套件全绿。追补(维护者看渲染图后指出):真正挡屏幕的是「无地图位置」浮块 → 收起形态收成一行小胶囊 160×26px、展开上限 340→280px,两条口径与清单改为「常驻 DOM、视觉收起」以保住票 07 的断言面;走查 12/12。
  • 34: 沅陵县城轻量体块模型 — 已关闭(已交付但被否决):用真实经纬度原料(OSM 轮廓/路网/水系 + 1:5 万居民地面 + 本地 DEM)程序化生成县城体块,tools/city-massing/ 下留了可重跑生成器与回验脚本。地理配准是对的(投影到目标点 ENU 再写 transform;回验证伪整条坐标链:最大 8/8 个真实轮廓外接盒角点在 GLB 里 2m 内命中、原点回算误差 0.000m),但维护者目测否决:① 屋顶是漏的(顶面绕序与法线不匹配 + 材质 doubleSided:false → 背面剔除)② 尺寸与卫星图不搭边 ③ 模型压在地形之上、不被遮挡(高程取自 17×17 粗网格插值,逐栋没按 DEM 采样 → 有的飘有的陷)。教训:能加载 + 坐标对 ≠ 看起来对;重做转 35,map.tilesets 已置空。
  • 14: 事件详情与设备定位 — 已关闭(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 状态更新 — 已关闭(真机 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: 数据新鲜度与过期状态 — 已关闭(真机 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 领域适配层 — 已关闭:驾驶舱的领域适配层落地。交付边界经维护者裁定为「接缝 + 共享详情域」(面板级聚合留给 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: 地图摘要分页与快照 — 已关闭:分页装载器落在 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: 坐标校验与无地图位置 — 已关闭:坐标准入判据收成唯一一处 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: 点位状态图例与标记 — 已关闭:点位状态→视觉表现收成唯一一张表 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: 工单状态时间线 — 已关闭(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: 设备详情聚合与分区状态 — 已关闭:设备详情六个分区(档案/指标/预警/报警/工单/图片)按设备编码加载。取数收进 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: 监测点详情与设备卡片 — 已关闭:两级详情落地。层级是一条纯函数导航状态机 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: 基础设施模式面板 — 已关闭:基础设施模式四块面板(基础档案 / 运行信息 / 管理信息 / 应急保障)各接一个后端面板级聚合读模型端点,四块各自成败(一块坏了其余三块照常画出真实内容)。契约适配 + 视图计划收在一层纯函数 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: 运行监测模式面板 — 已关闭(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: 事件列表与预警/报警切换 — 已关闭(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) — 已关闭(100%):新增 map.primaryArea.bounds([西,南,东,北] 度,与 map.overlays[].bounds 同口径)作区域限位与自建雾的唯一开关。判定收进新 domain/primaryArea.js(形状 + west<east、south<north),配置层原样收下、绝不自动翻转甲方给的 [W,N,E,S](翻对了没人发现抄错,翻错了就是拿假范围锁相机);未配置 / 顺序非法 / 个数不对三条路都走关闭且原因措辞互不相同,浮标与 data-map-primary-area(-reason) 如实读出。本部署显式值=甲方框定沅陵县城范围(与甲方原值只差纬度两值对调,逐位差 < 1e-9 度);config.example.json 与部署手册(新增 2.9)同步。计划与引擎解耦:算在「引擎已关闭」早退之前 —— 这条是 DOM 宿主探针抓到的真缺陷(修复前关引擎的部署在浮标上完全失声)。票 37/38 的唯一入口是 mapService.getPrimaryAreaPlan()(返回副本)。逻辑闸门 t36(验收点名的四类输入 + 静态守卫,5 处变异全部打红)+ 全量回归 21 个闸门全绿(t05 守卫面 218→230);真机走查 5/5 通过(维护者 2026-09-28 要求执行)—— 因本机 puppeteer 的 @puppeteer/browsers 是空目录,改用 DSH 自带浏览器真实驱动,走真实运行时配置文件层(data-source=file)而非注入旁路,A1 合法 / A2 未配置 / A3 顺序写反 / A4 个数不对 / A5 关引擎各一条,每条同读宿主 data-*、getPrimaryAreaPlan() 与浮标文本,截图存证。
  • 37: 进区锁定与相机区域限位 — 已关闭(100%):需求①「限高限低」的落地。新增 domain/areaLimits.js(限位解析=区域 home.range × 视图 [400,6000] × 全局三处取交集且只有 applyAreaLimits() 一个合成点、软区夹取、防入地、状态转移、顶栏形态、闲置判定);areaState 进 filterStore(与 viewMode 同层,切模式不重置),服务层 setAreaState 是唯一转移入口(归位即进区);只有「返回全球」能解除并飞回程序初始态全景(0/−60/2×10⁷),全球态闲置 10 秒回引导页(区内不计时、不重置模式与筛选;2026-09-30 由 5 秒改定)。维护者 2026-09-28 裁定一处契约缺口:验收第 3 条的「中心点 ∈ bounds」只管手动三键,点选/定位到框外点位是例外(监测点散布全县 105×122 km,主场景区只框县城 13.4×11 km,硬夹会把绝大多数点位挡在区外、镜头停在框边)——该缺口 spec.md 自己记为「待平行图 03 裁决」,而平行图只裁了 range/高度;拖拽手感=按紧向外推到软区边停止、松手自动弹回;两条已落 ADR 0009。另澄清一处维度口径:防入地原文 h ≥ 地形 + 10 m 把相对高度与绝对高程相减,字面直译会把 120 m 下限顶到 ≈148 m、当场推翻票 30 已验收的「拉到 120 m 仍见地面」,实现按「相机绝对高度 ≥ 其正下方地形 + 10 m」取。逻辑闸门 t37(假引擎驱动 7 组输入链)+ 6 处变异全部打红(含"防入地退回字面口径"实测 148.4 m);真机走查 10/10(进入→锁定→拉远被夹在 12000→管线仍 6000→拖到软区边松手弹回→返回全球精确落 0/−60/2×10⁷→全球闲置回引导、区内闲置不回)。实现中抓到并修掉两个真缺陷:① applyAreaLimits() 早于 viewMode 赋值 → 切管线时按地面算限位(domain 闸门与静态守卫都看不见,已做成顺序断言);② 新 chip 把顶栏状态区挤爆 46px、「归位」压住「预警处置」 → 状态列改 minmax(min-content, 1fr),并加一条按真实几何的走查断言。全量回归 23 个闸门全绿(t05 守卫面 230→233)。
  • 19: 预警处置模式面板 — 四块预警处置面板完成:当前事件/工单状态/设备定位、历史趋势范围、处置效率与时长、多维事件分布和重点识别;四块共享 eventStore.mode,mock 阶段已收口,真实联调仍受 #25/J2/BT-5 约束。2026-09-29 由票 20 重开复核(非重做):按维护者裁定把工单字典对齐后端 WorkOrderStatusEnum 0–7 时,查出本票两处真缺陷 —— ① 「工单状态分布」标签整体错位一格(旧的 1–7 把「已结案」标在码 6 上,而后端 6 是「延期中」、5 才是「已结案」),已随字典对齐修正、标题改为中性的「工单状态分布」;② 处置率「分母为 0」会画虚假 0.0%(后端回 state=EMPTY+value="0.0000"),已修并导出 handlingRateView/handlingRateHint 供态势总览共用同一条判据。另发现本票从未把新文件补进 t05 静态守卫面(票 11/17/18 都补过),实测那两个文件干净后已补入(t05 255→270)。票面七条验收的前端交付面不变。
  • 38: 区内视距雾气 — 已关闭(100%,维护者 2026-09-28 真机确认「效果没问题,完成验收」):需求②的落地。新增 domain/fog.js(18/42 km 衰减曲线且端点精确 —— 0 处恰好 0、42 km 恰好 1,单纯指数衰减在有限距离到不了 1 故不算达标;门控、进出推进与落态、过期取消、进入高度带只出强度标量、降级选择、色相判据、「就绪≠已遮住」的读数形状、性能预算)+ 引擎侧全仓唯一一处 PostProcessStage(B 方案:只处理有有效几何深度的地物,无深度背景保留原色)+ 服务层策略与生命周期 + map.fog 配置 + 顶栏读数 + 部署手册 2.11。门控收口在 setAreaState(区域状态一变雾就跟着走)。逻辑闸门 t38 + 11 处变异全部打红;全量回归 24 闸门全绿(t05 守卫面 233→236)。性能实测 P95 增幅 0%(开/关雾各 120 帧强制渲染计时;口径限 CPU 侧、非 GPU 时间,已如实标注)。真机验证抓到 4 个真 bug,其中最隐蔽的一个由维护者实报「之前能看到雾、现在看不见了」揪出:动态 uniform 用了常量形式 → Cesium 在 createUniformMap 时一次性快照、之后每帧读快照,于是改原始对象永不生效,alpha 恒为 0;而所有读数都说正常(strength:1、data-*、enabled、ranOnce 全对)——只有画面对不上,任何"读数断言"都会漏过它,实证对照 deliveredToShader 0 vs 1。已改为函数形式 uniform(与可丢弃原型同款)并留运行期诊断口 getFogState().uniformDiagnostics(读 _uniformMap 这条每帧真正取值的路径)+ 静态守卫(变异确认有牙)。另三个:null 被 Number(null)===0 当成 0 秒渐变、stage.ready 随渲染活动回落导致待命被误报 failed(failed 改为只由确凿证据判定,另报 ranOnce)、chip 与宿主同名 data-map-fog 导致 querySelector 命中错元素(本仓票 07 栽过同一个坑)。顺带修掉并发会话遗留:eventStore.js 的 JSDoc 缺开头致整个模块语法错误 + useMapStore 用了没 import;t18 两条钉在会漂写法上的断言改为按不变式判。 2026-09-29/30 第二轮(维护者真机复核后新增,见票面「维护者第二次现场复核」):维护者实报「退出时雾气消散太慢、整个地球变白一段时间」,并指出第一版修复又引出「雾几乎一瞬间就产生或消失」。根因是我上一轮漏算的量级问题:返回全球 range 跨 1.7e6 倍(12 km→20,000 km),而雾带只有 20–42 km(占跨度的 0.0001%)⇒ 阻尼(τ=110 ms)下穿越整条雾带只要 0.15 ms,所以任何"瞬时高度驱动"都必然跳变。修法两层:① 时间限速(approachAltitudeFactor + altitudeSlewMs,单帧最多走 dt/slew,另夹 FOG_MAX_FRAME_MS = 120)—— 把"雾的变化速度"变成结构性保证,与相机飞多快解耦;② 返回全球改走按时长 + 慢起缓动的飞行(不再用阻尼),因为阻尼是 1−e^(−dt/τ)、起步最快,头一帧就越过整条雾带。另查明「进入时变绿」的真因是几何而非雾强度:雾是距离雾(18 km 起步)而区内相机仅约 9.8 km 高 ⇒ 屏幕中心的视线距离恒 < 18 km,雾在结构上盖不住它(实测脚下 9.8 km→遮蔽率 0.000、水平 25 km→0.634),故中心观感只看精细层 alpha,修法是缩短渐显窗口而不是加雾强度。新增守卫:t38 的高度限速一节 + "限速必须真的挂在渲染帧上"(这条是变异测试抓出来的 —— 第一版只断言"函数内部调了限速函数",插一句提前 return 就能骗过);cockpitConfig.js 新增 altitudeZeroM > altitudeFullM 守卫(写反等于关掉该保护,只报 issue 不替改值)。变异 10/10 + 改写后 6/6 全部打红。最终时长由维护者在真机调定并裁定"以我为准"(fadeInMs 900 / fadeOutMs 300 / altitudeFullM 14000 / altitudeZeroM 3000000 / altitudeSlewMs 600)—— 部署层取值与代码默认(800/400、20/42 km、400 ms)有意分离,代码默认保持"雾先淡完"的取向供别的部署沿用。

  • 39: 粗→精影像交接 — 已关闭(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: 全程质量收口 — 已关闭(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: 监测点定位与面板锚定接入 — 已关闭(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: 关联监测设备临时图层 — 已关闭(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: 总体概览模式面板 — 已关闭(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: 面板与三维设备点双向对应 — 已关闭(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: 设备位置符号与可替换资产接缝 — 已关闭(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: 一级模式图层计划与选择态矩阵 — 已关闭(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: 重叠设备聚合与展开 — 已关闭(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: 设备状态视觉特效与门控 — 已关闭:中心图标常亮,外围能量环表达设备状态,设备详情随所选设备换锚;主场景 MP-001 K=2 / MP-002 K=11 已同步五态样例,维护者人工确认及本轮相关回归通过,代码 09ef651 已推送(本轮按项目规则未跑 build/debug/浏览器)。

  • 45: 重叠设备聚合与展开(维护者复验重开) — 当前 claimed:监测点名称、聚合明确计数、点击暗淡固定展开、所选设备持续作为聚合入口已修复;连续交互19项与相关脱浏览器检查通过,待本轮画面确认,旧已关闭记录仅作历史。

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。
  • 后端补契约的落地时间与责任人(见 后端契约缺口待办(跨领域两项 + 补契约清单) 的「现行补契约清单」;当前前端面板新增 I1/I2/E1 与新模型 J1/J2,历史关闭票不是现行联调验收)。

Out of scope

  • 登录、权限管理、工单处置等写操作。
  • UE5、Pixel Streaming、Docker、国产化认证。
  • CAD 转换、坐标精化、高精度模型生产。
  • 重写 issues/01–23。