Переглянути джерело

docs(cockpit): 补充票35屋顶影像质量证据与蓝湖资产共识记录

- 新增屋顶影像质量复核产物与统计
- 新增蓝湖资产替换共识与 qiankun 宿主验收记录
ChenJZ 5 днів тому
батько
коміт
f56127cf66

+ 190 - 0
.scratch/cockpit-shell-panel-layout/verify/artifacts/t35/roof-imagery-quality/manual-review-gate.json

@@ -0,0 +1,190 @@
+{
+  "generatedAt": "2026-10-15",
+  "decision": "manual-review-required",
+  "thresholds": {
+    "manualReviewDarkFraction": 0.3,
+    "manualReviewSeamMeanRgbRms": 10.0,
+    "manualReviewSeamP95RgbRms": 30.0
+  },
+  "roofCropRows": [
+    {
+      "id": "cad-25612",
+      "darkFraction": 0.4018,
+      "darkRisk": true,
+      "sourceTileCount": 1
+    },
+    {
+      "id": "cad-25467",
+      "darkFraction": 0.4847,
+      "darkRisk": true,
+      "sourceTileCount": 2
+    },
+    {
+      "id": "cad-25968",
+      "darkFraction": 0.33973,
+      "darkRisk": true,
+      "sourceTileCount": 4
+    },
+    {
+      "id": "cad-25957",
+      "darkFraction": 0.35991,
+      "darkRisk": true,
+      "sourceTileCount": 2
+    }
+  ],
+  "seamRows": [
+    {
+      "tileA": "D:/GIS/GIS/yuanlingPic3/19/422931/305369.png",
+      "tileB": "D:/GIS/GIS/yuanlingPic3/19/422932/305369.png",
+      "orientation": "east",
+      "meanRgbRmsDelta": 9.52,
+      "p95RgbRmsDelta": 24.41,
+      "meanLumaDelta": 8.08,
+      "manualReviewRisk": false
+    },
+    {
+      "tileA": "D:/GIS/GIS/yuanlingPic3/19/422932/305369.png",
+      "tileB": "D:/GIS/GIS/yuanlingPic3/19/422933/305369.png",
+      "orientation": "east",
+      "meanRgbRmsDelta": 7.42,
+      "p95RgbRmsDelta": 19.32,
+      "meanLumaDelta": 6.77,
+      "manualReviewRisk": false
+    },
+    {
+      "tileA": "D:/GIS/GIS/yuanlingPic3/19/422933/305369.png",
+      "tileB": "D:/GIS/GIS/yuanlingPic3/19/422934/305369.png",
+      "orientation": "east",
+      "meanRgbRmsDelta": 9.57,
+      "p95RgbRmsDelta": 24.3,
+      "meanLumaDelta": 7.96,
+      "manualReviewRisk": false
+    },
+    {
+      "tileA": "D:/GIS/GIS/yuanlingPic3/19/422933/305370.png",
+      "tileB": "D:/GIS/GIS/yuanlingPic3/19/422934/305370.png",
+      "orientation": "east",
+      "meanRgbRmsDelta": 11.98,
+      "p95RgbRmsDelta": 32.85,
+      "meanLumaDelta": 9.31,
+      "manualReviewRisk": true
+    },
+    {
+      "tileA": "D:/GIS/GIS/yuanlingPic3/19/422939/305370.png",
+      "tileB": "D:/GIS/GIS/yuanlingPic3/19/422940/305370.png",
+      "orientation": "east",
+      "meanRgbRmsDelta": 13.19,
+      "p95RgbRmsDelta": 31.2,
+      "meanLumaDelta": 11.88,
+      "manualReviewRisk": true
+    },
+    {
+      "tileA": "D:/GIS/GIS/yuanlingPic3/19/422939/305371.png",
+      "tileB": "D:/GIS/GIS/yuanlingPic3/19/422940/305371.png",
+      "orientation": "east",
+      "meanRgbRmsDelta": 6.47,
+      "p95RgbRmsDelta": 14.2,
+      "meanLumaDelta": 4.62,
+      "manualReviewRisk": false
+    },
+    {
+      "tileA": "D:/GIS/GIS/yuanlingPic3/19/422939/305372.png",
+      "tileB": "D:/GIS/GIS/yuanlingPic3/19/422940/305372.png",
+      "orientation": "east",
+      "meanRgbRmsDelta": 8.48,
+      "p95RgbRmsDelta": 21.13,
+      "meanLumaDelta": 6.42,
+      "manualReviewRisk": false
+    },
+    {
+      "tileA": "D:/GIS/GIS/yuanlingPic3/19/422939/305373.png",
+      "tileB": "D:/GIS/GIS/yuanlingPic3/19/422940/305373.png",
+      "orientation": "east",
+      "meanRgbRmsDelta": 8.74,
+      "p95RgbRmsDelta": 21.02,
+      "meanLumaDelta": 7.71,
+      "manualReviewRisk": false
+    },
+    {
+      "tileA": "D:/GIS/GIS/yuanlingPic3/19/422940/305370.png",
+      "tileB": "D:/GIS/GIS/yuanlingPic3/19/422941/305370.png",
+      "orientation": "east",
+      "meanRgbRmsDelta": 6.65,
+      "p95RgbRmsDelta": 15.43,
+      "meanLumaDelta": 5.8,
+      "manualReviewRisk": false
+    },
+    {
+      "tileA": "D:/GIS/GIS/yuanlingPic3/19/422940/305371.png",
+      "tileB": "D:/GIS/GIS/yuanlingPic3/19/422941/305371.png",
+      "orientation": "east",
+      "meanRgbRmsDelta": 8.02,
+      "p95RgbRmsDelta": 23.07,
+      "meanLumaDelta": 6.89,
+      "manualReviewRisk": false
+    },
+    {
+      "tileA": "D:/GIS/GIS/yuanlingPic3/19/422940/305372.png",
+      "tileB": "D:/GIS/GIS/yuanlingPic3/19/422941/305372.png",
+      "orientation": "east",
+      "meanRgbRmsDelta": 7.85,
+      "p95RgbRmsDelta": 18.4,
+      "meanLumaDelta": 6.61,
+      "manualReviewRisk": false
+    },
+    {
+      "tileA": "D:/GIS/GIS/yuanlingPic3/19/422940/305373.png",
+      "tileB": "D:/GIS/GIS/yuanlingPic3/19/422941/305373.png",
+      "orientation": "east",
+      "meanRgbRmsDelta": 7.16,
+      "p95RgbRmsDelta": 17.03,
+      "meanLumaDelta": 5.93,
+      "manualReviewRisk": false
+    },
+    {
+      "tileA": "D:/GIS/GIS/yuanlingPic3/19/422941/305370.png",
+      "tileB": "D:/GIS/GIS/yuanlingPic3/19/422942/305370.png",
+      "orientation": "east",
+      "meanRgbRmsDelta": 7.33,
+      "p95RgbRmsDelta": 22.85,
+      "meanLumaDelta": 6.17,
+      "manualReviewRisk": false
+    },
+    {
+      "tileA": "D:/GIS/GIS/yuanlingPic3/19/422941/305371.png",
+      "tileB": "D:/GIS/GIS/yuanlingPic3/19/422942/305371.png",
+      "orientation": "east",
+      "meanRgbRmsDelta": 6.57,
+      "p95RgbRmsDelta": 18.88,
+      "meanLumaDelta": 5.03,
+      "manualReviewRisk": false
+    },
+    {
+      "tileA": "D:/GIS/GIS/yuanlingPic3/19/422941/305372.png",
+      "tileB": "D:/GIS/GIS/yuanlingPic3/19/422942/305372.png",
+      "orientation": "east",
+      "meanRgbRmsDelta": 5.65,
+      "p95RgbRmsDelta": 12.81,
+      "meanLumaDelta": 5.2,
+      "manualReviewRisk": false
+    },
+    {
+      "tileA": "D:/GIS/GIS/yuanlingPic3/19/422941/305373.png",
+      "tileB": "D:/GIS/GIS/yuanlingPic3/19/422942/305373.png",
+      "orientation": "east",
+      "meanRgbRmsDelta": 7.68,
+      "p95RgbRmsDelta": 20.5,
+      "meanLumaDelta": 6.75,
+      "manualReviewRisk": false
+    }
+  ],
+  "automaticIntegrationAllowed": false,
+  "reason": [
+    "Statistics cover crop rectangles, not only roof UV pixels.",
+    "Darkness can be shadow, roof material, occlusion or non-roof pixels.",
+    "Tile edge deltas can cross different land cover.",
+    "Only maintainer cockpit review can accept visual naturalness."
+  ],
+  "candidateUnchanged": true,
+  "runtimeConfigChanged": false
+}

