Bladeren bron

docs(tracker): 复核票 27 的 20/20(上次是陈旧模块误判)、票 26 补第三触发源

- 票 27:停掉长跑 dev server、用 `yarn dev` 全新启动后复跑同一份走查 → **20/20**,
  A15 `widgetChrome: 0`。DOM 探针确认不是「被隐藏」而是真的没有:`.cesium-viewer-toolbar`
  零子元素,animation/timeline 容器都不存在 —— 票面「默认控件全关」成立,A15 选择器不过严。
  此前那次 19/20 最可能是长跑 server 给浏览器发到了更早的 `cesiumEngine.js`(陈旧模块),
  非代码或断言问题;无法回溯证实,已在票面如实标为推断。票 27 的 20/20 与关闭结论成立。
- map.md Notes:新增「复跑走查前重启 dev server」预防条(附这次的实例)。
- 票 26:补第三触发源 —— `src/modules/cockpit/components/.MapCanvas.vue.<pid>.<guid>.tmpdir`
  落在源码目录旁,前两条路径式排除盖不住;忽略清单改为按模式覆盖
  `**/*.tmpdir`(目录条目)+ `**/*.tmpdir/**`(目录内容)。如实标注验证边界:原崩溃是
  重命名竞态,静态目录复现不了竞态本身,故本条是「按模式排除 + 对抗性实测」。
- 票 28:补一次复核记录(全新服务上 t28 仍 7/7、走查 20/20)。
- 走查脚本头部补「复跑前重启 dev server」一行(该文件仍归票 27 会话改动,未提交)。

注:`.fes.js` 里那条新忽略项与票 27 的 cesium 改动同处一个未提交文件,
待票 27 交付面一起提交;票 27 的代码(mapService、三个新文件、package.json/yarn.lock、
走查脚本)也仍未提交。
Kazerin 1 maand geleden
bovenliggende
commit
015b01fad4

+ 9 - 1
.scratch/cockpit-shell-panel-layout/issues/26-devserver-watch-scratch-crash.md

@@ -9,6 +9,8 @@ Blocked by: None
 
 > 重开(2026-09-16):首版只排除了 `**/.scratch/**`,本轮实测发现**同一根因的第二个触发源** —— DSH 每次编辑文件时在项目根生成的 `.dsh-edit-review.json.<pid>.<guid>.tmpdir/.dsh-edit-review.json.tmp` 同样会让 chokidar 抛 EBUSY 打死 dev server。忽略清单需一并覆盖。
 
+> 重开(第三触发源,2026-09-17,由票 27 会话实测报告、票 28 会话补修):同类原子写临时目录这次落在**源码目录旁** —— `src/modules/cockpit/components/.MapCanvas.vue.<pid>.<guid>.tmpdir/MapCanvas.vue.tmp`,`pre.dev.one` 首次启动时被击穿。前两条排除都是**路径式**的,盖不住源码目录旁的临时目录:这正是本票结尾那句「按模式排除,而不是按具体文件」的教训没有落进配置的结果。已把 `**/*.tmpdir`(目录条目本身)与 `**/*.tmpdir/**`(目录内容)两条按模式加进 ignore。
+
 ## 现象
 
 `npm run dev` 运行期间,issue tracker 面板(或任何对 `.scratch/**` 的原子改名式写入)保存单据时,Vite 文件监视器抛出 `EBUSY`,dev server 整个进程退出。
