docs: add OpenCPN code-first S-52 audit task

This commit is contained in:
OpenAI Codex
2026-09-20 17:43:38 +08:00
parent 29ee5306f1
commit 1a07406e6e

View File

@@ -0,0 +1,517 @@
# CM95 S-52 OpenCPN 代码优先流程等价审计 Codex 任务 v1
日期:2026-09-20
## 1. 任务性质
本任务是**只读代码审计任务**。核心不是继续从最终截图寻找差异,而是以仓库内固定的 OpenCPN 源码快照为 Source of Truth,正向还原 OpenCPN 的 S-52 渲染流程,再与当前 CM95 实现逐层比较。
本轮原则:
- 先审计,后修改。
- 不修改 CM95 功能代码。
- 不修改 Web runtime renderer。
- 不修改 builder、CSP resolver、style compiler、PBF schema 或 release gate。
- 不把任何 PREVIEW_ONLY / NOT_IMPLEMENTED 状态改成 IMPLEMENTED。
- 不通过“截图看起来接近”替代代码流程等价证明。
- 不新增或修改 Jest 测试。
- 如需临时分析脚本,只能放在 `/tmp` 或其他非仓库临时位置,不提交。
## 2. 仓库与 Git 边界
仓库:`/root/sourceserver/cm95`
分支:`main`
开始任务时必须记录:
- `git rev-parse HEAD`
- `git status --short --branch`
- 当前工作区是否存在与本任务无关的未提交修改。
本任务不得清理、重置、覆盖或提交与本任务无关的现有修改。最终如提交 Git,只允许显式 stage 本任务新增/修改的审计文档和审计产物。
## 3. 固定 OpenCPN 对照基线
不要依赖 `/tmp/opencpn-s52-audit/` 作为必要输入。统一使用仓库内已固定的上游参考快照:
`docs/外部参考/OpenCPN源码基线_f7211b4e/`
固定 OpenCPN commit:
`f7211b4e1874c453e37a7f190aa191c55a25c32e`
关键源码:
- `docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/gui/src/cm93.cpp`
- `docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52cnsy.cpp`
- `docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52plib.cpp`
- `docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52plib.h`
- `docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52utils.cpp`
- `docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52utils.h`
- 以及该快照中完整的 `libs/s52plib/src/`。
开始审计前先运行 SHA-256 校验,确认参考源码未被修改;若校验失败,停止并报告,不得继续以损坏快照作为基准。
## 4. 当前 CM95 对照范围
至少审阅以下当前实际实现:
- `src/cm95pbf/s52.py`
- `src/cm95pbf/csp.py`
- `src/cm95pbf/presentation.py`
- `tools/cm95_s52_style_compiler.py`
- `tools/cm95_s52_runtime_capability_audit.py`
- `config/s52/cm95_s52_runtime_capabilities_web_compare_v1.json`
- `web/kyushu_compare.html`
- 与上述文件直接相关的 lookup、resource、presentation、release audit 代码。
同时阅读已有文档,避免重复调查:
- `docs/报告/CM95_S52_OpenCPN渲染流程对照审计报告_v1.md`
- `docs/CM95_S52_语义守恒与运行时能力审计标准_v1.md`
- `docs/CM95_S52_静态物化与运行时呈现边界_v1.md`
- `docs/CM95_S52_NavSea运行时呈现契约_v1.md`
已有报告中的结论只能作为线索,最终判断必须重新落到 OpenCPN 固定源码和当前 CM95 实现。
## 5. 审计总流程
必须从 OpenCPN 正向建立以下链路,不允许从当前 PBF 或截图反推 OpenCPN:
`CM93 Object`
→ `CreateS57Obj / class normalization`
→ `S-52 Lookup`
→ `initial Rule chain`
→ `CS(...)`
→ `CSP branch`
→ `nested CSP calls`
→ `ordered instruction sequence`
→ `primitive opcode`
→ `resource dependency`
→ `mariner dependency`
→ `display category / priority / render order`
→ `renderer`
然后对照 CM95 对应链路:
`CM95 source`
→ `s52.py`
→ `csp.py`
→ `presentation.py / PBF runtime facts`
→ `style compiler`
→ `Web runtime`
→ `primitive consumer`
## 6. 第一批强制审计 CSP
按以下优先级执行,并建立完整调用关系,不得把它们当成孤立函数:
1. `OBSTRN04`
2. `WRECKS02`
3. `UDWHAZ03`
4. `SOUNDG03`
5. `LIGHTS05 / LIGHTS06`
6. `DEPARE01 / DEPARE02`
7. `DEPCNT02`
8. `SOUNDG02`
9. `QUAPOS01 / QUAPNT01 / QUALIN01`
10. `SLCONS03`
11. `RESTRN01`
12. `RESARE02`
注意:OpenCPN 当前源码中 `LIGHTS05()` 会进入 `LIGHTS06()`;审计必须以真实可达执行链为准,不能只按旧函数名或注释判断。
## 7. Gate A:OPENCPN_FLOW_EQUIVALENCE
先确认对象在进入 CSP 前已经走到了与 OpenCPN 等价的对象视图和 Lookup。
至少核对:
- CM93 class substitution;
- geometry type;
- S-57/S-52 object class;
- Lookup table 选择;
- initial instruction string;
- display category;
- display priority;
- point/boundary table 相关选择;
- SCAMIN / scale 相关入口;
- 任何会影响后续 CSP 选择的对象属性转换。
已知至少检查:
- `ITDARE → DEPARE`
- `SPOGRD → DMPGRD`
- `FSHHAV → FSHFAC`
- `OFSPRD → CTNARE`
- `$AREAS + $SCODE=II25 → BACKGROUND`
## 8. Gate B:CSP_BRANCH_EQUIVALENCE
对每个目标 CSP,从 OpenCPN 源码抽取完整 branch matrix。
每个 branch 至少记录:
- procedure;
- geometry;
- object class;
- attribute presence / value condition;
- mariner parameter condition;
- surrounding DEPARE/DRGARE 等关联对象条件;
- safety contour / safety depth 等阈值;
- 分支中调用的 nested CSP;
- 最终有序 instruction sequence;
- display category / priority side effect;
- resource dependency。
不能只记录“支持 OBSTRN04”或“支持 WRECKS02”。
如果 OpenCPN 某个分支是:
`SY(DANGER51) + SOUNDG03 + QUAPNT01 + TX(...)`
而当前实现只得到:
`SY(DANGER51) + SOUNDG03`
必须判定该 branch 为 FAIL。
## 9. Gate C:NESTED_CSP_CALL_GRAPH
建立第一批 CSP 的真实调用图。
重点确认:
- `OBSTRN04 → UDWHAZ03`
- `OBSTRN04 → SOUNDG03 / sounding formatting path`
- `OBSTRN04 → QUAPNT01`
- `WRECKS02 → UDWHAZ03`
- `WRECKS02 → SOUNDG03 / sounding formatting path`
- `WRECKS02 → QUAPNT01`
- `LIGHTS05 → LIGHTS06`
如果 nested CSP 的输出在 Python、PBF、style compiler 或 Web runtime 任意一层被截断,必须在调用图中标出具体断点。
## 10. Gate D:INSTRUCTION_SEQUENCE_EQUIVALENCE
OpenCPN 的审计单位是**完整、有序 Rule Chain**,不是单个 symbol code。
对每个 branch 比较:
`OpenCPN expected ordered sequence`
与:
`CM95 resolver / runtime actual ordered sequence`
至少识别并展开:
- `SY`
- `TX`
- `TE`
- `SOUNDG / sounding symbol chain`
- `CA`
- `AP`
- `AC`
- `LS`
- `LC`
- `OP`
- `CS`
需要区分:
- primitive 缺失;
- primitive 多出;
- 顺序变化;
- 参数变化;
- nested CSP 被扁平化后语义丢失;
- display side effect 丢失。
## 11. Gate E:RUNTIME_OPCODE_COVERAGE
对 release 实际出现且由 runtime 处理的 CSP,不能再只使用粗粒度 `RUNTIME_OBSTRN`、`RUNTIME_WRECKS` 等 capability 名称证明完整性。
必须从 branch expected sequence 逐 opcode 检查实际 consumer。
例如 OBSTRN04 某 branch 需要:
`SY + SOUNDG03 + QUAPNT01 + TX`
则必须分别回答:
- SY 谁执行;
- SOUNDG03 谁执行;
- QUAPNT01 谁执行;
- TX 谁执行;
- 参数来源是否与 OpenCPN 等价;
- 执行顺序是否守恒。
只实现其中一部分,整个 branch FAIL。
## 12. Gate F:REAL_DATA_BRANCH_COVERAGE
基于当前正式 Production Plan 与 release 数据扫描实际出现的条件组合。
当前已知正式基线:
- Builder:`cm95pbf-builder/0.4.12-opencpn-cm93-coverage`
- Production:`cm95pbf-production/2.3-opencpn-cm93-coverage`
- Plan:`build/production/cm95_s52_kyushu_production_plan_v4_opencpn_coverage.json`
- Plan ID:`57e72826db1d3e3e69f0b3b084bef4fb9469b5edc47dc0030fc2ec3e46adecf9`
- Release:`cm95-d1-62a85bd40d3a-57e72826db1d`
- Release root:`build/releases-s52-v2/cm95-d1-62a85bd40d3a-57e72826db1d`
如这些产物在当前机器不存在,记录为 evidence unavailable,不得伪造统计。
对实际数据生成 branch signature,至少覆盖与目标 CSP 相关的:
- geometry;
- object class;
- VALSOU present/absent/value band;
- WATLEV;
- EXPSOU;
- CATOBS / CATWRK 等类别属性;
- QUASOU / QUAPOS;
- TECSOU / STATUS;
- surrounding DRVAL1 / DRVAL2;
- safety contour / safety depth;
- isolated danger result;
- scale / display dependency。
每个真实出现的 branch signature 必须映射到 OpenCPN branch matrix,并标记当前 CM95 是否完整覆盖。
## 13. Gate G:RESOURCE_DEPENDENCY_EQUIVALENCE
对 branch 中引用的 `SY/AP/LC` 等资源逐项核对:
- OpenCPN 引用的资源名;
- CM95 resource universe 是否存在;
- style/runtime 是否能消费;
- 缺失时是否 fail-closed;
- 是否存在未经 OpenCPN 依据自行造出的 fallback。
已知 `DANGER53` 这类“代码可能引用但资源不存在”的情况必须单独记录为 upstream/resource anomaly,不能为了让审计 PASS 自行补造资源。
## 14. Gate H:MARINER_DEPENDENCY_EQUIVALENCE
至少核对:
- safety contour;
- safety depth;
- shallow contour;
- deep contour;
- symbolized boundaries;
- point table;
- boundary table;
- display category;
- SCAMIN;
- display scale;
- 其他会改变 CSP branch 或 primitive 参数的 mariner setting。
必须明确哪些值应保留到 runtime 才决定。
禁止把 mariner-dependent 最终表现提前写死进 PBF,再宣称与 OpenCPN 等价。
## 15. Gate I:RENDER_ORDER_EQUIVALENCE
检查 OpenCPN Rule Chain、display priority、display category、对象 geometry 与 MapLibre/style/runtime 实际绘制顺序之间是否存在语义变化。
至少识别:
- 同一对象多个 primitive 的相对顺序;
- fill / pattern / line / symbol / text 的先后关系;
- CSP side effect 导致的 DisplayBase promotion;
- runtime overlay 与静态 style layer 之间是否破坏 OpenCPN 的优先级语义。
如果当前架构无法证明严格顺序等价,应明确标记 UNKNOWN / NOT_PROVEN,不得默认 PASS。
## 16. 已知问题作为回归检查点
已有 v1 报告发现的以下问题必须重新从固定源码验证,不可直接抄结论:
- OBSTRN04 POINT 的 QUAPNT01;
- OBSTRN04 的 OBJNAM TX;
- OBSTRN04 LINE sounding;
- OBSTRN04 AREA sounding / QUAPNT01;
- isolated AREA 的 QUAPNT01;
- WRECKS02 POINT / AREA 的 QUAPNT01;
- WRECKS 的 `TX('Wk')` / `WRECKS07`;
- 当前 Web hazard depth text 与 OpenCPN SOUNDG03 glyph chain 的差异;
- LIGHTS05 实际进入 LIGHTS06。
## 17. 审计状态定义
每个 CSP / branch / opcode 至少使用以下状态之一:
- `PASS`:当前实现能够证明与固定 OpenCPN 基线等价。
- `PARTIAL`:只实现了部分 branch、部分 primitive 或近似表现。
- `FAIL`:存在明确缺失、错误条件、错误参数、错误顺序或错误 side effect。
- `NOT_IMPLEMENTED`:当前系统无对应实现。
- `NOT_PROVEN`:存在实现,但证据不足以证明流程等价。
- `NOT_OBSERVED_IN_REAL_DATA`:OpenCPN 代码存在该分支,但当前正式数据未观察到。
- `UPSTREAM_RESOURCE_MISSING`:OpenCPN 代码引用资源,但固定上游资源本身缺失;不得自行造资源后标 PASS。
不要用一个 CSP 的总体标签覆盖内部 branch 状态。
## 18. 必须输出的 branch 表
报告中第一批每个 CSP 至少给出一张表,列包含:
| 字段 | 含义 |
|---|---|
| branch_id | 稳定 branch 编号 |
| geometry | POINT / LINE / AREA 等 |
| condition | OpenCPN 分支条件 |
| mariner_dependency | 使用的 mariner 参数 |
| nested_csp | 调用的子 CSP |
| expected_sequence | OpenCPN 有序 instruction sequence |
| cm95_python | Python resolver 对应状态 |
| pbf_or_runtime_facts | 中间语义是否守恒 |
| style_or_runtime_consumer | 最终 consumer |
| real_data_count | 正式数据实际命中数 |
| status | PASS / PARTIAL / FAIL 等 |
| evidence | 文件、函数、行号或 artifact 证据 |
## 19. 必须输出的调用图
至少给出第一批 CSP 的调用图,明确:
- 顶层 CSP;
- nested CSP;
- 最终 primitive;
- mariner-dependent node;
- 当前 CM95 决策发生在哪一层;
- 语义在哪一层可能被截断。
调用图可以使用 Mermaid,但必须同时有文字说明,避免只靠图形表达关键结论。
## 20. 必须输出的架构结论
审计结束后必须回答:
1. 当前是否存在同一 OpenCPN branch condition 在 Python 与 JS 中重复手写;
2. 哪些 CSP 已经因此产生已确认的不等价;
3. 哪些 CSP 即使当前画面没有暴露问题,也存在相同结构风险;
4. 是否应建立统一的 OpenCPN-compatible CSP Decision IR / Runtime Rule IR;
5. 如果建立 IR,哪些字段必须保留到 runtime;
6. 哪些 opcode 可以由通用 executor 执行,哪些需要专用 renderer;
7. 哪些现有 PBF scalar hint 会压缩或丢失有序 Rule Chain。
## 21. 本轮正式输出
本轮只允许生成/更新审计资料,不修改产品实现。
必须输出:
1. `docs/报告/CM95_S52_OpenCPN渲染流程对照审计报告_v2.md`
- 在现有 v1 基础上升级;
- 以代码流程等价为主线;
- 包含 branch matrix、call graph、instruction sequence、runtime opcode coverage、real-data branch coverage 和明确缺口。
2. `docs/CM95_S52_语义守恒与运行时能力审计标准_v2.md`
- 在现有 v1 基础上升级;
- 正式加入:
- CSP_BRANCH_EQUIVALENCE
- NESTED_CSP_CALL_GRAPH
- INSTRUCTION_SEQUENCE_EQUIVALENCE
- RUNTIME_OPCODE_COVERAGE
- REAL_DATA_BRANCH_COVERAGE
- RESOURCE_DEPENDENCY_EQUIVALENCE
- MARINER_DEPENDENCY_EQUIVALENCE
- RENDER_ORDER_EQUIVALENCE
- 明确 Data Release PASS 与 Target Portrayal PASS 继续分离。
3. 机器可读审计产物:
`build/audits/cm95_s52_opencpn_code_first_equivalence_v2.json`
机器可读产物至少记录:
- OpenCPN commit;
- CM95 HEAD;
- Production Plan ID;
- CSP;
- branch_id;
- branch signature;
- expected ordered instructions;
- CM95 observed instructions / consumers;
- real_data_count;
- status;
- evidence paths;
- unresolved items。
如果当前机器缺少正式 release 数据,可允许 `real_data_count=null` 并记录 evidence unavailable,但代码流程审计仍必须完成。
## 22. 禁止事项
本轮禁止:
- 因发现缺陷直接修改 `src/cm95pbf/csp.py`;
- 修改 `web/kyushu_compare.html` 来让截图接近;
- 修改 runtime capability registry 把 FAIL 改成 PASS;
- 修改 style compiler 掩盖 opcode 缺口;
- 增加未经 OpenCPN 依据的 symbol fallback;
- 删除或弱化已有 release gate;
- 用人工截图作为 branch equivalence 的主要证据;
- 因当前真实数据没有命中某分支就删除该 OpenCPN 分支;
- 把“当前 Web 有一段文字/图标”当作完整 SOUNDG03/SY/TX executor 的证明。
## 23. 完成前必做对照清单
结束前逐项确认,不得静默跳过:
- [ ] 已校验仓库内 OpenCPN 固定源码快照 SHA-256。
- [ ] 已记录 CM95 HEAD 与工作区状态。
- [ ] 已从 OpenCPN 正向还原 Object → Lookup → CSP → Rule Chain → primitive 流程。
- [ ] 已完成第一批 12 组 CSP 的 branch matrix。
- [ ] 已建立 nested CSP call graph。
- [ ] 已逐 branch 比较完整有序 instruction sequence。
- [ ] 已检查 runtime opcode consumer,不再只看粗粒度 capability。
- [ ] 已扫描或明确说明无法扫描正式 real-data branch coverage。
- [ ] 已检查 resource dependency。
- [ ] 已检查 mariner dependency。
- [ ] 已检查 render order / DisplayBase 等 side effect。
- [ ] 已重新验证 v1 已知 OBSTRN/WRECKS/SOUNDG/LIGHTS 缺口。
- [ ] 已输出 v2 审计报告。
- [ ] 已输出 v2 审计标准。
- [ ] 已输出机器可读 JSON。
- [ ] 未修改任何产品功能代码。
- [ ] 未把 PREVIEW_ONLY / FAIL 人为改成 PASS。
- [ ] 未新增或修改 Jest 测试。
- [ ] 已逐项回看本任务文件,确认无必做项遗漏。
无法完成的项目必须在报告中明确写出原因、缺少的证据和后续需要什么,不得静默省略。
## 24. Git 收尾
由于当前仓库可能存在与本任务无关的未提交修改,必须严格隔离提交范围。
只允许提交本任务产生的:
- `docs/报告/CM95_S52_OpenCPN渲染流程对照审计报告_v2.md`
- `docs/CM95_S52_语义守恒与运行时能力审计标准_v2.md`
- `build/audits/cm95_s52_opencpn_code_first_equivalence_v2.json`
不得使用会把其他工作区修改一起带入的宽泛 `git add .`。
完成后:
1. 显式 stage 上述文件;
2. 检查 staged diff;
3. 提交到当前 `main`;
4. push 到 `origin/main`;
5. 在完成报告末尾记录 commit hash、commit message、push 结果;
6. push 失败时明确报告失败,不得声称远端已完成。
## 25. 最终判定原则
这次审计的目标不是让结果变成 PASS,而是让“不等价发生在哪里”可被机器和人准确定位。
如果当前实现只能证明部分等价,应保留 PARTIAL / FAIL / NOT_PROVEN。
截图只作为最后的视觉验证材料。真正的主证据必须是:
`OpenCPN source branch → expected Rule Chain → CM95 decision/facts → runtime primitive consumer`
只有这条链完整守恒,才允许将对应 branch 判为 PASS。