40-quality-closeout.md 14 KB

40: 全程质量收口(内部换级 / 降级 / 性能 / 证据)

What to build: 把前三票的「方案通过」收成「生产可用」:确认矩形内部换级与各降级路径的真实表现,拿到性能与走查证据,回写偏差,并把可丢弃原型归档。

Blocked by: 39

Status: resolved Labels: ready-for-agent

契约来源

  • 规格:cockpit-primary-area-guardrails/spec.md、spec-fog.md。
  • 决策:cockpit-primary-area-guardrails/issues/07-fog-implementation-spec.md、08-coarse-to-fine-transition-solution.md(均已 resolved)。
  • 迷雾清单:cockpit-primary-area-guardrails/map.md 的「Not yet specified」两条。

验收

  • 矩形内部 LOD 换级:真机确认是否肉眼可辨;若可辨,在「不接管 Cesium 场景、不引第二渲染器」的边界内评估局部过渡或 shader 羽化;预烘焙中精度影像层仅作更高成本兜底。
  • 异常降级四条路径各有可复现证据且提示如实(不宣称已遮缝):① 无深度能力;② 自建雾运行失败;③ 目标瓦片失败;④ 主场景区未配置(关闭限位与雾)。
  • 管线视图:近处管线可读;若因透明深度错位减弱/暂关雾,记录实际处置与提示文案。
  • 性能:同机 / 同分辨率 / 同视角 / 相同加载条件下,取无原型基线与开启雾后的重复采样 P95 帧耗时,合计增幅 ≤15%;首次启用与进出切换的卡顿单独记录。读数口径写清(渲染事件间隔 ≠ GPU 时间)。
  • 走查证据:进入 → 锁定 → 拉远被夹 → 位于边界回弹 → 返回全球 → 闲置回引导;并覆盖浅俯角与多个旋转方向。
  • 规格同步:实现与 spec.md / spec-fog.md 的偏差逐条回写票面;改口径需维护者确认,不静默漂移。
  • 原型归档:可丢弃原型与 /cockpit 入口的 DEV 接线单独保存到 throwaway 分支,并在本票留分支指针;/cockpit 的 pages 入口在生产路径下不再挂原型。
  • 运维自检:docs/cockpit-deployment.md 增一节「限位与雾气是否生效」(或明确记为遗留,写明原因)。
  • 收口时区分三类结论:已验证(有证据)、已知代价(明确接受,例如降级时远景保护减弱)、遗留(列明下一步),不得把原型观感认可写成生产达标。

进度:100%(2026-09-30 维护者真机验收通过,收口)

一、维护者新报缺陷(2026-09-30):闲置回引导态"看不出来"

原话:

退出到地球视图后,在重新回到引导状态时,摄像机角度没有回到引导状态,且地球也没有重新开始旋转, 除了中心出现「点击进入系统」外用户不一定能意识到进入引导状态了。

这不是新需求,是一次实现漂移 —— ADR 0009 第 27 行早已写死: 「全球态闲置 5 秒未操作 → 回到程序初始态:地球自转恢复 + 屏幕中央闪烁『点击进入系统』」。 所以它同时踩了两条既有契约:票 37 验收第 3 条、以及上面这句 ADR。

两个根因(都在代码里坐实,不是猜):

  1. 相机没回初始全景。 CockpitShell.vue 的 returnToIntro() 只做了两件事 —— introVisible.value = true 与 setGlobeSpin(...),没有把相机送回去。 于是画面停在用户最后停留的视角上,"回初始态"就只剩中央那行字。
  2. 自转起不来,而且失败被伪装成成功。 mapService 用了粘滞开关 instance.spinPaused: goHome() 与 returnGlobal 把它置 true,而 setGlobeSpin(>0) 命中它时 return true —— 代码里没有任何地方会把它清回 false。于是回引导态那次起转被静默吞掉, 且调用方(外壳挂载时的 600 ms 重试)以为已经成功、不再重试。

修法:

  • 相机侧:新增 mapService.resetToIntroView(),把相机送回 INITIAL_PANORAMA(0/−60/2×10⁷), 中心沿用当前位置(与「返回全球」同一条口径);returnToIntro() 改成 「显示引导层 + 回初始全景 + 恢复自转」三件事一起做。 已经在初始全景上就不排飞行 —— 否则自转要白等一个飞行时长才起得来。
  • 自转侧:删掉粘滞开关,把"意图"与"正在转的循环"分开 —— 意图 spinWanted(想要几秒一圈)、循环 instance.spin。 setGlobeSpin() 先记意图;"现在能不能转"按相机是否真的停稳(state.settled)判, 不能转就如实 return false(不再假装成功);等相机落定后由 resumeSpinIfWanted() 自动接上 (挂在 notifyCameraStateChange,飞行最后一帧必到)。 stopSpin() 负责放弃意图(进入系统/归位/返回全球都不该再自己转起来), startSpin() 只停循环、不抹掉意图。
  • 读数加 spinWanted:只报 spin 分不清「意图立着但相机还没落定」与「压根没想转」。

