فهرست منبع

票22:登记本轮修复、离线回归证据与真机复验待办

Kazerin 1 هفته پیش
والد
کامیت
fe3a095c28
1فایلهای تغییر یافته به همراه68 افزوده شده و 15 حذف شده
  1. 68 15
      .scratch/cockpit-real-data-wayfinding/issues/22-22.md

+ 68 - 15
.scratch/cockpit-real-data-wayfinding/issues/22-22.md

@@ -1,32 +1,85 @@
 # 22: 前端执行票 —— 真实响应信封消费与业务错误映射修复
 
 Type: task
-Status: resolved-as-not-a-defect
-Label: ready-for-human
+Status: claimed
+Assignee: codex
+Label: ready-for-verification
 Category: frontend-execution
 
-## 结论更正(2026-10-10 15:58,本票前提被推翻)
+## 进度
 
-**本票最初的前提「前端把真实数据读成空、把业务失败读成成功」不成立。经复核应予撤销。**
+**状态:修复已实施并通过离线回归;真机复验(待验证项 1、2)未执行,本票保持开启。**
 
-## 复核方法与证据
+Codex 按本地 tracker 的 `Status: claimed` 约定认领(deck 的 assignees 写入读回失败,以 `Assignee:` 字段为准)。核心信封解包由并行改动 `ead39442` 提供,非本票实施;本轮**新实施**的是同一条业务错误映射链上的两处白名单继承键缺口,提交 `86db4d4`。
 
-1. **独立进程复现**:`t18-envelope-probe.mjs` 单独调用前端适配层,`fetchBaseArchives` → `state=success`、`facilityCount=1006`;`fetchMapSnapshot` → `state=success`、`total=13`、`snapshotAt` 正常。业务载荷键齐全(`totals`/`industryDistribution`/`districtDistribution` 均存在)。
-2. **抗顺序复现**:`t18-envelope-repro.mjs` 依次在「首次调用」「一次 raw 请求后」「后端 `code=500` 之后」「20 次同类请求之后」四种前置状态下调用,五次全部返回 `success/1006`。未复现任何读空或误判。
-3. **浏览器真实取证**:`DATA_SOURCE.REAL` 下 `/infrastructure/base-archives` 请求 HTTP 200、载荷 1820 字节真实数据,面板渲染出**真实读数**(设施总数 1006、管线里程 18.81943、供水 1004/排水 2、片区「未分区」1006、建成年限 1002/4)。网络层与适配层均正常。
+## 状态:缺陷真实存在,已被并行改动修复,待验证
 
-## 结论
+**本票一度被我错误撤销,此处更正。**
 
-前端**确实**正确地把后端 `code=500` 判为失败分区(界面显示「数据加载失败 · 数据源暂不可用 · 该面板加载失败(其它分区不受影响)」),**没有**把失败显示成成功,也**没有**把真实数据显示成空。此前列为失败的三项探针检查(`frontend-envelope-archive`、`frontend-envelope-map`、`frontend-business-error-not-success`)是**探针脚本自身的缺陷**,不是产品缺陷。
+### 缺陷(真实存在,2026-10-10 10:25 实测证实)
 
-## 仍然成立的部分(保留,非本票原范围)
+`cockpitApi.js` 原实现直接 `return { data: payload.data }` —— 而 `payload.data` 是 `readJsonPayload` 返回的**整个 AjaxResult 信封** `{code, msg, data}`,业务载荷从未被解包。后果:
 
-面板失败的真实成因是后端业务 `code=500`,归 [票 19](19-19-early-warning-warning-name.md)(部署时序错位)。但有一项**独立的真实前端缺陷**在浏览器取证中新发现,需另立票:运行监测-监测点台账在真实空态下显示「已列出 0 个点位 · 本端点序列共 **undefined** 条」——分母取到了 `undefined` 并直接渲染,属真实展示缺陷。
+1. **真实数据被读成空**:`fetchBaseArchives` 对真实 `/infrastructure/base-archives`(`facilityCount=1006`)返回 `state=empty`;`fetchMapSnapshot` 对真实 `/map-snapshot`(`total=13`)返回 `total=null`、`snapshotAt=''`。
+2. **业务错误被读成成功**:`/events/current?mode=ALARM` 业务 `code=500`,前端分区判为 `success` —— 故障被伪装成正常空态。
 
