# Phase 0 技术设计 契约本体见父任务 `design.md` §1,本文件只补 Phase 0 自身的实现设计。 ## 1. 确定性核查的具体做法 编译器的潜在非确定性来源,按可疑度排序: | 来源 | 表现 | 处置 | |---|---|---| | `fs.mkdtempSync(path.join(pipelineDir, "native-road-"))` | staging 目录名随机 | 若名字出现在 `compiled.json` 的 `source` 字段里,需归一化 | | `compiled.json` 内的绝对路径 | `source.osm` / `source.overrides` / `source.trafficSignals` 是绝对路径 | 归一化为相对 repo root | | 时间戳 | 未确认是否存在 | 核查 `compile-native-roads.js` 写入的 metadata | | `Map` / `Set` 迭代顺序 | JS 保证插入序,若插入源本身有序则确定 | 观测两次运行 `orderHash` 是否一致即可暴露 | | 浮点累加顺序 | 几何坐标末位抖动 | 若出现,说明存在顺序依赖,属真 bug,需修 | | `crypto` 随机(`traffic-signals.js` require 了 `crypto`) | 信号 ID 可能含随机成分 | 重点核查 —— `native-traffic-signals.json` 是 `loadOrGenerate`,已存在则复用,首次生成可能不确定 | **核查顺序**: 1. 备份两个有效区域的 `native-road/` 与 `native-traffic-signals.json` 2. 删除 `native-road/`(保留 signals,因为 `loadOrGenerate` 语义是已存在则复用) 3. 跑第一次 → snapshot A 4. 再删 `native-road/`,跑第二次 → snapshot B 5. diff A/B → volatile 字段清单 6. 额外一轮:删掉 signals 文件也重生成一次,确认 signal ID 是否确定 ## 2. `scripts/road-parity.js` 设计 ``` 用法: node scripts/road-parity.js --config --snapshot node scripts/road-parity.js --config --compare ``` ### 归一化规则(写死在脚本里,并回写进契约文档) 1. **路径**:任何绝对路径 → 相对 repo root;staging 目录名 → 字面量 `` 2. **JSON**:递归按 key 排序后 `JSON.stringify`,再 hash 3. **GeoJSON**: - `contentHash` = features 按 `properties.native_id`(缺失则按几何首坐标)排序后 hash - `orderHash` = 原序 hash - 两者都记录;比对时 `contentHash` 不一致 = 语义变化(严重), 仅 `orderHash` 不一致 = 顺序抖动(需解释但可能可接受) 4. **浮点**:不做舍入。坐标变化就是变化,不允许用容差掩盖 (拆分是纯搬迁,任何坐标位移都是 bug) ### 输出形状 ```json { "contract": "native-road-package/v1", "areaId": "fengshu-er-road", "files": { "compiled.json": { "contentHash": "…", "bytes": 12345 }, "diagnostics.json": { "contentHash": "…", "bytes": 678, "count": 42 }, "layers/road_surface.geojson": { "contentHash": "…", "orderHash": "…", "features": 128 } }, "volatileExcluded": ["source.osm", "source.overrides", ""] } ``` `count` / `features` 是冗余的人类可读字段 —— hash 变了但计数没变, 说明是内容变化;计数也变了,说明是增删。加速归因。 ## 3. 基线目录布局 ``` .trellis/tasks/08-25-road-compiler-extraction/baseline/ fengshu-er-road.json nantaizi-lake-innovation-valley.json README.md ← 记录生成时的 git commit、命令、volatile 字段清单 ``` `README.md` 必须记录生成基线时的 commit hash,否则将来无法判断 "基线是在哪个代码状态下产生的"。 ## 4. 为什么校验脚本放宿主而不是直接放未来的编译器仓库 Phase 0 时编译器仓库还不存在。脚本先落宿主 `scripts/`, Phase 2 随编译器一起搬走,届时宿主保留一个薄封装(或直接调编译器包的 CLI)。 这样 Phase 1 的 parity 验证不需要等仓库拆分。