chore(task): archive 08-25-rc-p0-contract-baseline

This commit is contained in:
2026-08-25 16:53:04 +08:00
parent e7bc7f82b1
commit d7c2e13124
26 changed files with 792 additions and 3 deletions

View File

@@ -0,0 +1 @@
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}

View File

@@ -0,0 +1,81 @@
# 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 <area-config> --snapshot <out.json>
node scripts/road-parity.js --config <area-config> --compare <baseline.json>
```
### 归一化规则(写死在脚本里,并回写进契约文档)
1. **路径**:任何绝对路径 → 相对 repo rootstaging 目录名 → 字面量 `<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", "<staging>"]
}
```
`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 验证不需要等仓库拆分。

View File

@@ -0,0 +1 @@
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}

View File

@@ -0,0 +1,118 @@
# Phase 0 执行计划
## 前置
- [ ] 分支:`experiment/road-compiler-rethink`(已在)
- [ ] 确认工作区干净(`.trellis/tmp-*.js` 五个未跟踪文件与本任务无关,可先清理或忽略)
- [ ] 阅读父任务 `design.md` §1 契约定义
## Step 1 — 摸清两区域当前状态
```bash
ls config/areas/
for d in outputs/*/native-road/layers; do echo "$d: $(ls $d | wc -l)"; done
```
判定:`hanyang-block` 为废案,不纳入本阶段;只核查两个有效区域的配置与当前产物。
- [x] 逐区域检查 config 的 `nativeRoad.edgeLines` / `junctionTemplates.enabled`
- [x] 将两个有效区域的配置差异记录在基线 README
**门槛**:两个有效区域的图层差异有书面解释。
## Step 2 — 确定性核查
```bash
# 备份
cp -r outputs/<area>/native-road /tmp/rc-baseline-a
# 重编
rm -rf outputs/<area>/native-road
npm run road:compile -- --config config/areas/<area>.json
cp -r outputs/<area>/native-road /tmp/rc-baseline-b
# 比对
diff -r /tmp/rc-baseline-a /tmp/rc-baseline-b
```
- [x] 对两个有效区域各跑一遍
- [x] 记录所有差异字段 → volatile 清单
- [x] 额外一轮:删掉 `native-traffic-signals.json` 重生成,
确认信号 ID 是否确定(`traffic-signals.js` require 了 `crypto`,重点核查)
- [ ] 若发现**几何坐标级**的非确定性 → 停止本阶段,先开 bug 任务修掉
**门槛** 🔴:确定性已证明,或非确定性已定位并修复。这是全案的地基。
## Step 3 — 写 `scripts/road-parity.js`
`design.md` §2 实现。
- [x] `--snapshot` 生成归一化 checksum 清单
- [x] `--compare` 比对并逐文件报差异,不一致时非零退出
- [x] GeoJSON 双 hash`contentHash` 排序后 / `orderHash` 原序)
- [x] volatile 字段按 Step 2 结论归一化
- [x] 自验:同一区域连跑两次 snapshot`--compare` 必须通过
```bash
node scripts/road-parity.js --config config/areas/fengshu-er-road.json --snapshot /tmp/s1.json
node scripts/road-parity.js --config config/areas/fengshu-er-road.json --compare /tmp/s1.json
echo "exit=$?" # 必须为 0
```
**门槛**:脚本对未改动的代码报告一致。
## Step 4 — 建立两区域基线
```bash
mkdir -p .trellis/tasks/08-25-road-compiler-extraction/baseline
for a in fengshu-er-road nantaizi-lake-innovation-valley; do
rm -rf outputs/$a/native-road
npm run road:compile -- --config config/areas/$a.json
node scripts/road-parity.js --config config/areas/$a.json \
--snapshot .trellis/tasks/08-25-road-compiler-extraction/baseline/$a.json
done
```
- [x] 两个基线 JSON 生成
- [x]`baseline/README.md`:生成时的 git commit、命令、volatile 字段清单、
两个有效区域图层差异的解释
- [x] 反向自验:再跑一次 `--compare` 两个基线全部通过
## Step 5 — K2 决策
- [x] 检查 `comparison.json` 当前是否真被消费grep 宿主与 workbench
- [x] 若有 → 方案 B可选注入在契约里定义 `comparisonDir` 为 optional
## Step 6 — 落契约文档
- [x]`docs/native-road-package-v1.md`,内容源自父任务 `design.md` §1
- [x] 补入 Step 2 的 volatile 字段清单
- [x] 补入 Step 5 的 K2 决策
- [x]`docs/changelog.md` 记一行
## 验证命令汇总
```bash
# parity 两区域全绿
for a in fengshu-er-road nantaizi-lake-innovation-valley; do
node scripts/road-parity.js --config config/areas/$a.json \
--compare .trellis/tasks/08-25-road-compiler-extraction/baseline/$a.json || echo "FAIL $a"
done
# 既有测试未破
npm run test:native-road
npm run test:road-workbench
# 确认无生产代码改动
git diff --stat -- scripts/lib blender
```
最后一条必须为空输出 —— Phase 0 不碰生产代码。
## Review Gate
全部 AC0.1AC0.6 打勾后,再进 Phase 1。**基线不可靠时不要前进。**
## Rollback
本阶段纯新增(文档 / 脚本 / 基线数据)。回滚 = 删除新增文件。
唯一例外Step 2 若发现并修复了非确定性 bug那部分是真实代码改动
应拆成独立 commit 并单独保留。