+ 325 - 0
.scratch/cockpit-shell-panel-layout/verify/artifacts/t35/roof-imagery-quality/roof-imagery-statistics.json

@@ -0,0 +1,325 @@
+{
+  "generatedAt": "2026-10-15",
+  "method": "Pillow/Numpy raster statistics only; not roof segmentation, radiometric correction or facade evidence",
+  "roofCrops": [
+    {
+      "id": "cad-25612",
+      "roofCrop": {
+        "sizePx": [
+          126,
+          99,
+          "source channels RGB"
+        ],
+        "luminancePercentiles": {
+          "1": 21.36,
+          "5": 24.51,
+          "25": 32.88,
+          "50": 86.9,
+          "75": 149.34,
+          "95": 198.17,
+          "99": 210.66
+        },
+        "darkPixelFractionLumaUnder55": 0.4018,
+        "brightPixelFractionLumaOver210": 0.01098,
+        "saturationPercentiles": {
+          "50": 24.0,
+          "90": 73.0,
+          "99": 90.0
+        },
+        "edgeGradientP95": 16.86,
+        "rgbStddev": [
+          75.93000030517578,
+          58.20000076293945,
+          54.22999954223633
+        ]
+      },
+      "sha256": "bc8c1862a7542a3a663bd2a5f6d5f25645869ac6bbbad82a18c085773db719b1",
+      "sourceTileCount": 1,
+      "sourceTiles": [
+        {
+          "path": "D:/GIS/GIS/yuanlingPic3/19/422932/305368.png",
+          "sha256": "ee7e90754d6d956c6c6dadba1d56e4ee1318e66f6f8e0c6819cc43ccd112e943"
+        }
+      ]
+    },
+    {
+      "id": "cad-25467",
+      "roofCrop": {
+        "sizePx": [
+          159,
+          59,
+          "source channels RGB"
+        ],
+        "luminancePercentiles": {
+          "1": 28.58,
+          "5": 39.33,
+          "25": 48.74,
+          "50": 55.59,
+          "75": 133.96,
+          "95": 206.28,
+          "99": 216.64
+        },
+        "darkPixelFractionLumaUnder55": 0.4847,
+        "brightPixelFractionLumaOver210": 0.03507,
+        "saturationPercentiles": {
+          "50": 19.0,
+          "90": 34.0,
+          "99": 57.0
+        },
+        "edgeGradientP95": 16.0,
+        "rgbStddev": [
+          61.400001525878906,
+          56.68000030517578,
+          50.560001373291016
+        ]
+      },
+      "sha256": "6174a3ed5645b7deffe4c230568a8d33d44d41ad4e0a14f7981bff08df5e14fb",
+      "sourceTileCount": 2,
+      "sourceTiles": [
+        {
+          "path": "D:/GIS/GIS/yuanlingPic3/19/422931/305369.png",
+          "sha256": "468cda905c9a12adc2c260e5d85e6d4975551583c1e8479c41ced00787988ff3"
+        },
+        {
+          "path": "D:/GIS/GIS/yuanlingPic3/19/422932/305369.png",
+          "sha256": "bfcf37ac8471568e5bd485ba8cb468e6efe7ed45cdbbd26f79cb232e2ab34ed6"
+        }
+      ]
+    },
+    {
+      "id": "cad-25968",
+      "roofCrop": {
+        "sizePx": [
+          55,
+          127,
+          "source channels RGB"
+        ],
+        "luminancePercentiles": {
+          "1": 28.2,
+          "5": 32.63,
+          "25": 49.45,
+          "50": 108.19,
+          "75": 162.79,
+          "95": 192.79,
+          "99": 199.72
+        },
+        "darkPixelFractionLumaUnder55": 0.33973,
+        "brightPixelFractionLumaOver210": 0.0,
+        "saturationPercentiles": {
+          "50": 23.0,
+          "90": 86.0,
+          "99": 101.0
+        },
+        "edgeGradientP95": 16.42,
+        "rgbStddev": [
+          70.54000091552734,
+          56.560001373291016,
+          53.0
+        ]
+      },
+      "sha256": "00c3bd53275ee53b252e6d00bbf66f24eba0893a7dd5baefa75c970a9de9d22c",
+      "sourceTileCount": 4,
+      "sourceTiles": [
+        {
+          "path": "D:/GIS/GIS/yuanlingPic3/19/422933/305370.png",
+          "sha256": "0e87588fe0b1ebe2737b5847dba63ba6e38d003102149d3e0284d646c849b98c"
+        },
+        {
+          "path": "D:/GIS/GIS/yuanlingPic3/19/422934/305370.png",
+          "sha256": "cc0c49bef3a205f9962a082916e896f867892486ebf4514eeddc07c853b02a7f"
+        },
+        {
+          "path": "D:/GIS/GIS/yuanlingPic3/19/422933/305369.png",
+          "sha256": "6db77e1e6941eb681411e2e9c07b5b84f2276c87bbea8c04d9c7bea6dae40d01"
+        },
+        {
+          "path": "D:/GIS/GIS/yuanlingPic3/19/422934/305369.png",
+          "sha256": "ce3ff787bf5cb5336cd18db253d5412d1d04799fe4c385ae8ed43a09472ca8cb"
+        }
+      ]
+    },
+    {
+      "id": "cad-25957",
+      "roofCrop": {
+        "sizePx": [
+          55,
+          127,
+          "source channels RGB"
+        ],
+        "luminancePercentiles": {
+          "1": 31.33,
+          "5": 35.61,
+          "25": 47.09,
+          "50": 81.11,
+          "75": 159.24,
+          "95": 190.24,
+          "99": 198.31
+        },
+        "darkPixelFractionLumaUnder55": 0.35991,
+        "brightPixelFractionLumaOver210": 0.00157,
+        "saturationPercentiles": {
+          "50": 25.0,
+          "90": 84.0,
+          "99": 100.0
+        },
+        "edgeGradientP95": 16.29,
+        "rgbStddev": [
+          68.11000061035156,
+          56.66999816894531,
+          50.959999084472656
+        ]
+      },
+      "sha256": "2cd2dc727a2995384178edb84cc84392d80e578d775766622ffebf9d94834cb1",
+      "sourceTileCount": 2,
+      "sourceTiles": [
+        {
+          "path": "D:/GIS/GIS/yuanlingPic3/19/422933/305370.png",
+          "sha256": "0e87588fe0b1ebe2737b5847dba63ba6e38d003102149d3e0284d646c849b98c"
+        },
+        {
+          "path": "D:/GIS/GIS/yuanlingPic3/19/422934/305370.png",
+          "sha256": "cc0c49bef3a205f9962a082916e896f867892486ebf4514eeddc07c853b02a7f"
+        }
+      ]
+    }
+  ],
+  "sourceTileCount": 25,
+  "adjacentTileSeams": [
+    {
+      "tileA": "D:/GIS/GIS/yuanlingPic3/19/422931/305369.png",
+      "tileB": "D:/GIS/GIS/yuanlingPic3/19/422932/305369.png",
+      "orientation": "east",
+      "meanRgbRmsDelta": 9.52,
+      "p95RgbRmsDelta": 24.41,
+      "meanLumaDelta": 8.08
+    },
+    {
+      "tileA": "D:/GIS/GIS/yuanlingPic3/19/422932/305369.png",
+      "tileB": "D:/GIS/GIS/yuanlingPic3/19/422933/305369.png",
+      "orientation": "east",
+      "meanRgbRmsDelta": 7.42,
+      "p95RgbRmsDelta": 19.32,
+      "meanLumaDelta": 6.77
+    },
+    {
+      "tileA": "D:/GIS/GIS/yuanlingPic3/19/422933/305369.png",
+      "tileB": "D:/GIS/GIS/yuanlingPic3/19/422934/305369.png",
+      "orientation": "east",
+      "meanRgbRmsDelta": 9.57,
+      "p95RgbRmsDelta": 24.3,
+      "meanLumaDelta": 7.96
+    },
+    {
+      "tileA": "D:/GIS/GIS/yuanlingPic3/19/422933/305370.png",
+      "tileB": "D:/GIS/GIS/yuanlingPic3/19/422934/305370.png",
+      "orientation": "east",
+      "meanRgbRmsDelta": 11.98,
+      "p95RgbRmsDelta": 32.85,
+      "meanLumaDelta": 9.31
+    },
+    {
+      "tileA": "D:/GIS/GIS/yuanlingPic3/19/422939/305370.png",
+      "tileB": "D:/GIS/GIS/yuanlingPic3/19/422940/305370.png",
+      "orientation": "east",
+      "meanRgbRmsDelta": 13.19,
+      "p95RgbRmsDelta": 31.2,
+      "meanLumaDelta": 11.88
+    },
+    {
+      "tileA": "D:/GIS/GIS/yuanlingPic3/19/422939/305371.png",
+      "tileB": "D:/GIS/GIS/yuanlingPic3/19/422940/305371.png",
+      "orientation": "east",
+      "meanRgbRmsDelta": 6.47,
+      "p95RgbRmsDelta": 14.2,
+      "meanLumaDelta": 4.62
+    },
+    {
+      "tileA": "D:/GIS/GIS/yuanlingPic3/19/422939/305372.png",
+      "tileB": "D:/GIS/GIS/yuanlingPic3/19/422940/305372.png",
+      "orientation": "east",
+      "meanRgbRmsDelta": 8.48,
+      "p95RgbRmsDelta": 21.13,
+      "meanLumaDelta": 6.42
+    },
+    {
+      "tileA": "D:/GIS/GIS/yuanlingPic3/19/422939/305373.png",
+      "tileB": "D:/GIS/GIS/yuanlingPic3/19/422940/305373.png",
+      "orientation": "east",
+      "meanRgbRmsDelta": 8.74,
+      "p95RgbRmsDelta": 21.02,
+      "meanLumaDelta": 7.71
+    },
+    {
+      "tileA": "D:/GIS/GIS/yuanlingPic3/19/422940/305370.png",
+      "tileB": "D:/GIS/GIS/yuanlingPic3/19/422941/305370.png",
+      "orientation": "east",
+      "meanRgbRmsDelta": 6.65,
+      "p95RgbRmsDelta": 15.43,
+      "meanLumaDelta": 5.8
+    },
+    {
+      "tileA": "D:/GIS/GIS/yuanlingPic3/19/422940/305371.png",
+      "tileB": "D:/GIS/GIS/yuanlingPic3/19/422941/305371.png",
+      "orientation": "east",
+      "meanRgbRmsDelta": 8.02,
+      "p95RgbRmsDelta": 23.07,
+      "meanLumaDelta": 6.89
+    },
+    {
+      "tileA": "D:/GIS/GIS/yuanlingPic3/19/422940/305372.png",
+      "tileB": "D:/GIS/GIS/yuanlingPic3/19/422941/305372.png",
+      "orientation": "east",
+      "meanRgbRmsDelta": 7.85,
+      "p95RgbRmsDelta": 18.4,
+      "meanLumaDelta": 6.61
+    },
+    {
+      "tileA": "D:/GIS/GIS/yuanlingPic3/19/422940/305373.png",
+      "tileB": "D:/GIS/GIS/yuanlingPic3/19/422941/305373.png",
+      "orientation": "east",
+      "meanRgbRmsDelta": 7.16,
+      "p95RgbRmsDelta": 17.03,
+      "meanLumaDelta": 5.93
+    },
+    {
+      "tileA": "D:/GIS/GIS/yuanlingPic3/19/422941/305370.png",
+      "tileB": "D:/GIS/GIS/yuanlingPic3/19/422942/305370.png",
+      "orientation": "east",
+      "meanRgbRmsDelta": 7.33,
+      "p95RgbRmsDelta": 22.85,
+      "meanLumaDelta": 6.17
+    },
+    {
+      "tileA": "D:/GIS/GIS/yuanlingPic3/19/422941/305371.png",
+      "tileB": "D:/GIS/GIS/yuanlingPic3/19/422942/305371.png",
+      "orientation": "east",
+      "meanRgbRmsDelta": 6.57,
+      "p95RgbRmsDelta": 18.88,
+      "meanLumaDelta": 5.03
+    },
+    {
+      "tileA": "D:/GIS/GIS/yuanlingPic3/19/422941/305372.png",
+      "tileB": "D:/GIS/GIS/yuanlingPic3/19/422942/305372.png",
+      "orientation": "east",
+      "meanRgbRmsDelta": 5.65,
+      "p95RgbRmsDelta": 12.81,
+      "meanLumaDelta": 5.2
+    },
+    {
+      "tileA": "D:/GIS/GIS/yuanlingPic3/19/422941/305373.png",
+      "tileB": "D:/GIS/GIS/yuanlingPic3/19/422942/305373.png",
+      "orientation": "east",
+      "meanRgbRmsDelta": 7.68,
+      "p95RgbRmsDelta": 20.5,
+      "meanLumaDelta": 6.75
+    }
+  ],
+  "interpretationLimits": [
+    "Dark-pixel share and luminance spread combine cast shadows, roof material, capture mosaics and building occlusion.",
+    "Adjacent tile edge mismatch is a proxy and may cross different land cover; it is not a seam detector.",
+    "The 512/205px GLB atlases are roof bounding rectangles; only the roof mesh UV subset is rendered.",
+    "No source image is altered; no geometric claim follows from these statistics."
+  ],
+  "runtimeConfigChanged": false,
+  "runtimeRendered": false
+}