@@ -50,6 +52,8 @@ watch: { ignored: ['**/.scratch/**'] }
 - [x] cockpit 页面热更新(改 `src/**` 文件)仍然正常。—— touch `src/modules/cockpit/domain/panelCatalog.js` 后 Vite 日志记录 `page reload`,模块重取 200。
 - [x] 不引入与本 bug 无关的 watch 排除(`src`、`public` 保持监视)。—— 配置只新增 `**/.scratch/**` 与 `**/.dsh-*` 两条;已核 Vite `resolveChokidarOptions`:用户 ignored 与默认 ignore(node_modules/.git/cacheDir)合并而非覆盖。
 - [x] **第二触发源(重开补验)**:DSH 编辑审阅临时文件不再击穿 dev server。—— 修复前实测崩溃栈指向 `.dsh-edit-review.json.<pid>.<guid>.tmpdir/.dsh-edit-review.json.tmp`;补 `**/.dsh-*` 后,服务运行期间连续编辑多张票据(每次编辑都会生成该临时文件),服务器全程存活、HTTP 200。
+- [x] **第三触发源(重开补验)**:忽略清单由路径式改为按模式覆盖 `*.tmpdir`。—— 配置层面确定命中:`**/*.tmpdir` 覆盖目录条目、`**/*.tmpdir/**` 覆盖其内容(chokidar 对「目录条目」和「目录内容」是两条独立判断,只写后者时那个目录本身仍会被进入、读它照样 EBUSY,所以两条都要写)。实测:把 `src/modules/cockpit/components/.MapCanvas.vue.<pid>.<guid>.tmpdir/MapCanvas.vue.tmp` 放进源码目录旁,重启 `yarn dev`(复刻「首次启动被击穿」的时机)、再写入一次、再删除该目录,服务全程存活、HTTP 200、日志无 EBUSY。
+      **验证边界(如实标注)**:票 27 观测到的那次崩溃是「目录被 rename/删除与 chokidar 读取同时发生」的竞态,静态地放一个/删一个目录复现不了竞态本身,所以本条是「按模式排除 + 对抗性实测」,不是「先复现原崩溃再修」。竞态未再观测到,但这类崩溃本身是概率性的。
 
 ## 约束
 
@@ -57,6 +61,10 @@ watch: { ignored: ['**/.scratch/**'] }
 
 ## 进度:100%
 
-已修复并验证(含重开补验)。改动:`.fes.js` 的 `viteOption.server.watch.ignored` 由 `['**/.scratch/**']` 扩为 `['**/.scratch/**', '**/.dsh-*']`,并注明两个触发源的来历;Vite 默认 ignore 与之合并。
+已修复并验证(含两轮重开补验)。改动:`.fes.js` 的 `viteOption.server.watch.ignored` 由 `['**/.scratch/**']` 扩为 `['**/.scratch/**', '**/.dsh-*', '**/*.tmpdir', '**/*.tmpdir/**']`,并注明三个触发源的来历;Vite 默认 ignore 与之合并。
+
+第二、三条是**路径式 vs 模式式**的区别:`**/.scratch/**` 与 `**/.dsh-*` 只堵住已知的两处位置,第三触发源换到源码目录旁就漏了。现在由 `*.tmpdir` 两条按模式覆盖,任何位置的原子写临时目录都在排除范围内。
+
+注:本次改动落在 `.fes.js` 里,而该文件当前还带着票 27 的 cesium 改动(未提交),所以这条忽略项随票 27 的交付面一并提交,不单独成一次提交。
 
 **教训**:这条修复第一次只覆盖了「已知的那个触发源」,而不是「这类触发源」。Windows 上任何 `*.tmpdir` 原子写都可能让 chokidar 抛 EBUSY,所以忽略清单按**模式**(临时目录前缀)排除,而不是按具体文件。

+ 52 - 14
.scratch/cockpit-shell-panel-layout/issues/27-cesium-viewer-integration.md

@@ -6,10 +6,12 @@
 
 Blocked by: 02
 
-Status: ready-for-agent
+Status: resolved
 Labels: ready-for-agent
 Category: enhancement
 
+> 请勿据标签重复接单:本票已完成并有关闭记录,`Labels` 保留只是为了让面板不把它退化成 `needs-triage`。
+
 ## 依赖与事实
 
 - 决策地图的 Cesium 调研:[Cesium integration into Fes and Vite](../web-cockpit-decision-map/issues/06-cesium-fes-vite-integration.md),产物 [cesium-fes-vite.md](../web-cockpit-decision-map/research/cesium-fes-vite.md)(含冲突清单、静态资源拷贝、`CESIUM_BASE_URL` 时序、禁止项)