证据:

  • 新闸门 verify/t40-intro-return.mjs(静态守卫 + 一处纯行为断言):禁止 spinPaused 复活、 断言"记下意图之后不得 return true"、断言 stopSpin 放弃意图而 startSpin 只停循环、 断言落定后兑现意图、断言 returnToIntro 三件事齐备、断言 resetToIntroView 套用初始全景 且已在其中时不排飞行、断言阈值复用 SETTLE_EPSILON、断言读数带 spinWanted。
  • 定向变异 10/10 全部打红 —— 把两个原缺陷的形状各自复现一遍都抓得住: return true 假装成功、spinPaused 复活、startSpin 误用 stopSpin、stopSpin 不放弃意图、 落定后不兑现、回引导态不送相机、不套用初始全景、去掉"已在其中"短路、改用魔数、读数去掉 spinWanted。
  • 部署手册第 5 节补一行「闲置回引导」自检(含读哪两个读数);顺手修掉该表一处属性名写错 (data-fog-phase → data-map-fog-phase;实际写的是 host.dataset.mapFogPhase)。
  • 全量闸门 33 通过 / 1 失败,唯一失败 t04-config 与本票无关(并发会话的 cockpitApi.js 硬编码后端地址,见票 07 方向);t37 已随「契约改 10 秒」转绿。

二、诊断中发现的规格漂移(2026-09-30 已由维护者定夺:改为 10 秒并同步契约)

domain/areaLimits.js 的实现值原先是 10000;本轮并发写入期间一度被改成 2000,导致 t37 立即失败。维护者已经明确选择「我要 10 秒:把契约一起改」,因此我把实现恢复为 10000,并同步更新了五处权威: ADR 0009 / 票 37 验收 / 部署手册 / 常量注释 / 闸门 t37(含 walkthrough 的等待时长)。 现在实现与契约重新一致,t37 已转绿。

三、逐条验收状态(维护者 2026-09-30 真机验收通过,全部收口)

# 验收项 状态
1 矩形内部 LOD 换级真机确认;可辨则在不接管场景/不引第二渲染器边界内评估 已验证(维护者真机确认通过) —— 验收第 1–10 条整体通过
2 异常降级四条路径各有可复现证据且提示如实 已验证 —— 逻辑面(① 无深度能力 / ② 自建雾失败 → t38-fog;③ 目标瓦片失败 → t39-handover;④ 主场景区未配置 → t36/t37)+ 维护者真机走查
3 管线视图:近处管线可读;若因透明深度错位减弱/暂关雾,记录处置与文案 已验证(维护者真机确认通过)
4 性能:无原型基线与开雾后 P95 帧耗时,合计增幅 ≤15%;首启与进出切换卡顿单独记录 已验证(维护者真机确认通过) —— 票 38 亦测得开/关雾 P95 增幅 0%(CPU 侧口径,已如实标注)
5 走查证据:进入 → 锁定 → 拉远被夹 → 边界回弹 → 返回全球 → 闲置回引导;覆盖浅俯角与多旋转方向 已验证(维护者真机确认通过) —— 末段「闲置回引导」功能缺陷本轮已修(resetToIntroView + 自转意图),闸门 + 变异 10/10
6 规格同步:实现与 spec 的偏差逐条回写;改口径需确认 已完成 —— ① 票 39 部署值 vs 代码默认(有意分离,已记录);② GLOBAL_IDLE_MS 5秒→10秒(维护者确认,契约五处已同步);③ 引导视角契约改为 Cesium Viewer 默认相机(维护者裁定,见下)
7 原型归档到 throwaway 分支 + 留分支指针 + 生产路径不挂原型 已完成 —— 归档分支 throwaway/cockpit-prototypes @ 81932a0;生产树已删 src/modules/cockpit/prototypes/ 并摘除 /cockpit 入口接线(见下)
8 运维自检:手册增一节「限位与雾气是否生效」 已完成 —— 手册第 5 节「部署后自检」全表;本轮补「闲置回引导」一行并修一处属性名错误
9 收口时区分已验证 / 已知代价 / 遗留 已完成 —— 见上表与下方"已知代价"

四、已知代价(明确接受,不当作达标)

  • 交接 requireTilesLoaded: false 牺牲的正是决策 08 设它要防的"先空白再跳方块"(票 39 记录)。
  • 退出时「相机已高于雾的全遮蔽端点而雾仍可见」的窗口 ≈ 387 ms(票 38/39 记录)。

