# 坐标自动准入(取消人工校核前置)与主场景区边界冲突 Type: implementation Status: resolved Assignee: codex Label: ready-for-agent Blocked by: None Category: backend-coordinate-admission ## 进度 ### ✅ 2026-10-12 结票:独立复核 + 落库缺口已闭合 **结论:本票交付物成立,已关闭。** 本轮不是采信既有票面,而是从远端与线上重新取证;过程中发现并跟踪了一处**票面未记录的落库缺口**,该缺口已由并行会话闭合,我逐位核对无误。 **一、独立复核(我自己跑,未采信执行 agent 汇报)** | 复核项 | 证据 | 判定 | | --- | --- | --- | | 判据实现 | `E:\pipe-ner-server` 工作区 `CockpitCoordinateValidator`:`MISSING_*` → `*_OUT_OF_RANGE` → `ZERO_COORDINATE` → `OUTSIDE_PRIMARY_AREA` → `VALID`;`coordinateVerified` 不再参与判定 | ✅ | | 线上 `map-snapshot` | 只读 GET `192.168.110.96:8301`:`renderableCount=12`、`totalObjectCount=15`、`invalidCount=3`、**`unverifiedCount=0`**;3 个 INVALID 全为 `MISSING_LONGITUDE` | ✅ | | 线上 `infrastructure/geometry` | 5272 feature:`VALID` 5269、`INVALID` 3(`LONGITUDE_OUT_OF_RANGE`×2、`OUTSIDE_PRIMARY_AREA`×1) | ✅ | | 零残留 | 扫 5 个 cockpit 端点(map-snapshot / monitoring distribution / monitoring situation / base-archives / geometry),`UNVERIFIED` 与 `UNVERIFIED_COORDINATE` **出现次数均为 0** | ✅ | | 被挡几何原值 | 逐条读线上原值:`YL-YJ-001`/`YL-YJ-002` = `1212,1212`(`LONGITUDE_OUT_OF_RANGE`)、`GD-001` = `12,12`(`OUTSIDE_PRIMARY_AREA`)——与票面下表一致 | ✅ | | 跨界管线 | 用 `map.primaryArea.bounds` 独立重算 5272 条几何:**完全在区内 5268、跨边界 0**,票面「跨界 0 条」成立 | ✅ | | 前端契约 | 跑 `t07-coordinate.mjs` → **39/39 通过**;`t24-pipe-layer.mjs` C7(区外专属文案)通过;`COORDINATE_REASON_TEXT` 含 `OUTSIDE_PRIMARY_AREA` | ✅ | | 文档同步 | [ADR 0002]()、[ADR 0005]()、[ADR 0009]()、`cockpit-deployment.md` 自检步骤均已改为自动准入口径 | ✅ | | 管理端零引用 | `E:\city-life-line` 全仓搜 `coordinateValidation`/`coordinateVerified`/`CockpitCoordinateValidator` → **0 命中**,反证「既无校核字段也无校核入口」 | ✅ | | 后端改动范围 | `8013a207` 触及 15 个文件(13 个测试 + 判据 + `docs/cockpit/README.md`);`1427a67e` 触及 6 个适配器注释——**全部在 cockpit 包内**,无越界改动 | ✅ | | 测试名如实改名 | 3 处方法名按新语义改名(如 `invalidEndIsNotMaskedByAnUnverifiedStart…` → `…ByAValidStart…`),**不是把断言反过来** | ✅ | **二、本轮发现并跟踪的落库缺口(已闭合)** 复核中发现:后端修复提交当时**只在 `E:\pipe-ner-server` 本地 `master`,未推送任何远端**,而 `origin/master` 上仍是旧的 `if (!coordinateVerified) → UNVERIFIED` 判据——即「从 `origin/master` 部署会把整个修复静默回退」。这与本 effort Notes 的基线锚点(「仓库提交(分支+哈希+**已推送**)」)及票 31/20/12/13 的收口口径都不符,因此票面原先并未记录这一事实。 经维护者裁定后核对,该缺口**已由并行会话闭合**,我独立确认: - 真实远端 `refs/heads/master` = `d81c977b`(`git ls-remote`,非本地跟踪引用);`f33ef570` 已是 `origin/master` 祖先。 - `origin/master` 上的 `CockpitCoordinateValidator` 代码体已是新判据(`if (!insidePrimaryArea(...)) → OUTSIDE_PRIMARY_AREA`),旧的 `if (!coordinateVerified)` **只剩 javadoc 里描述历史的那一句**,代码中无该门槛。 - rebase 前后 **patch-id 逐位相同**:`8013a207 → f33ef570`、`1427a67e → d81c977b`——证明变基是**内容等价**的重放,没有夹带改写。 - 因此票面 `8013a20`/`1427a67` 与线上 `f33ef570`/`d81c977b` 是**同一改动的两个哈希**(rebase 重写),后续引用以远端哈希为准。 **三、前端落库状态**:修复提交 `bf8d21b` 已是前端 `origin/master`(`5cc5d036…`)祖先。本地另有 2 个未推送提交(`c4aa10e`/`21fbbe5`)属**票 24 几何接线与票 32-构件素材**,不触碰坐标准入门径,与本票无关。 **四、未闭合项**:3 条脏几何(`YL-YJ-001/002` = `1212,1212`;`GD-001` = `12,12`)属**业务侧数据订正**,非代码问题——判据在如实工作。本票按自身交付物(取消人工校核前置 + 区外自动准入 + 口径裁定)收口,该数据项保留在「待办」并转业务侧,不阻塞本票关闭。 --- **✅ 2026-10-12 维护者重启后端,真实接口闭环复验通过。** 后端 `8013a20`:`CockpitCoordinateValidator` 改为「结构合法 + 主场景区内」自动准入,新增 `OUTSIDE_PRIMARY_AREA`,`coordinateVerified` 不再影响结论;12 个测试文件重基线 + 新增 21 例判据单测。 **重启后实测(真实在线接口,非离线复算)**: | 端点 | 修复前(旧构建) | **重启后实测** | 离线探针预测 | 一致 | | --- | --- | --- | --- | --- | | `/map-snapshot` 可上图 | 0 / 15 | **12 / 15** | 12 / 15 | ✅ | | `/map-snapshot` `unverifiedCount` | 15 | **0** | 0 | ✅ | | `/map-snapshot` `invalidCount` | 0 | **3**(均 `MISSING_LONGITUDE`) | 3 | ✅ | | `/infrastructure/geometry` 可上图 | 0 / 5272 | **5269 / 5272** | 5269 / 5272 | ✅ | | 逐对象 `state` | 全 `UNVERIFIED` | 12×`VALID/renderable:true`、3×`INVALID/MISSING_LONGITUDE` | — | ✅ | 真实接口结果与重启前用**已编译生产判据**对同一批数据做的离线预测**逐项吻合**,说明预测方法与线上行为一致。零 `UNVERIFIED` 残留。 验证(均由我本人在落盘后独立复跑,非仅采信执行 agent 汇报): - 针对性 `-Dtest=*Cockpit*Test,*BusinessCockpit*Test` → **486 / 0 / 0,BUILD SUCCESS**(基线 465 / 15F / 1E)。 - 全量 `-pl zksy-admin -am test` → 全 reactor **BUILD SUCCESS**,`zksy-admin` **505 / 0 / 0**。 前端 `bf8d21b`:原因词表同步 + `t07` **39/39**。真实数据离线复算可上图 12/15 与 5269/5272。 ### 第二轮补强(非自证式验证 + 文档/注释同步) - **真实接口数据 × 已编译生产判据联测**(`verify/artifacts/t32/LiveCoordinateAdmissionProbe.java`,只读):把真实后端导出的 15 个地图对象 + 5272 条几何坐标喂给**线上要跑的那一份** `CockpitCoordinateValidator`(而非另写的复算脚本),四项断言全 PASS:`map 12/15`、`geo 5269/5272`、原因码分布 `MISSING_LONGITUDE=3 / LONGITUDE_OUT_OF_RANGE=2 / OUTSIDE_PRIMARY_AREA=1`、**零 `UNVERIFIED`**。 - 过程中探针自身出过**一次解析错误**(把经度当编码、只把纬度当载荷,误报 `map 3/15`);修正后重跑通过。该错误只影响探针读数,不触及生产判据——如实记录以免后来者误读中间结果。 - **更正一处旧结论**:那 3 个被挡的地图对象(`41010500001320000001`、`41010500001320000002`、`TEST-CBLL222222222`)经查响应里 `longitude`/`latitude` 是**真正的 `null`(缺坐标)**,本票早前写的「`(0,0)` 零坐标」是我的读数误判。结论不变(都该被挡下),新判据下原因是 `MISSING_LONGITUDE`。 - **管理端 `E:\city-life-line` 无需改动**,已核实:它对 `coordinateValidation` / `coordinateVerified` **零引用**——这反过来印证了根因(既无校核字段、也无校核入口)。 - **同步 6 处会误导后人的注释**(后端 `1427a67`):各适配器 javadoc 原先写着「无校核字段 → 按未校核上报、由契约层判定为不可渲染」——**这正是本次 bug 的认知来源**,判据改了而注释不改,下一个人会照原路再走一遍。纯注释改动,改后复跑仍 **486 / 0 / 0**。 - **同步 4 份文档**(`f9c89ba`):[ADR 0002]() 原句「未通过校验的对象不上图」、[ADR 0005]()、`cockpit-deployment.md` 的自检步骤与可观察字段表——按项目「代码优先」规则更新为 2026-10-12 自动准入口径。部署文档原先教运维「若真实数据均未校验,记为无可上图样本」,不改会让人把正常状态当缺口上报。 - 前端 `5096454` 修正 `coordinate.js` 头部注释「六取一」→「七取一」。 一处数字纠正如实记录:我最初推算 `CockpitMapSnapshotControllerTest` 汇总应为 `renderableCount=7` 是**错的**——7 个对象里 `EQ-FREE-001`(`181`)与 `EQ-FREE-004`(`0,0`)仍非法,可上图应为 3 个点位 + 2 个设备 = **5**。执行 agent 按实测写 `5 / 2 / 0` 并纠正了我,票面以实测为准。 与本次无关的既有失败(已独立复核归因):`zksy-system` 的 `AlarmLifecycleServiceTest.insertWritesLogBeforeCommittingWithoutTrigger` —— 断言期望的日志串与实现不一致(`alarmContent` 已含「实际值/阈值」格式化文本,被再次拼接),单跑复现 same failure;该 alarm 域对 cockpit **零引用**,物理上不受本次改动影响。未擅自修它。 待办:① 维护者重启后端加载新构建;② 重启后复验 `renderableCount=12 / unverifiedCount=0`;③ 对区外口径 A(严格小区块,当前实现)vs B(放宽到影像覆盖范围以保住框外可定位)拍板——本实现与 ADR 0009 的框外定位例外存在冲突;④ 2 条经度越界窨井属真实数据错误,需业务侧订正。 ## 来源 维护者 2026-10-12 实报:真实后端有大量监测点、设备点位与管线数据,但**一个都没有上屏**,全部显示「坐标未经校验」,且找不到任何校验入口。随后裁定:**取消该人工校核步骤**(「所有进入数据库中的点位坐标都是已经过校验后的了」),或改为自动规则——只有落在主场景区内的坐标才正常显示。 ## 现象与实测(修复前) 对真实后端 `192.168.110.96:8301` 只读 GET: | 端点 | 对象数 | 可上图 | 未校验 | 非法 | | --- | ---: | ---: | ---: | ---: | | `/map-snapshot` | 15 | **0** | 15 | 0 | | `/infrastructure/geometry` | 5272 | **0** | 5270 | 2(经度越界) | 样本 `GS001` 坐标 `110.399876, 28.458287` 数值完全正常,仍被判 `UNVERIFIED_COORDINATE / renderable=false`。 ## 根因(已确认) 判定链三段,每段都指向同一个缺失: 1. `CockpitCoordinateValidator.validate()` 第一道判断是 `if (!coordinateVerified) return UNVERIFIED/renderable=false`——**未校核优先于所有数值检查**。 2. 真实适配器一律传 `false`:`BusinessCockpitMapSnapshotAdapter`(监测点、设备)、`BusinessCockpitDeviceFactResolver`、`CockpitFacilityGeometryService`(先用 true 做结构检查,通过后再故意用 false 降级成未校核)。 3. 全后端搜索**没有任何一行代码把 `coordinateVerified` 置为 true**;`monitor_point`、`equipment_base`、`pipe_network_base` **都没有校核列**;管理端也没有任何校核接口或页面。 前端没有误判:唯一准入判据只认后端 `renderable === true`,后端给 false 就不画——按原契约是**正确行为**。后端 `docs/cockpit/README.md` 自己也写过「可信坐标校核来源需另行排期」,即这是已知契约缺口,不是前端 bug。 ## 维护者裁定 - **不做人工校核入口**(该步骤没有必要,入库坐标即视为可信来源)。 - 改为**自动规则**:数值合法 + **落在主场景区内**才可上图。 - 范围选择:**只用主场景小区块**(`map.primaryArea.bounds`),不用更大的影像覆盖范围。 ## 实施(已完成) 判定顺序(唯一一处 `CockpitCoordinateValidator`): 1. 缺经度 → `INVALID/MISSING_LONGITUDE` 2. 缺纬度 → `INVALID/MISSING_LATITUDE` 3. 经度越界 → `INVALID/LONGITUDE_OUT_OF_RANGE` 4. 纬度越界 → `INVALID/LATITUDE_OUT_OF_RANGE` 5. 零坐标 → `INVALID/ZERO_COORDINATE` 6. 数值合法但区外 → `INVALID/**OUTSIDE_PRIMARY_AREA**`(新增原因码) 7. 其余 → `VALID/reason=null/renderable=true` 结构错误**优先于**区外判定,因此既有的非法坐标拦截一条都没有被削弱。`coordinateVerified` 入参保留(不改 16 处调用点)但**不再参与判定**;新流程不再产生 `UNVERIFIED`。 - 无数据库改动,无管理端改动,不伪造坐标。 - 前端 `COORDINATE_REASON_TEXT` 同步新增 `OUTSIDE_PRIMARY_AREA` → 「不在主场景区范围内」。 - 后端文档 `docs/cockpit/README.md` 的坐标章节已同步改写(原「未验证禁止渲染」表述作废)。 ### 实测效果(真实接口数据 × 已编译生产判据) 用编译好的生产类 `CockpitCoordinateValidator` 逐条判定**从真实接口导出的坐标**(探针 `verify/artifacts/t32/LiveCoordinateAdmissionProbe.java`,只读): | 端点 | 修复前可上图 | 修复后可上图 | 被挡下(按原因码) | | --- | ---: | ---: | --- | | `/map-snapshot` | 0 / 15 | **12 / 15** | `MISSING_LONGITUDE` ×3 | | `/infrastructure/geometry` | 0 / 5272 | **5269 / 5272** | `LONGITUDE_OUT_OF_RANGE` ×2、`OUTSIDE_PRIMARY_AREA` ×1 | 被挡下的 3 个地图对象是 `41010500001320000001`、`41010500001320000002`、`TEST-CBLL222222222`——经核实它们的 `longitude`/`latitude` 在响应里是**真正的 `null`(缺坐标)**,不是零坐标。探针断言 `map 12/15`、`geo 5269/5272`、**零 `UNVERIFIED`** 三项全部 PASS。 (早前本票一度把这 3 个写成「`(0,0)` 零坐标」,那是我读数的误判,已在此更正;结论不变——它们本就该被挡下。) 额外核对:**没有一条管线跨越主场景区边界**(跨界 0 条),所以小区块不会把管线切坏或让整段变 `geometry=null`。 ### 被挡下的 3 条几何 —— 均为业务侧脏数据(非判据问题) 已逐条读出原值,可直接交业务侧修正;**不需要也不应该由代码"顺手"改**: | 来源 | 编码 | 名称 | 原始坐标 | 原因码 | 判读 | | --- | --- | --- | --- | --- | --- | | `manhole_base` | `YL-YJ-001` | 沅陵县窨井001 | `1212, 1212` | `LONGITUDE_OUT_OF_RANGE` | 疑似把 `121.2,121.2` 的小数点漏了,或占位值未替换 | | `manhole_base` | `YL-YJ-002` | 沅陵县窨井002 | `1212, 1212` | `LONGITUDE_OUT_OF_RANGE` | 同上 | | `gas_pipe_point` | `GD-001` | 管点001 | `12, 12` | `OUTSIDE_PRIMARY_AREA` | 数值合法但不在沅陵,明显是占位/测试数据 | 三者都**本该被挡下**:判据在如实工作,不是判据误伤。修正归业务侧(`equipment_base` / 管点表直接改值),或确认为测试数据后清理。 ## 验证 - 后端针对性测试:`mvn -o -pl pipe-network-service/zksy-admin -am test "-Dtest=*Cockpit*Test,*BusinessCockpit*Test" "-DfailIfNoTests=false"` → **486 / 0 / 0,BUILD SUCCESS**(我自己复跑确认,非仅采信执行 agent 汇报)。 - 后端全量测试:`mvn -o -pl pipe-network-service/zksy-admin -am test` → 全 reactor **BUILD SUCCESS**,`zksy-admin` **505 / 0 / 0**(同样自己复跑确认)。 - 新增 `CockpitCoordinateValidatorTest` **21 用例**:区内 VALID、东南西北四向区外 `OUTSIDE_PRIMARY_AREA`、闭区间四角边界命中与边界外一丁点不命中、结构错误优先于区外、数值合法但区外、**`coordinateVerified` 传 true/false 结论完全一致**、`insidePrimaryArea(null,*)` 两个方向 → false。 - 12 个受影响的既有测试文件已按新契约重基线;非法坐标参数化用例 **CsvSource 一行未改**(`181`、`-181`、`91`、`-91`、`(0,0)`、null 全部保留作结构错误样本),原始坐标保留断言与「被拦对象仍计入统计」断言均保留;3 处语义自相矛盾的方法名已如实改名(不是把断言反过来)。 - 前端 `t07-coordinate.mjs` **39/39 通过**(原因词表 6 → 7 已同步)。 - **真实数据 × 已编译生产判据联测**(本轮新增,非自证式):`LiveCoordinateAdmissionProbe` 读取真实接口导出的 15 个地图对象 + 5272 条几何坐标,逐条调用**线上要跑的那一份** `CockpitCoordinateValidator`(不是另写的复算脚本),结果 `map 12/15`、`geo 5269/5272`、零 `UNVERIFIED` 三项断言全部 PASS。过程中探针自身出过一次解析错误(把经度当编码、只喂纬度,误报 `map 3/15`),已修正并重跑;这类错误只影响探针读数,不影响生产判据。 - 已确认与本变更无关的既有失败:前端 `t17`(`MANAGEMENT_REGION_GAP` 导出缺失)、`t05 D1`(`overviewStore.js` 出现 `deviceStatistics`,并行会话在改);后端 `AlarmLifecycleServiceTest` 既有失败(还原 validator 到 HEAD 后原样复现)。 ## ✅ 区外口径已裁定:A(严格主场景区) ADR 0009 与票 37 在 2026-09-28 曾裁定「点选/定位到框外对象是例外」;本次准入规则让**区外对象 `renderable: false`**,与此例外存在张力。 **2026-10-12 维护者裁定:选 A(严格区块)。** 已同步修订 [ADR 0009](): - 准入保持严格主场景区,接受「框外点位不上图、不可定位」。 - 该例外**经产品路径已不可达**(`mapLocate` 对不可上图对象返回 `BLOCKED`;`selectMonitoringPoint` 先过 `isRenderable`),不会再出现"点选框外对象、镜头飞出去"。 - 相机层的例外实现(`cameraControl.js` 定位豁免 + `outsideArea` 读数)**保留不删**:它是相机层的正确保证,票 37 用例继续有效。 - **同源警告已写入 ADR**:准入矩形在后端是常量、在限位侧是配置 `map.primaryArea.bounds`,当前两者逐位相同;改配置必须同时改后端常量。 影响面:当前真实数据全部落在区内,所以今天**无可见行为差异**;差别只在未来接入框外合法点位时——那时按 A 它们不上图、留在清单并显示 `OUTSIDE_PRIMARY_AREA` 原因。 ## 待办 - [x] 维护者重启后端实例加载新构建(2026-10-12 已完成)。 - [x] 重启后复验真实接口:`map-snapshot` **12/15、`unverifiedCount=0`**;`infrastructure/geometry` **5269/5272**(已完成,见上表)。 - [x] 区外口径拍板:**选 A(严格主场景区)**,已同步 ADR 0009(2026-10-12)。 - [x] 独立复核会话重跑取证并结票(2026-10-12,见「进度」与 Comments)。 - [ ] 3 条脏几何数据(`YL-YJ-001/002` = `1212,1212`;`GD-001` = `12,12`)需业务侧订正或确认为测试数据后清理——见下表,属数据问题、非代码问题。**已转业务侧,不阻塞本票。** - [x] 本票原与 [32-32.md](32-32.md)(管线构件 glTF 素材)**共用编号 32**,属 tracker 重号缺陷;**2026-10-12 已改号为 34**(`34-coordinate-auto-admission.md`),重号解除。 ## Out of scope - 前端管线图层接线(`/map-facilities` → `/infrastructure/geometry` 路径不匹配)——维护者明确指示「管线先放着,本来也还没开发完全」,归票 24。 - 人工坐标校核字段、校核人/时间/来源与回填——已被本次裁定取消,不再需要。 - 后端其它业务路径与写接口。 ## Comments ### local — 2026-10-12T21:05:00.000Z **结票(独立复核会话)**。按 wayfinder 规则先把票读全(正文/评论/字段行/父子与阻塞边),再对抗式复核交付物。 复核结论:**交付物成立,票关闭**。我自己重跑取证而非采信票面——线上只读复验 `map-snapshot` 12/15、`unverifiedCount=0`、3×`MISSING_LONGITUDE`;`infrastructure/geometry` 5269/5272(`LONGITUDE_OUT_OF_RANGE`×2 + `OUTSIDE_PRIMARY_AREA`×1);5 端点扫描 `UNVERIFIED` 零残留;5272 条几何按 `primaryArea.bounds` 独立重算跨界 **0** 条;`t07` 39/39、`t24` C7 通过;ADR 0002/0005/0009 与部署文档均已同步;管理端 `E:\city-life-line` 零引用;两个后端提交全部落在 cockpit 包内。 **本轮新发现(票面原先未记录)**:后端修复当时只在本机 `master`、未推送任何远端,`origin/master` 仍是旧判据——「从远端部署会静默回退整个修复」。经维护者裁定后核对,该缺口**已由并行会话闭合**:真实远端 `master=d81c977b`,`f33ef570` 已是其祖先,远端代码体为新判据(旧 `if (!coordinateVerified)` 仅存于 javadoc 的历史描述),且 rebase 前后 **patch-id 逐位相同**(`8013a207→f33ef570`、`1427a67e→d81c977b`),证明是内容等价重放。票面旧哈希与新哈希为同一改动。 前端 `bf8d21b` 已在 `origin/master`;本地 2 个未推送提交属票 24/32-素材,与坐标准入无关。 未闭合项仅剩 3 条脏几何数据(`1212,1212`×2、`12,12`×1),属业务侧订正、非判据问题,已保留在待办并转业务侧,不阻塞本票。 **方法论记录**:核对「是否已推送」必须用 `git ls-remote` 读真实远端 + `git show origin/master:` 读远端内容,不能只看本地跟踪引用或 `git status -sb`——并行会话会在本地引用滞后期间改变远端。 ### local — 2026-10-12T11:07:12.071Z 实施已完成并提交:后端 8013a20(判据重写 + 13 个测试重基线 + 新增 16 例 CockpitCoordinateValidatorTest + docs/cockpit/README.md 同步),前端 bf8d21b(原因词表新增 OUTSIDE_PRIMARY_AREA + t07 回归同步,39/39 通过)。自验:mvn -o -pl pipe-network-service/zksy-admin -am test "-Dtest=*Cockpit*Test,*BusinessCockpit*Test" "-DfailIfNoTests=false" → 486/0/0 BUILD SUCCESS(我本人复跑,非仅采信队友汇报)。红线复核:非法坐标参数化 CsvSource 一行未改,原始坐标保留断言与「被拦对象仍计入统计」断言均保留;3 处语义自相矛盾的方法名已如实改名。真实数据离线复算:map-snapshot 可上图 0/15 → 12/15(挡下 3 个 (0,0)),geometry 0/5272 → 5269/5272;跨界管线 0 条。未重启/未构建(遵守 AGENTS.md),真实接口仍是旧构建 renderableCount=0,需维护者重启后复验。临时镜像 268MB 与日志已清理,IDE 文件已还原。