-## 状态
+实测证据:`.scratch/cockpit-real-data-wayfinding/verify/artifacts/t18/live-audit-results.json`(写入时间 10:25),三项检查 `frontend-envelope-archive`、`frontend-envelope-map`、`frontend-business-error-not-success` 均为 `failed`。
 
-本票**关闭为「前提不成立」**,不作为待修缺陷。真实待修项已分别落在 19(后端部署)、20(管网映射)、23(分页与八桶契约)、24(设施管线图层)、25(署名)、26(抖动诊断);台账 `undefined` 展示缺陷另立票跟踪。
+### 修复现状(并行改动,非本票实施)
+
+工作区已存在对应修复(`src/modules/cockpit/services/cockpitApi.js`,文件修改时间 **14:38:36**):新增信封拆解分支——若载荷含数值型 `code`,则 `code !== 200` 判失败(401/403 映射 `UNAUTHORIZED`,其余 `UNAVAILABLE`),缺 `data` 字段判 `BAD_PAYLOAD`,`code === 200` 才解包 `body.data`。
+
+该修复于 14:38 落地,**晚于**上述 10:25 的失败实测,**早于**我 15:50 的复核。复核因此在已修复代码上进行并返回正常。
+
+### 我的错误(记录在案)
+
+我 15:58 撤销本票时,用**修复后**的代码复核得到「适配层正常」,据此判定缺陷「不成立」并关闭票据。这是推理错误:**修复后的通过不能否定修复前已证实的缺陷**。正确处置是标记为「已修复待验证」,而非撤销。相关术语纪律:验证缺陷必须确认**被测代码版本**与**复现时版本**一致。
+
+## 本轮实施(Codex,2026-10-12):业务错误映射的白名单继承键缺口
+
+核验已有修复时发现**同类缺陷的第二、第三处**,都在本票“业务错误映射”范围内:
+
+`readBusinessReason`(`cockpitApi.js`)用普通对象下标查 `BACKEND_ERROR_CATEGORY`。白名单只有一个条目,但下标查找会**命中 `Object.prototype` 的继承键**——后端随便回一个 `category: "toString"`,失败原因就成了函数而不是契约值。与文件里既有的注释纪律(「只认白名单里这一个取值……不让后端塞任意原因进结果」)直接冲突。
+
+对抗式自查时用 `node` 探针确认第二处同源缺陷:`sectionStateFromBackend('toString')` 与 `sectionStateFromFreshness('toString')` 返回的是**函数**,会原样漏进 `data-section-state`。
+
+修复(提交 `86db4d4`,两个文件共 5 行):三处改为只认白名单**自有键**。这是最小改动,不新增抽象,沿用本文件既有的 `Object.prototype.hasOwnProperty.call` 写法。
+
+离线回归:新增 [`verify/t22-envelope-frontend.mjs`](../verify/t22-envelope-frontend.mjs),不联网、注入 `fetchImpl`,覆盖三项真实失败检查对应的**同一批领域出口**(`fetchBaseArchives` / `fetchMapSnapshot` / `fetchCurrentEvents`)、HTTP 与业务两级失败、mock 分支、裸载荷、白名单继承键、后端分区状态映射 —— **19/19 通过**(修复前 5 项继承键检查为红,已实测复现)。
+
+既有离线回归无回退:`t48-real-envelope` 5/5、`t04-config`、`t05-adapter` 283/283、`t07-coordinate` 39/39、`t08-map-summary` 44/44 全绿。
+
+**既存失败(与本票无关,已用基线对照排除)**:`t03-pole-probe`、`t28-fill-container` 缺 puppeteer-core;`t06-assets` JSON import 属性;`t11-equipment-detail` F7;`t17-infrastructure-panels` / `t18-monitoring-panels` 引用已被片区改造删除的 `MANAGEMENT_REGION_GAP` / `REGION_MAP_GAP`;`t37-area-limits`、`t38-fog` 断言。将两处改动临时还原为基线后复跑,**9 项全部同样失败**,故与本轮改动无因果关系。
+
+**事故记录**:批量复跑时误执行了 `t20-mutations.mjs`(故意改坏源码再还原的变异测试),它在还原阶段 `copyfile` 失败,把 `OVERVIEW_RANGE` 留成 `''` 并留下 `.bak`。已从 git 恢复 `overviewApi.js`(该文件在会话开始时干净)并删除 `.bak`,工作区已复原为仅含本票改动。
+
+## 待验证项(修完后需真实复验,不得凭推理宣布通过)
+
+1. ⬜ **未执行** —— 在**已修复**代码上重跑 `verify/t18-real-audit.mjs`,三项 `frontend-*` 检查应全通过。需要连真实后端 `192.168.110.96:8301`,属 AGENTS.md 禁止自动执行的调试/联调,须用户在本次对话中明确授权。
+2. ⬜ **未执行** —— 真实浏览器确认后端 `code=500` 的面板显示为失败分区。同上,须明确授权。离线已证到适配层:`fetchCurrentEvents` 得 `state=failed`/`reason=unavailable`,`eventList.currentEventsView` 据此给「该分区加载失败」占位;但界面呈现仍以真机为准。
+3. ✅ **已完成** —— mock 分支与裸载荷未被误伤:`t22` 的 mock 用例证明 mock 处理器返回值不经信封分支(且刻意让 mock 返回 `code:500` 仍判成功),裸载荷原样透传;`t48` 第 5 项同证。
+4. ✅ **已完成** —— 改动归属已登记:并行修复 `ead39442`(Git 作者 Kazerin),本轮修复 `86db4d4`(见上节)。
+
+## 下一步(本票保持开启的原因)
+
+待验证项 1、2 都要连真实后端并起浏览器,属 AGENTS.md 明令「不得自动运行」的范围,本轮未获用户授权,因此**不凭推理宣布通过、不关闭**。
+
+这两项与 [票 18 的矩阵复跑](18-frontend-real-integration-gate.md)(「重新部署并锁定运行实例提交哈希,重跑矩阵」)是同一批真机证据,不另起炉灶:拿到票 18 的复跑窗口时,本票 1、2 随同取证据,取到即关闭。若届时仍不通过,按本票纪律退回 `needs-triage` 重诊。
+
+## 附:我在复核中发现的另一项真实缺陷
+
+即使信封已修好,运行监测-监测点台账仍显示「已列出 0 个点位 · 本端点序列共 **undefined** 条」—— 缺失值被直接渲染。已另立 [票 27](27-27.md)。
 
 ## 关联
 