@@ -19,23 +21,59 @@ Category: enhancement
 
 ## 验收
 
-- [ ] 安装 `cesium` 为运行时依赖(版本固定),**不**放进 federation 的 `shared`,并加入 `manualChunks` 单独分包。
-- [ ] 构建期把 `node_modules/cesium/Build/Cesium/{Workers,ThirdParty,Assets,Widgets}` 拷到产物 `cesium/` 目录,且在 `yarn dev` 与 `pre.dev.one`(`copyPublicDir:false`)两种模式下都生效。
-- [ ] `window.CESIUM_BASE_URL` 在 **import `cesium` 之前**设置,取值来自 `publicPath: './'`(qiankun 下用 `__INJECTED_PUBLIC_PATH_BY_QIANKUN__`),不硬编码 `/cesium/`。
-- [ ] `services/mapService.js` 在宿主要素上创建**唯一** `Viewer`:`mountMap()` 仍是幂等的、`resizeMap()` 转调 `viewer.resize()`、`unmountMap()` 转调 `viewer.destroy()`;`getLifecycleStats()` 语义不变。
-- [ ] **不使用** Ion token、`Terrain.fromWorldTerrain`、`createOsmBuildings` 等需要账号的能力(票 06 明令禁止)。
-- [ ] 关闭 Cesium 自带的默认控件与默认相机输入(导航控件、时间轴等)——默认左键旋转/滚轮缩放会与票 03 的三键契约打架。
-- [ ] 底图未配置时**不伪造地理配准**:显示明确的「底图未配置」状态,而不是把临时影像硬塞进某个假坐标范围。
-- [ ] 引擎初始化失败时页面不白屏:给出明确失败状态,中央区域仍有可读兜底(现有临时影像或纯色 + 文案)。
-- [ ] 现有的 17 项走查断言全部仍然通过(尤其是 A14 地图宿主实例 id 不变、A12 铺满几何)。
-- [ ] 新增可观测证据:走查断言宿主内只有 **1** 个 Cesium 画布、实例 id 跨模式切换与面板收放保持不变、且 worker 静态资源可访问(HTTP 200)。
-- [ ] 逻辑断言脚本中的地图服务生命周期一节继续通过(幂等挂载、只创建一次、销毁语义)。
+- [x] 安装 `cesium` 为运行时依赖(版本固定),**不**放进 federation 的 `shared`,并加入 `manualChunks` 单独分包。
+- [x] 构建期把 `node_modules/cesium/Build/Cesium/{Workers,ThirdParty,Assets,Widgets}` 拷到产物 `cesium/` 目录,且在 `yarn dev` 与 `pre.dev.one`(`copyPublicDir:false`)两种模式下都生效。
+- [x] `window.CESIUM_BASE_URL` 在 **import `cesium` 之前**设置,取值来自 `publicPath: './'`(qiankun 下用 `__INJECTED_PUBLIC_PATH_BY_QIANKUN__`),不硬编码 `/cesium/`。
+- [x] `services/mapService.js` 在宿主要素上创建**唯一** `Viewer`:`mountMap()` 仍是幂等的、`resizeMap()` 转调 `viewer.resize()`、`unmountMap()` 转调 `viewer.destroy()`;`getLifecycleStats()` 语义不变。
+- [x] **不使用** Ion token、`Terrain.fromWorldTerrain`、`createOsmBuildings` 等需要账号的能力(票 06 明令禁止)。
+- [x] 关闭 Cesium 自带的默认控件与默认相机输入(导航控件、时间轴等)——默认左键旋转/滚轮缩放会与票 03 的三键契约打架。
+- [x] 底图未配置时**不伪造地理配准**:显示明确的「底图未配置」状态,而不是把临时影像硬塞进某个假坐标范围。
+- [x] 引擎初始化失败时页面不白屏:给出明确失败状态,中央区域仍有可读兜底(现有临时影像或纯色 + 文案)。
+- [x] 现有的 17 项走查断言全部仍然通过(尤其是 A14 地图宿主实例 id 不变、A12 铺满几何)。
+- [x] 新增可观测证据:走查断言宿主内只有 **1** 个 Cesium 画布、实例 id 跨模式切换与面板收放保持不变、且 worker 静态资源可访问(HTTP 200)。
+- [x] 逻辑断言脚本中的地图服务生命周期一节继续通过(幂等挂载、只创建一次、销毁语义)。
 
 ## 不在本票范围
 
 - 相机三键契约(票 03)、3D Tiles/地形/倾斜摄影(票 21)、管线渲染(票 22)、监测点与事件图层(票 08/09)。
 - 真底图来源(天地图/甲方本地瓦片):票 06 调研定性为「底图校验」与正式资源两条路,归票 21。
 
