已关闭。两个曾被 #29 阻塞的验收项(状态栏/饼图真实八桶计数、多选筛选后点数变化)已由后端票 29 在同一份真实运行环境(dataSource=real → http://192.168.110.96:8301)上以 real-probe-new-runtime.json(45/45)与 browser-verification.md 收口:八桶 EMPTY=6、UNASSOCIATED=7 其余 0,状态栏与饼图同源;筛选 13→1(ALARM)→2(ALARM,WARNING),全局计数不变;BOGUS/ALARM,BOGUS 两端 HTTP 400 INVALID_REQUEST;分页 token 保留(map-page2-token-retained)。本会话在不重复触发构建/浏览器的前提下复核:① 前端源码未变(仅 domain/mapSummary.js:toStatusDistribution 八桶完整性 + 装载器缺 token 停住 / 第 2 页起必带锁定 snapshotAt / SNAPSHOT_EXPIRED 保留旧对象);② filterOptions.js 的 statusFilterQuery 白名单去重、丢弃 BOGUS,保证「不把脏值发给后端」;③ 重跑 t23-map-contract 11 项断言全过(token 传递与转义、八桶完整性、脏值剔除、缺 token 停住且保留对象、快照失效不归零)。按项目规则本会话未自动运行 build/dev/browser;真实浏览器验收未在本会话重跑,其在 #29 会话中的记录结果判定有效。
票 18 真实联调与浏览器取证(2026-10-10)。
GET /map-snapshot?page=2&pageSize=2(不带 snapshotAt),后端按契约返回 400 SNAPSHOT_EXPIRED,地图随即进入「快照已失效 · 请刷新」与 0 点位。首屏 GET /map-snapshot?page=1&pageSize=2 是正常签发 snapshotAt 的。map-snapshot 响应没有 pointStatusDistribution,mapFilter.pointStatus 也不回显;传 pointStatus=ALARM 与不传返回完全相同的 total=13(入参被忽略)。UNKNOWN 视为非法值返回 INVALID_REQUEST;/monitoring/distribution 对 UNKNOWN 与 UNASSOCIATED 均返回 400。非法值 BOGUS 在 map-snapshot 上返回 200 而不是 400(与前端白名单预期不一致)。—,分布面板提示「全局状态分布未取到(后端尚未发布八桶全局计数)」。snapshotAt;快照失效按既有恢复纪律处理,不把整张图清空成 0 点位。UNKNOWN + UNASSOCIATED,多值逗号分隔、OR;map-snapshot 与 /monitoring/distribution 两端行为一致;非法值两侧一致拒绝。浏览器实测:地图自动分批加载到完成且不出现快照失效;状态栏与分布面板显示真实八桶计数;多选状态筛选后地图点数随之变化;后端返回 INVALID_REQUEST 时前端不把脏值发出去。探针 map-eight-status-buckets、status-*、invalid-* 相关检查项全部通过。
发现于票 18;承接决策票 02 与规格 07。
① 分页 token:前端已正确传递,历史故障不再复现;本次仅补防护与回归。
真实只读探针 [t23-real-probe](../verify/t23-real-probe.mjs)(http://192.168.110.96:8301,仅 GET)实测:
| 检查 | 实测 | 判定 |
|---|---|---|
| 第 1 页签发 token | snapshotAt=2026-10-10T17:18:53.1160157+08:00,total=13 |
通过 |
| 第 2 页带 token | HTTP 200 / code=200 |
通过 |
| 第 2 页不带 token | HTTP 400 / category=SNAPSHOT_EXPIRED |
后端规则通过 |
| 前端装载器是否漏带 | createMapSummaryLoader 第 2 页起必带 state.snapshot(domain/mapSummary.js) |
票 18 第 3 点「上次故障未复现」属实 |
因此原现象 1 属历史已消失的故障,不能当成仍存在的缺陷;但缺 token 时前端会带着空 token 继续翻页这一点没有防护,本轮已补。
② 八桶契约缺口在服务端,前端无法单方面交付。
| 真实实测 | 结果 | 与前端契约的差距 |
|---|---|---|
map-snapshot 响应含 pointStatusDistribution |
无 | 状态栏与饼图只能如实降级(决策 02 已允许 available:false) |
mapFilter 回显键 |
industry/district/districtName/districtFilter/deviceType/pointCodes/deviceCodes |
无 pointStatus |
pointStatus=ALARM 与不传 |
total 均为 13 |
入参被完全忽略,筛选不生效 |
map-snapshot?pointStatus=UNKNOWN |
HTTP 200 | 规格要求可接受,但实为忽略而非匹配 |
map-snapshot?pointStatus=UNASSOCIATED |
HTTP 200 | 同上 |
map-snapshot?pointStatus=BOGUS |
HTTP 200 | 违反规格 07「认不出的值 400 INVALID_REQUEST」 |
/monitoring/distribution?pointStatus=UNKNOWN |
HTTP 400 | 该端点只认六态单值 |
服务端源码复核一致:CockpitMapSnapshotResponse 无 pointStatus 入参与 MapFilter.pointStatus(mapFilter 六个键);CockpitMonitoringDistributionService.resolvePointStatus 只认六态单值。前端已做的白名单过滤(isKnownPointStatusBucket)保证不会把脏值发出去,但真实环境「不传脏值」与「后端拒绝脏值」两件事无法由前端同时成立。
nextPage > 1 且无锁定 snapshotAt 时进入失败态并停住(reason='snapshot-token-missing'),不发无效分页请求,也不清空已加载对象。toStatusDistribution 由「有数组即算已发布」改为八桶齐全且计数为非负整数才 available:true;缺桶、空数组、重复桶、非法桶、非整数或负数一律 available:false。这消除了「部分桶被当作真实零值」的风险——正是状态栏显示 — 的场景里最容易出现的口径造假。[t23-map-contract](../verify/t23-map-contract.mjs):首屏不带 token、第 2 页必带(经真实 transport 拼串断言,含 +///: 这类需要转义的 token)、BOGUS 被白名单剔除、缺桶/重复桶/负数不被接受、缺 token 停住且保留已加载对象、SNAPSHOT_EXPIRED 保留旧对象不归零。八桶的入参、回显、全局计数与非法值拒绝,全部落在 E:\pipe-ner-server 的响应类与服务类。前端无法在不伪造的前提下让界面显示真实八桶计数——本地过滤代替后端筛选已被本 map 的 Out of scope 明文禁止。因此真实八桶验收项由新开的后端执行票承接,前端验收只到「如实降级 + 不发脏值 + token 正确」。
本票在真实八桶计数落地前不关闭。
环境 http://localhost:8001,dataSource: real(本机 gitignored public/cockpit/config.json 指向 http://192.168.110.96:8301/api/cockpit/v1);捕获 d57c3eed5ebd1,440 请求、0 错误。
| 验收项 | 实测 | 判定 |
|---|---|---|
| 自动分批到完成 | 5 个刷新周期,每周期 page=1(不带 token)→ page=2…7(全部带 snapshotAt),全部 HTTP 200 |
通过 |
| token 转义正确 | snapshotAt=2026-10-10T17%3A38%3A15.997194%2B08%3A00(:→%3A、+→%2B) |
通过 |
| 不出现快照失效 | 界面「地图摘要加载完成 · 已上图 0 个 · 无地图位置 13 个」;全程无 SNAPSHOT_EXPIRED、无 expired 态 |
通过 |
| 状态栏与饼图同源降级 | 八桶计数全部显示 —(未取到),未把缺失桶画成 0 |
通过 |
| 多选状态筛选后点数变化 | 无法验证:pointStatus 被服务端忽略,ALARM 与不传均 total=13 |
受阻于 29 |
| 不把脏值发给后端 | 契约回归断言 BOGUS 被白名单剔除(真实环境服务端对 BOGUS 回 200,前端侧仍不发) |
通过 |
同屏真实读数印证接线正常:设施底数 5276、管线里程 67.60944 km、行业数 4(燃气/供水/排水/窨井)、片区「未分区」5278。
[t23-map-contract](../verify/t23-map-contract.mjs):11 项断言通过(token 传递与转义、八桶完整性、BOGUS 剔除、缺 token 停住且保留已加载对象、SNAPSHOT_EXPIRED 不归零)。domain/mapSummary.js(+19/−5)。git stash 后重跑,失败集合完全相同(缺 puppeteer-core 模块、票 15 遗留的 MANAGEMENT_REGION_GAP 断言等),即本轮零回归。等待 29 落地服务端八桶契约后,重跑 [t23-real-probe](../verify/t23-real-probe.mjs) 与浏览器验收,补「状态栏/饼图显示真实八桶计数」与「多选筛选后点数变化」两项,再关闭本票。
Status: resolved Assignee: codex Type: task Blocked by: #29 Labels: ready-for-agent, frontend-execution, wayfinder:task