+ 16 - 0
.scratch/cockpit-shell-panel-layout/verify/artifacts/t35/round35-roof-imagery-statistics.md

@@ -0,0 +1,16 @@
+# Round 35:屋顶影像色差统计
+
+新增[roof-imagery-statistics.json](<roof-imagery-quality/roof-imagery-statistics.json>),仅对本地z19源瓦片和四栋屋顶裁片做RGB/亮度统计,没有修改图像或模型。
+
+屋顶裁片统计:
+
+- `cad-25612`:暗像素(亮度<55)40.18%,中位亮度86.90;1个源瓦片;
+- `cad-25467`:暗像素48.47%,中位亮度55.59;2个源瓦片;
+- `cad-25968`:暗像素33.97%,中位亮度108.19;4个源瓦片;
+- `cad-25957`:暗像素35.99%,中位亮度81.11;2个源瓦片。
+
+四栋裁片暗像素比例约34%–48%,说明屋顶外观受大面积暗部影响明显;但这个统计不能区分阴影、屋顶材质和建筑遮挡,也不能称为摄影测量材质。
+
+从25个涉及源瓦片中发现16组相邻东向边界,RGB均方根边界差约5.65–13.19。它只能作为潜在拼接/地表变化风险指标,因为相邻瓦片可能跨越不同地物,不能直接判定瓦片拼接错误。
+
+结论:高清候选解决了64px atlas导致的像素密度问题,但没有消除源影像自身的色块/阴影风险;候选继续保持未接入。维护者在正式驾驶舱需重点判断屋顶是否自然,而不是根据统计自动接受。