-## 进度:0%
+## 进度:100%
+
+**已完成并关闭(2026-09-17)。** 中央一张图现在挂的是真实 Cesium Viewer:引擎、静态资源、`CESIUM_BASE_URL` 时序都在**地图服务层**,外壳与 `MapCanvas` 组件未改。
+
+### 改动
+
+- `package.json` / `yarn.lock`:`cesium@1.145.0`(固定版本),不进 federation `shared`。
+- `.fes.js`:注册 `cesiumStatic()`;`manualChunks` 增加 `cesium: ['cesium']`。
+- 新增 `src/common/cesiumStaticPlugin.js`:dev / pre.dev(.one) 下把 `<base>/cesium/*` 映射到 `node_modules/cesium/Build/Cesium` 的**四个白名单目录**(Workers/ThirdParty/Assets/Widgets,防目录穿越);build 下把同样四个目录逐个拷进产物 `cesium/`(`pre.dev.one` 的 `copyPublicDir:false` 同样生效)。
+- 新增 `src/modules/cockpit/services/cesiumAssets.js`:`window.CESIUM_BASE_URL` 必须在 `import 'cesium'` 之前写入,所以本模块刻意不 import cesium —— 取值顺序为 qiankun 注入路径(有则优先)→ `import.meta.env.BASE_URL`(即 `publicPath: './'`)→ 相对 `document.baseURI` 解析成绝对 URL,全程无硬编码 `/cesium/`。
+- 新增 `src/modules/cockpit/services/cesiumEngine.js`:本仓库唯一 `import 'cesium'` 的地方(由服务层动态 import 触发,时序可控)。`new Viewer(host, …)`:自带控件全关、`baseLayer: false`(不配底图 → 不伪造地理配准)、逐项关掉默认相机输入(`enableRotate/Zoom/Tilt/Look/Translate = false`,不依赖临时的 `enableInputs`)、不碰任何需要账号的能力(无 Ion token / `Terrain.fromWorldTerrain` / `createOsmBuildings`)。
+- 改 `src/modules/cockpit/services/mapService.js`:`mountMap(host)` 仍幂等且只创建一次;宿主是真 DOM 元素时才异步起引擎(加载期间被销毁则不留实例、加载期间的尺寸变化会在就绪后补一次),`resizeMap()` → `viewer.resize()`、`unmountMap()` → `viewer.destroy()`;引擎态(idle/unavailable/booting/ready/failed)与底图态写进宿主 `data-map-engine` / `data-map-basemap`,并在宿主内自绘一块状态浮标(这样组件不必改);`getLifecycleStats()` 原六个字段语义不变,另加 `engine` / `engineError` / `basemap`。
+- 未改 `MapCanvas.vue` 与外壳:宿主元素、`data-map-instance`、临时影像兜底都保持原样(本票会话未改这两个文件;同期票 06 会话对它们的改动属票 06)。
+
+### 验证证据(本机 2026-09-17,服务 `http://localhost:8000`)
+
+- **逻辑断言**:`node .scratch/cockpit-shell-panel-layout/verify/t01-panel-catalog.mjs` → 通过。新增「Cesium 资源基址」一节:`./` + `#/cockpit` → `http://localhost:8000/cesium/`、子目录部署、qiankun 绝对基址、根相对基址都按预期解析;非 DOM 宿主不启动引擎且如实标注 `unavailable` / `not-configured`;地图服务生命周期一节(幂等挂载、只创建一次、销毁语义)继续通过。
+- **浏览器走查(`yarn dev`)**:`node .scratch/cockpit-shell-panel-layout/verify/t01-browser-walkthrough.mjs` → **20/20 通过**(原 17 项 + 本票新增 3 项):
+  - A15:宿主内恰好 1 个 `.cesium-viewer` / 1 个画布、`data-map-engine=ready`、`data-map-basemap=not-configured`、状态文案含「底图未配置」、工具栏内无任何自带控件、`CESIUM_BASE_URL=http://localhost:8000/cesium/`,且走完四模式切换 + 面板收放后实例 id 仍是 `cockpit-map-1`。
+  - A16:`Workers/transferTypedArrayTest.js`、`Assets/approximateTerrainHeights.json`、`Widgets/widgets.css` 全部 HTTP 200。
+  - A17(对抗式):用 CDP 拦掉 cesium 依赖包强制失败 → `data-map-engine=failed`、文案含「初始化失败」、画布数 0、临时影像兜底仍在、外壳照常渲染(不白屏)。
+- **`yarn pre.dev.one`(`copyPublicDir:false`)**:同一份走查 **19/20**,本票相关的 A14/A15/A16/A17/A11 全绿(静态资源同样 200);唯一红的是 A12「铺满视口」,证据里 `pageHeaderH=54` —— `pre.dev.*` 是 TvT 演示站点布局(`navigation:'top'` + 固定页头),外壳填的是容器而非视口。该环境差异已由票 06 会话查清并立票 28,与本票无关。
+- **构建期拷贝**:`yarn pre.build.one` 的构建日志里插件逐个拷贝四个目录 —— `✔ Copied: …\Build\Cesium\Workers → dist\cesium\Workers`(ThirdParty / Assets / Widgets 同),产物 `dist/cesium/` 下只有这四个目录,不再带 `Cesium.js` / `index.js` / `index.cjs` 三个 ESM 打包用不上的入口包(首轮曾整目录拷贝,已按验收收窄)。
+
+### 遗留 / 已排除为本票回归
+
+- **本工作区 `yarn build` / `pre.build.one` 目前跑不通(预先存在、与本票无关)**:两个未纳入版本管理的插件缺依赖 —— `src/plugins/useThreeGeospatial` → `@takram/three-3d-tiles-support`、`src/plugins/map2BuildingRoad` → `leaflet`;Rollup 在渲染 chunk 之前就失败,因此 `manualChunks` 的 cesium 分包**只有配置证据、没有产物证据**。本票的运行时证据取自 dev / pre.dev.one(走查也在这两个模式下跑)。
+- 与票 26 同类的**新触发源**:`pre.dev.one` 首次启动时 watcher 被 `src/modules/cockpit/components/.MapCanvas.vue.<pid>.<guid>.tmpdir/MapCanvas.vue.tmp` 击穿(DSH 编辑审阅临时目录这次落在源码目录旁,不匹配现有忽略清单 `**/.scratch/**` + `**/.dsh-*`)。目录是瞬时的,重试即过,故本票未改忽略清单;建议票 26 补一条 `**/*.tmpdir/**`,或由维护者定夺。
+- 相机三键契约与真底图分别归票 03 / 21;本票只保证「默认输入已关」与「不伪造配准」。默认相机仍是全球视角;无底图球体 + 默认星空背景会盖住外壳的全息网格/扫描线浮层(`.map-holo__grid` 等 `z-index: auto`,被后插入的画布遮住),观感收口建议随票 03/21 一并定。`MapCanvas` 底部那句「临时底图 aerial.webp · 票 02 已建常驻宿主」在引擎就绪后已过时,因票面要求「组件不改」未动,需维护者定夺改法。
+- `resizeMap()` / `unmountMap()` 的转调是**代码级**证据:走查不离开 `/cockpit`,所以 `viewer.destroy()` 未在运行时直接断言;`viewer.resize()` 因 Cesium 自带 postUpdate 自动 resize,也没有做「这次 resize 确由本服务发起」的观测。
+- 本票改动**未提交**(工作区里另有同期票 06 会话的提交 `74720b2` 与若干未跟踪的插件目录)。
+
+## 复跑复核(票 28 会话独立复核,2026-09-17)
+
+原先的长跑 dev server 停掉后,用 `yarn dev` 全新启动再跑同一份走查:**20/20 通过**,A15 `widgetChrome: 0`。用 DOM 探针确认这不是「被隐藏」而是真的没有:`.cesium-viewer-toolbar` 存在但**零子元素**,`.cesium-viewer-animationContainer` 与 `.cesium-viewer-timelineContainer` **都不存在**——票面「默认控件全关」的结论成立,A15 的选择器并不过严。
+
+此前同一份代码上出现过一次 19/20(A15 红、`widgetChrome: 1`)。两次唯一的差别是服务实例:那次打的是**长时间运行的旧 dev server**,最可能是它给浏览器发到了更早的 `cesiumEngine.js` 模块(陈旧模块),不是代码或断言问题;事后无法回溯证实,故只作推断记录。教训:涉及引擎/懒加载模块的走查,**复跑前重启 dev server**(该条已写进走查脚本头部与 `map.md` 的 Notes)。
 