View File

@@ -0,0 +1,84 @@
# Phase 0冻结契约与 parity 基线
父任务:`.trellis/tasks/08-25-road-compiler-extraction/`
契约权威定义:父任务 `design.md` §1
## Goal
在任何代码移动之前,把 `native-road-package/v1` 契约落成仓库内文档,
并为两个有效区域建立可复跑的逐文件 parity 基线,作为全案唯一 oracle。
**本阶段不移动、不重构任何生产代码。** 只新增文档、基线数据和一个校验脚本。
## Requirements
### R0.1 契约文档
- 把父任务 `design.md` §1输入契约 / 输出契约 / 消费方式)落成
`docs/native-road-package-v1.md`
- 文档必须列全 12 个图层名、`compiled.json` 顶层结构、`diagnostics.json` 记录形状、
stdout 标记格式。
- Phase 2 会把该文档搬进编译器仓库,此处先落在宿主。
### R0.2 确定性前置核查 🔴
**基线只有在编译器确定性的前提下才有意义。** 必须先证明这一点。
- 对同一输入连续跑两次 `road:compile`,比较全部输出文件。
- 识别并记录所有 volatile 字段时间戳、绝对路径、staging 目录名、
任何来自 `mkdtemp` 的随机名、Map/Set 迭代顺序敏感的输出)。
- 校验脚本必须归一化或排除这些字段。
- 若发现真实的非确定性(相同输入产出不同几何),**必须先修掉再继续**
否则整个拆分没有 oracle。
### R0.3 parity 校验脚本
- 新增 `scripts/road-parity.js`宿主侧Phase 2 随编译器搬走)。
- 能力:
- `--snapshot <out.json>` 遍历指定区域的 `native-road/` 全部文件 +
`native-traffic-signals.json`,归一化后输出排序稳定的 checksum 清单
- `--compare <baseline.json>` 与基线比对,差异逐文件报告
- 非零退出码表示不一致
- GeoJSON 需按稳定键排序后再 hash避免 feature 顺序抖动造成假阳性),
但**顺序本身若变化必须被报告**——用两个 hash`contentHash`(排序后)
`orderHash`(原序),分别报告。
### R0.4 两区域基线
- 区域:`fengshu-er-road``nantaizi-lake-innovation-valley`
- 从干净状态跑 `npm run road:compile`,产出基线 JSON 提交进版本控制
- 基线文件位置:`.trellis/tasks/08-25-road-compiler-extraction/baseline/<area>.json`
Phase 2 搬进编译器仓库当测试语料,见父任务 R4
`hanyang-block` 是废案,不纳入编译、快照或后续 parity 兼容范围。
### R0.5 K2 决策定档
`comparison.json` 依赖宿主 osm2streets 产物(`compile-native-roads.js:87`
`area.outputs.geojsonDir`),拆出后不可达。本阶段必须做出决策并写入契约文档:
- 方案 A移除 `comparison.json`osm2streets 已 legacy
- 方案 B保留`comparisonDir` 作为可选输入注入
- 倾向 A。决策写入 `docs/native-road-package-v1.md` 的「已移除能力」一节。
## Acceptance Criteria
- [x] AC0.1 `docs/native-road-package-v1.md` 存在,覆盖输入契约、输出契约、
12 图层清单、stdout 标记、已移除能力
- [x] AC0.2 连续两次编译的 parity 比对通过确定性已证明volatile 字段清单已记录在文档内
- [x] AC0.3 `scripts/road-parity.js` 可用,`--snapshot` / `--compare` 均工作,
不一致时非零退出
- [x] AC0.4 两个区域基线 JSON 已提交,且每个都能用 `--compare` 自比对通过
- [x] AC0.5 K2 决策已定档并写入契约文档
- [x] AC0.6 未修改任何生产代码(`git diff` 只含新增文档 / 脚本 / 基线)
## 依赖与顺序
- 无前置依赖,本阶段是全案入口
- **阻塞** Phase 1、2、3、4 —— 没有基线就没有 oracle
## Out of Scope
- 任何生产代码改动(含"顺手修一下"
- 目录结构调整
- `compiled.json` 结构变更

View File

@@ -0,0 +1,26 @@
{
"id": "rc-p0-contract-baseline",
"name": "rc-p0-contract-baseline",
"title": "Phase 0冻结契约与 parity 基线",
"description": "写死 native-road-package/v1 输入输出契约,并对两个有效区域建立逐文件 checksum 基线作为全案 oracle",
"status": "completed",
"dev_type": null,
"scope": null,
"package": null,
"priority": "P1",
"creator": "dingkang",
"assignee": "dingkang",
"createdAt": "2026-08-25",
"completedAt": "2026-08-25",
"branch": null,
"base_branch": "main",
"worktree_path": null,
"commit": null,
"pr_url": null,
"subtasks": [],
"children": [],
"parent": "08-25-road-compiler-extraction",
"relatedFiles": [],
"notes": "",
"meta": {}
}