+ 165 - 0
.scratch/lanhu-cockpit-asset-replacement/CONSENSUS.md

@@ -0,0 +1,165 @@
+# 蓝湖正式素材替换 · 项目共识与经验留档
+
+来源:`lanhu-cockpit-asset-replacement` 地图全部 20 张子票(8 决策 + 5 规格 + 5 执行 + 盘点/迁移各 1)的讨论与实施沉淀。
+性质:**本项目「设计素材接入」类任务的单一经验来源(single source of truth)**。后续同类任务(换设计稿、接新素材、改视觉体系)先读本文,不再逐轮重新发明。
+生效范围:城市生命线驾驶舱 `/cockpit` 的 2D UI 素材、图标、位置符号、字体、装饰。
+
+配套文档(各自仍是其领域的单一事实来源):
+- [UI 设计及图标素材使用共识](icon-usage-consensus.md) —— 图标/切图的目录、命名、染色、归档细则。
+- [终稿验收报告](final-acceptance-report.md) —— 本次验收证据与未闭合项。
+
+---
+
+## 一、总原则(决策类经验)
+
+### 1. 现状优先,默认归视觉
+设计稿差异**默认归入素材替换**,不无谓升级为结构决策;仅当差异触及**状态语义 / 信息层级 / 交互契约 / 数据契约**时才升级。
+- 信息层级一律**以现有实现为准**;数据内容一律**以实际后端为准**,设计稿数据仅作参考展示。
+- 升级必须**另立决策票**并由维护者裁定,不由实现者口头处理或直接按设计稿做。
+
+### 2. 结构边界词表(可判定)
+| 属视觉(可改) | 属结构(须升级) |
+| --- | --- |
+| 两栏宽、面板高、边距、间距、字号 | 面板数量、面板顺序、列排列、两栏改单栏 |
+| 图标/颜色/字体/纹理/形状外观 | 状态集合与触发时机、图层显隐、拾取、聚合、状态语义 |
+| 数据展示格式(时间/千分位/小数位) | 数据字段、单位、口径 |
+| 视觉反馈层(光效、动效表现) | 相机交互、选择态、定位/拾取/联动契约 |
+
+### 3. 设计稿「没画」≠「该删」
+设计稿未覆盖的现有 UI **维持现状**并标记「无设计稿依据」,不因蓝湖没画就删除或臆造样式。
+
+### 4. 色值只有一个来源
+色值只在 `styles/tokens.css` 声明一份;素材若自带彩色即成第二套色源。**实测教训**:素材成品色与项目语义色无一相同(素材 Red `#E80000` vs `--lv1 #ff4d5e`),因此只采用**纯白可着色母版 + 运行时注入状态色**。
+
+### 5. 临时资源必须可追溯、不得充当正式验收
+临时素材必须带标记(manifest `status: temporary`);**验收不得以临时资源作为正式视觉通过**;缺口清单必须列出仍未替换的临时项。
+
+---
+
+## 二、资产与运行时(工程类经验)
+
+### 1. manifest 是唯一权威入口
+组件/地图服务/设备资产层**只消费语义 key**,不直接引用物理路径。条目须含:语义 key、路径、来源、格式/尺寸、发布版本、内容 hash、状态(formal/temporary)、适用范围、核心标记、fallback key。
+- 生产默认**拒绝远程资源**;远程例外必须登记来源/域名/安全/缓存/超时/fallback。
+- 语义 key 稳定;物理文件名可带 hash。
+- **manifest 与资源原子发布**;核心资源组失败整包回滚,普通资源按条目 fallback。
+
+### 2. 路径必须跟随应用基址
+所有资源引用走 `import.meta.env.BASE_URL`(或宿主注入基址),**禁止硬编码 `/cockpit/...`**,以兼容相对 `publicPath` 与 qiankun 注入路径。
+
+### 3. 单一接缝优于分散实现
+本图建立的接缝(后续复用,不要另起):
+- **统一资产解析层**:manifest 校验 + 语义 key 解析 + fallback + 诊断。
+- **位置符号合成计划层**(`domain/markerPlan.js`):母版归属 / 内层图标几何 / 尺寸 / 锚点 / 着色 / 降饱和,纯数据产出,渲染媒介可换。
+
+### 4. 状态语义不可被素材替换改变
+点位状态(六态)与设备状态(五态)是**两张平行表**,色键可同、语义不混;认不出的状态如实落「状态未发布」,**不猜成正常**。颜色不是唯一线索(形状 + 文字并存)。
+
+---
+
+## 三、踩过的坑(每条都是真实事故)
+
+| 坑 | 现象 | 纪律 |
+| --- | --- | --- |
+| **manifest 被并发脚本整文件重写** | 另一会话跑 `build-manifest.mjs` 把 `fonts` 块抹掉(26409→15538 字节),字体静默失效 | 脚本重写前**保留未知顶层块**;`build-manifest.mjs` 已加**跨批防覆盖闸门**(未登记 entries 存在即中止) |
+| **Vite 缓存 `import` 的 manifest 转译结果** | 外部脚本改写 manifest 后页面拿到旧模块,字体静默不生效 | 门禁如实报 `fonts-unavailable` 使其可观察;改完 manifest **必须硬刷新** |
+| **HMR 模块图陈旧** | HMR 只热更边界组件、不重载其传递依赖的 manifest,浏览器仍跑旧 manifest | 同上;**判定效果前先硬刷新** |
+| **「字体请求为 0」被误判为正常** | Web 字体排在回退链首位,页面文本必用到;请求为 0 说明字体没生效 | **「字体请求为 0」必须判 FAIL** |
+| **窄视口响应式缺陷** | `--u` 触底 1px 后顶栏总宽超视口,grid 挤压菜单列 → 按钮文字换行被定高裁成单字 | 响应式回归**必须覆盖窄视口**(此前十轮只在大视口验证) |
+| **图片盒子被整图拉伸** | 装饰线可见段按百分比映射视口,挪图片盒子永远无效 | 用**裁切窗口**(background-size + position)而非挪盒子 |
+| **切图撑高 grid 行** | 标题切图作为 grid 项内容把顶栏行撑高,「垂直居中」基准被改 | 定高容器 + 绝对定位让溢出不占布局 |
+| **`mask-image` 不收层语法** | 四角切角染色自误 | 色值收口时验证 mask 语法 |
+| **单点验证代替全域验证** | 同一代码在不同视口/环境表现不同(predev 有 54px 页头) | 明确验收环境;**predev 差异不等于产品缺陷** |
+| **锚点迁移留下假绿** | 断言按旧口径建立,改渲染后仍绿 | 锚点口径变更必须**同步更新断言并以对抗方式确认能失败** |
+
+---
+
+## 四、字体与授权(合规类经验)
+
+### 1. 授权证据确证制
+- 内置字体需**书面/电子授权证据**,明确覆盖 Web 嵌入与商用,注明名称与版本。
+- **「免费下载」≠「允许 Web 嵌入与商用」**;**系统自带字体只能作回退栈、不得打包**。
+- 白名单只放行**确证带开源许可**的字族;名字暗示免费但无证据者**按无证据处理**,除非归档上游许可证文本。
+
+### 2. 实测教训:整目录不可放行
+维护者提供的字体目录 `E:\_Design\字体` **零授权文件**,且含明确 `All rights reserved` 的字库(`Modernia`、`Furore`)与多款公认商用收费字库。**不得以目录为单位放行**——必须逐族取证。
+
+### 3. 体积与子集
+中文开源字体单字重约 15–16 MB;**必须子集化或限定字重档数**,且**必须保留系统回退链**(动态数据可能含子集外汉字,否则出豆腐块)。
+
+### 4. 能力边界(不可越界)
+自动化**只能校验「是否携带许可证据」**,**不声称能判定授权合法性**——授权合法性是法律事实,由证据文件与责任人负责。
+
+### 5. 缺口分级(仅三类阻断发布)
+**阻断**:① 核心资源组(品牌标识 / 点位设备状态形状语义 / 状态色语义)不可用;② 视觉会误导业务语义;③ 法规/授权风险。
+**允许带回退交付**:其余缺口,条件是登记缺口清单 + 临时资源带标记 + 验收不把临时资源当正式视觉。
+
+---
+
+## 五、实施与验收(流程类经验)
+
+### 1. 批次按资产类别切,部分可并行
+品牌/图标、面板 token、位置符号、字体、装饰五批;**位置符号依赖 token**(外层母版着色),**装饰最后**;品牌/图标、token、字体可并行。
+
+### 2. 每批进入条件
+素材已入库且**过 manifest 校验**(存在、hash、fallback 齐全)+ 该批有**可执行验收清单** + 已用差异判断表分类。
+
+### 3. 双轨验收
+实现者自测(纯逻辑断言 + 宿主 `data-*` 读数 + 结构回归)+ 维护者走查真实 `/cockpit` 并签收。**终稿验收是最后一道闸,不是第一道。**
+
+### 4. 证据单一来源
+- 验收证据写进**执行票本身**(走查单 + 截图 + 读数),不另建「批验收记录」第二来源。
+- 偏差只写进**票 06 缺口清单**,不另建「偏差清单」。
+
+### 5. 缺陷分级
+P0 阻断(复用上述三类判据)/ P1 高(1280px 不可读、位置符号不可辨、锚点错位)/ P2 中(观感、性能)/ P3 低(非核心装饰,入缺口清单后可发布)。
+
+### 6. 临时素材关闭判据
+核心资源组**全部换正式素材** + 缺口清单**无「临时素材仍在使用」条目**;非核心装饰留临时项时在缺口清单**显式登记「未替换」**。最终以**逐项关闭清单**留痕并由维护者签收。
+
+### 7. 1280px 是硬承诺
+最小 1280×720 下:文字可读、按钮可操作、面板不重叠优先;左右栏保持宽度,面板内**纵向滚动**,支持卷轴式展开/折叠,装饰可缩放/隐藏/降级。
+
+---
+
+## 六、验收环境事实(本仓现状)
+
+| 环境 | 地址/状态 | 说明 |
+| --- | --- | --- |
+| 真实 `/cockpit` | `http://127.0.0.1:8000/#/cockpit` | **唯一开发/验收入口**,所有会话统一使用,不另起端口 |
+| 后端只读接口 | `http://192.168.110.96:8301/api/cockpit/v1/**` | 免鉴权;`dataSource: real` |
+| qiankun 宿主 | **本仓不存在宿主工程** | 仅被嵌侧适配代码 `src/.fes/plugin-qiankun/`;宿主验收需临时自建(见下) |
+| predev 预演站 | `npm run pre.dev` | 有 54px 页头,**布局差异不等于产品缺陷** |
+
+### qiankun 宿主验收的已知事实(供后续复用)
+自建最小宿主可验证「加载 + 挂载 + 基址注入」:
+- 被嵌侧 appName 为 **`TvT.js`**(dev HTML 实测);生命周期经 `window.moudleQiankunAppLifeCycles['TvT.js']` 暴露。
+- 必须传 `props.url`(如 `/cockpit`),否则被嵌侧不切 memory history、`/cockpit` 不渲染。
+- 实测宿主内 `__INJECTED_PUBLIC_PATH_BY_QIANKUN__` = `http://127.0.0.1:8000/`,**基址注入生效**。
+- **已知阻塞**:被嵌侧 `src/.fes/plugin-qiankun/micro/runtime.js:24` 解构 `{ router, history }` 并在 `:26` 调 `history.push(...)`,但 FES 4 的 `onRouterCreated` 契约只保证 `router`(同签名的 `plugin-access` 只解构 `{ router }`)→ 宿主内抛 `Cannot read properties of undefined (reading 'push')`,`/cockpit` 落在宿主默认页。该文件 `git status` 无本地改动,属**宿主集成既有问题**,不属素材替换范围。
+- 该阻塞若要修,属「宿主集成」独立事项;**素材替换侧的资源路径解析不受影响**(`resolveCesiumBaseUrl()` 对注入基址的三条断言已全绿)。
+
+---
+
+## 七、可复用的检查清单(新任务照抄)
+
+**开工前**
+- [ ] 差异判断表分类:哪些归视觉、哪些须升级
+- [ ] 素材是否入库并过 manifest 校验(存在/hash/fallback)
+- [ ] 是否有可执行验收清单
+
+**实施中**
+- [ ] 色值只在 `tokens.css`;无散落物理路径;无死资源
+- [ ] 资源走 `BASE_URL`;不硬编码绝对路径
+- [ ] 结构回归(四模式 / 16 panelId / 状态词表冻结)
+
+**收口前**
+- [ ] 纯逻辑断言 + 宿主读数 + 真实 `/cockpit` 走查
+- [ ] 1280×720 逐面板检查(含窄视口)
+- [ ] 偏差已登记缺口清单(唯一来源)
+- [ ] 临时资源状态明确(已替换/已删除/登记未替换)
+- [ ] 结构口径变更时,旧断言已同步更新且**确认能失败**
+
+---
+
+*本文由 `lanhu-cockpit-asset-replacement` 地图收口时提取。后续同类任务请以本文为起点;若与本文冲突,以更新的决策票为准并回写本文。*