-已立案。归属与依赖由维护者确认(新增票 27,并作为票 03 的前置——三键相机契约必须落在真实引擎上)。
+结论:票面 20/20 与关闭结论成立,本复核只是另起一份干净服务上的可复现证据;票面正文无需改动。

+ 2 - 0
.scratch/cockpit-shell-panel-layout/issues/28-shell-fill-container.md

@@ -53,6 +53,8 @@ Labels: ready-for-agent
 
 回归:`t06-assets.mjs` 15/15、`t01-panel-catalog.mjs` 通过、走查 A12 仍绿。
 
+复核(2026-09-17,停掉长跑服务后 `yarn dev` 全新启动):`t28-fill-container.mjs` 仍 **7/7**,走查 **20/20**(含 A12;A15 那次红已查明是长跑 server 的陈旧模块,见票 27 的复跑复核一节)。
+
 ### 环境口径的由来
 
 本票原先的验收第 1 条写的是「`npm run pre.dev` 演示环境下」,属于**框错了环境**:维护者 2026-09-16 明确真实启动路径是 `yarn dev`、真实应用环境不使用 predev 启动服务。上面的证据全部落在真实路径,宿主套壳那条用等价复现覆盖;走查脚本头部与 `map.md` 的 Notes 也同步了这条口径,免得后人再去追 predev。

+ 1 - 0
.scratch/cockpit-shell-panel-layout/map.md

@@ -19,6 +19,7 @@ Labels: wayfinder:map
 - `issues/26`(resolved)是 dev server watcher 的 EBUSY 崩溃修复:忽略清单须同时覆盖 `.scratch` 面板原子写与 DSH 编辑审阅临时文件。
 - `issues/27`(resolved)是引擎接入票:装 `cesium` + 静态资源/`CESIUM_BASE_URL` + 服务层创建唯一 Viewer;**票 03 依赖它**(相机契约必须落在真实引擎上)。已关闭,验证证据与遗留(本工作区 `fes build` 因两个未跟踪插件缺依赖而跑不完)见票面。
 - **真实启动路径是 `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:` 行。