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