+ 113 - 0
.scratch/lanhu-cockpit-asset-replacement/qiankun-host-acceptance/index.html

@@ -0,0 +1,113 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+  <meta charset="UTF-8" />
+  <title>qiankun 宿主(终稿验收临时宿主)</title>
+  <style>
+    html, body { margin: 0; padding: 0; height: 100%; background: #01060d; color: #e6f5ff;
+      font-family: "Microsoft YaHei UI", "Microsoft YaHei", sans-serif; }
+    #host-bar { height: 28px; line-height: 28px; padding: 0 10px; font-size: 12px;
+      background: #0a2740; border-bottom: 1px solid #1b93cf; display: flex; gap: 16px; }
+    #host-bar b { color: #4fd8ff; }
+    #micro-container { position: absolute; top: 28px; left: 0; right: 0; bottom: 0; overflow: hidden; }
+    #probe { position: absolute; right: 8px; top: 34px; z-index: 99999; font-size: 11px;
+      background: rgba(2,10,20,.9); border: 1px solid #1b93cf; padding: 6px 8px; max-width: 420px;
+      white-space: pre-wrap; line-height: 1.5; }
+  </style>
+</head>
+<body>
+  <div id="host-bar">
+    <span>qiankun 宿主验收(临时)</span>
+    <span>入口:<b>http://127.0.0.1:8000/</b></span>
+    <span>appName:<b>TvT.js</b></span>
+  </div>
+  <div id="micro-container"></div>
+  <div id="probe">probe: waiting…</div>
+
+  <script src="./qiankun.umd.min.js"></script>
+  <script>
+    (function () {
+      var log = [];
+      function rec(k, v) { log.push(k + '=' + v); document.getElementById('probe').textContent = log.join('\n'); }
+      window.__HOST_EVENTS__ = log;
+      window.__HOST_REQUESTS__ = [];
+      window.__HOST_ERRORS__ = [];
+
+      // 记录被嵌应用发出的资源请求(含字体/图标),用于「宿主内资源解析」验收
+      var origFetch = window.fetch;
+      window.fetch = function (input, init) {
+        var url = (typeof input === 'string') ? input : (input && input.url);
+        window.__HOST_REQUESTS__.push(url);
+        return origFetch.apply(this, arguments);
+      };
+      window.addEventListener('error', function (e) {
+        window.__HOST_ERRORS__.push(String(e.message || e));
+      }, true);
+      window.addEventListener('unhandledrejection', function (e) {
+        window.__HOST_ERRORS__.push('unhandledrejection: ' + String(e.reason));
+      });
+
+      rec('host', 'qiankun ' + (window.qiankun ? 'loaded' : 'MISSING'));
+      rec('powered', String(!!window.__POWERED_BY_QIANKUN__));
+
+      var ENTRY = 'http://127.0.0.1:8000/';
+      var APP = 'TvT.js';
+
+      try {
+        qiankun.registerMicroApps([{
+          name: APP,
+          entry: ENTRY,
+          container: '#micro-container',
+          activeRule: function () { return true; },
+          props: {
+            // 被嵌侧 lifecycle.js 只在收到 url 时切 memory history 并 push 该路由;
+            // 不传则停在宿主默认页,/cockpit 不会渲染。
+            url: '/cockpit',
+            onRouterInit: function (router) {
+              rec('routerInit', String(!!router));
+            }
+          }
+        }], {
+          beforeLoad: [function (app) { rec('beforeLoad', app.name); }],
+          beforeMount: [function (app) { rec('beforeMount', app.name); }],
+          afterMount: [function (app) {
+            rec('afterMount', app.name);
+            rec('poweredAfterMount', String(!!window.__POWERED_BY_QIANKUN__));
+            rec('injectedBase', String(window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__ || '(none)'));
+          }],
+          afterUnmount: [function (app) { rec('afterUnmount', app.name); }]
+        });
+
+        qiankun.start({ singular: true, sandbox: { experimentalStyleIsolation: false } })
+          .then(function () { rec('start', 'ok'); })
+          .catch(function (err) { rec('startError', String(err && err.message || err)); });
+      } catch (err) {
+        rec('registerError', String(err && err.message || err));
+      }
+
+      // 给外部读数的稳定窗口(含字体与图标请求统计)
+      window.__HOST_REPORT__ = function () {
+        var reqs = window.__HOST_REQUESTS__;
+        var fonts = reqs.filter(function (u) { return /\.woff2(\?|$)/.test(u); });
+        var icons = reqs.filter(function (u) { return /\/cockpit\/assets\/icons\//.test(u); });
+        return {
+          events: window.__HOST_EVENTS__.slice(),
+          powered: !!window.__POWERED_BY_QIANKUN__,
+          injectedBase: window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__ || null,
+          fontRequests: fonts,
+          iconRequests: icons,
+          totalRequests: reqs.length,
+          errors: window.__HOST_ERRORS__.slice(),
+          microRootHtmlLen: (function () {
+            var el = document.querySelector('#micro-container');
+            return el ? el.innerHTML.length : -1;
+          })(),
+          cockpitPresent: !!document.querySelector('#micro-container .cockpit-shell'),
+          panelCount: document.querySelectorAll('#micro-container [data-panel-id]').length,
+          markerCount: document.querySelectorAll('#micro-container [data-marker-overlay-key]').length
+        };
+      };
+    })();
+  </script>
+</body>
+</html>

Різницю між файлами не показано, бо вона завелика
+ 0 - 0
.scratch/lanhu-cockpit-asset-replacement/qiankun-host-acceptance/qiankun.umd.min.js


Деякі файли не було показано, через те що забагато файлів було змінено