五、验收第 7 条(原型归档)— 已完成(2026-09-30,维护者确认"现在就做")

归档用 git plumbing(临时 index)完成,没有动工作树里的并发在途改动:

  • 归档分支:throwaway/cockpit-prototypes @ 81932a0 (git log -1 该分支查看 commit:'归档:可丢弃原型(Transition/Fog/Anchor)与 /cockpit DEV 接线(票 40 收口)')
  • 归档内容(7 个原型文件 + 带 DEV 接线的 src/pages/cockpit/index.vue): anchor-prototype-effects.js、anchor-prototype-layout.js、AnchorPrototype.vue、 fog-prototype-stage.js、FogPrototype.vue、transition-prototype-scene.js、TransitionPrototype.vue
  • 生产路径:已删除 src/modules/cockpit/prototypes/;src/pages/cockpit/index.vue 已摘除 三组 defineAsyncComponent / show*Prototype 接线(现在只挂 CockpitShell,全仓再无 cockpit/prototypes 引用)。
  • 波及面已收:t39-handover.mjs 原先读 TransitionPrototype.vue 的两条断言改为 断言"生产树已无 prototypes/ 目录";服务层的 alpha 互斥开关(setHandoverPrototypeActive / applyHandoverAlpha 内 if (handoverPrototypeActive) return)保留 —— 一旦日后从归档分支还原原型, 互斥仍成立。cesiumEngine.js 两处提到 prototypes/fog-prototype-stage.js 的注释已标注"已归档分支"。
  • 还原方法:git checkout throwaway/cockpit-prototypes -- src/modules/cockpit/prototypes src/pages/cockpit/index.vue

Answer(收口结论,2026-09-30)

维护者真机验收通过(原话「验收通过,进行#40收口」),本票 9 条验收全部收口。

本票实际交付

  1. 修掉维护者实报的「闲置回引导态看不出来」缺陷(三个根因三层修):
    • 相机不回位 → 新增 resetToIntroView();
    • 自转起不来且失败被伪装成功 → 删粘滞开关 spinPaused,改 spinWanted 意图 + resumeSpinIfWanted() 落定接上;
    • 接错路径 → 自转兑现同时挂 notifyCameraStateChange 与相机控件的 onStateChange(飞行逐帧路径,落定那一帧必到)。
  2. 引导视角契约定夺(维护者裁定):初始引导页 = Cesium Viewer 默认相机(挂载时 syncFromEngine() 反读,存为 introView 基准);「返回全球」与「闲置回引导」的角度/高度都回到这份基准,中心保持在当前位置(不回大西洋/北美)。INITIAL_PANORAMA 只作 introView 缺席时的兜底。
  3. 闲置时长 5 秒 → 10 秒(维护者裁定),契约五处同步(ADR 0009 / 票 37 / 手册 / 注释 / t37)。
  4. 原型归档:throwaway/cockpit-prototypes @ 81932a0;生产树已删 prototypes/、摘 /cockpit 接线;还原方法见上。
  5. 运维自检:手册第 5 节补「闲置回引导」行,修 data-map-fog-phase 属性名笔误。
  6. 规格同步:三处偏差全部回写且经维护者确认,无静默漂移。

三类结论(验收第 9 条)

  • 已验证:LOD 换级、管线视图、性能 P95、走查链(含浅俯角/多方向)、闲置回引导、原型归档、运维自检 —— 维护者真机验收通过;逻辑面另有 33 个零依赖闸门 + 定向变异(本轮 t40 闸门 10/10、t39 §12 6/6)背书。
  • 已知代价:交接 requireTilesLoaded: false(放弃"先空白再跳方块"保障,票 39);退出时雾仍可见的窗口 ≈387 ms(票 38/39)。
  • 遗留(移交后续):收口时点的 2 个红闸门 t04-config(cockpitApi.js 硬编码后端地址)、t10-point-detail(PointDetailCard.vue 碰标记层)—— 均为并发会话的在途改动,与本票无关,已如实记录。

环境备注(如实记录)

  • 真机走查由维护者在浏览器上完成并确认;本会话曾用 browser-skill(bsk 0.3.2,被并发会话改到 C:\Users\ASUS\AppData\Roaming\npm\bsk.exe)打开 http://localhost:8000/#/cockpit 抓到 data-camera 读数完成诊断。
  • 本会话无 build/dev 验证;全部证据来自零依赖逻辑闸门、定向变异与真机读数。
  • GLOBAL_IDLE_MS 在本会话被并发会话三次改写(10000→2000→1000),均按维护者裁定恢复为 10000 —— 若再漂移,t37 会红。