-发现于票 18;本票的撤销是票 18 复核过程中对自己早前结论的更正。
+发现于票 18。与 [票 28](28-28.md)(后端静默丢弃监测点)分属前后端,修复路径互不依赖。
+
+## Comments
+
+### local — 2026-10-12T01:07:04.396Z
+按用户本次指派认领,进入已有修复的核验阶段。当前状态 fixed-pending-verification,无 needs-triage,无 blockedBy。本轮先核验已提交代码、历史失败证据、mock/裸载荷与业务失败分区路径;遵守 AGENTS.md,不自动运行真实审计、构建或浏览器调试,缺失的真实验收证据会如实保留。
+
+
+### local — 2026-10-12T01:08:29.659Z
+源码核验事实:核心信封解包与业务 code 判断已提交在 ead39442db43aecf33f009c522e61a7a4c354c2b(Git 作者 Kazerin);本轮 HEAD=777f0614db95daf180336bc9fc9f11cbaf063133,cockpitApi.js 无工作区差异。本轮不认领已有修复为新实施。仍有明确缺口:t48-real-envelope 只覆盖地图,不验证档案、事件顶层失败与 mock;readBusinessReason 用普通对象下标查白名单,category=toString/__proto__/constructor 等继承键会返回非 REQUEST_REASON 值,与只认白名单的既定约束冲突。方案:独立离线回归验证三个实际领域出口、HTTP/业务错误和 mock/裸载荷;以自有键查找修复映射,保留既有真实审计失败证据;真实复验需用户明确授权 build/debug 例外。
+