test(tools): rowgeom 逐行几何基线 + 长度不变式按类别判定
新增 `OffscreenChecks -run rowgeom`(§13.6 第 1 项),把 7 个面板的每个矩形的 每个字段导出为文本基线并逐项比对,作为"完整结构合并 / 重写布局算式"的前置护栏。 两层覆盖 - 细节层:6 信号 1 网格、7 面板全开、参数行展开、3 个真实固定点、折起+展开各一份, 983 条键(-data MF4-Record.MF4 时 1304 条),失败时直接给出字段名+行号+新旧值; - 规模层:全部信号合并成 1 个网格(MF4 = 566 行),每面板一个 FNV-1a 摘要 + 行数, 任何一行变动都会改哈希,同时不产生几十万行基线。 负向自检(证明护栏不是空转) - 篡改基线副本 `mark.markerRowCount=3 → 2` ⇒ `CHANGED … mismatches=1` + FAIL。 顺带修正第 36 轮定得过严的不变式 - `rowRefs` 是重载字段:MARK 面板用它表示"信号分组",真正的条目行在 markerRows, 且只填 rowNameRects / rowNameCloses,其余 14 列必须为空; ⇒ `panels` / `panelscroll` 的"18 列全等"改为按 `PanelStyle::markerGroups` 分流判定, 失败时打印 15 个计数字段便于定位。 - 导出器/不变式一律**逐列按自身长度**遍历(按 rowRefs.size() 索引其它列会越界, 这是本轮第一次 0xC0000005 的根因)。 测试用例里的两处修正 - `MERGE_LANES` 之后补 `SynchronizeView(true)`:`SetMode` 只重建 TabConfig 的网格配置, `PlotView::m_lanes` 要等下一次布局才刷新; - 场景 B 重新播固定点:`ShowSignals()` 先 `SetAllVisible(false)`,而"全部不可见 ⇒ 丢弃网格" 是 RebuildLanes 的既有设计,网格级状态随之清空(不是缺陷),因此不能跨场景依赖。 验证 - make release:零 warning,785,408 B 不变(本轮只动工具/检查与文档) - make -C Tools clean && make -C Tools:零 warning - UnitTests:checks=260 failed=0 cases=19 - OffscreenChecks -all:7 检查 / 127 断言全 PASS(原 6 / 101) - rowgeom -mode compare:默认数据 983+7 键、MF4 1304+7 键,0 差异 - panels -mode compare:7 个面板像素哈希 + 几何逐项不变 文档:Code_Review_Plan §九、Progress 第 37 轮、Report §14、agent_mem 第 37 轮、Tools/README §4.1/4.3/4.4
This commit is contained in:
1 parent
221f9d2410
commit
353c553721
6 files changed
+821
-48
No files matched your search
@@ -410,3 +410,84 @@ Makefile 按目录通配收集源文件,**无需登记新文件**。
|
||||
| `OffscreenChecks -all` | 6/6 PASS,`checks=101 failed=0`(断言合并后略减) |
|
||||
| `panels -mode compare` | 7 个面板像素哈希 + 几何**逐项不变**(两次重构全程守住) |
|
||||
| `panelscroll`(MF4,566 行) | 18 个行数组长度全部 = 566,行为不变 |
|
||||
|
||||
---
|
||||
|
||||
## 第 37 轮(2026-11-04):逐行几何基线(§13.6 第 1 项)
|
||||
|
||||
### P29 新增 `rowgeom` 检查 —— ✅ 完成
|
||||
|
||||
`Tools/OffscreenChecks/Src/Checks.cpp` 新增注册键 `rowgeom`,做法:
|
||||
|
||||
1. **场景 A(细节基线)**:切双光标 → 前 6 条信号 → `MERGE_LANES` → 7 个面板全开、
|
||||
第 2 条信号勾选(同一面板里同时有"展开的参数行"与"未勾选的行")→ 加 3 个真实固定点
|
||||
→ 导出 7 个面板 **展开态**全字段 → 全部折起再导一份 → 复原。
|
||||
2. **场景 B(规模基线)**:全部信号合并成 1 个网格(默认数据 3 行;`-data MF4-Record.MF4` = 566 行)
|
||||
→ 每面板一个 FNV-1a 摘要 + `rows` / `markerRows` 计数。
|
||||
3. **导出格式**(纯文本、逐行 `键=值`,便于 `git diff` 与人工定位):
|
||||
|
||||
```text
|
||||
mean.rowRefs.count=3
|
||||
mean.rowNameRects[001]=1155.75,221.25,512.50,25.00
|
||||
mark.markerGroupCount=2
|
||||
mark.markerGroup[000].header=1155.75,171.25,512.50,27.50
|
||||
mark.markerRow[000].item=0/1
|
||||
mark.markerRowCount=3
|
||||
```
|
||||
|
||||
⚠️ 因为 `rowRefs` 是**重载**的(见下),导出器按**每一列自己的长度**遍历,
|
||||
不能"取 `rowRefs.size()` 当行数统一遍历全部 18 列"。
|
||||
4. **`-mode record` / `-mode compare`**:比对双向(缺键 = MISSING,多键 = ADDED,值变 = CHANGED),
|
||||
最多打印 20 条差异;差异数为 0 才算 PASS。
|
||||
|
||||
### 过程中两次崩溃(都已定位,结论写进 `agent_mem.md`)
|
||||
|
||||
| # | 现象 | 根因 | 处置 |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 | `LaneCount()` 断言失败 | `SetMode(MERGED)` 只重建 `TabConfig` 的网格配置,`PlotView::m_lanes` 要等下一次布局/绘制才刷新 | `MERGE_LANES` 之后补 `SynchronizeView(true)` + `RenderFrame()` 再断言 |
|
||||
| 2 | 0xC0000005(导出时越界) | 假设"18 个行数组等长",但 **`MARK` 面板复用 `rowRefs` 表示信号分组**,只填 `rowNameRects` / `rowNameCloses`,其余 14 列为空 ⇒ 按 `rowRefs.size()` 索引其它列直接越界 | 导出器改为**逐列按自身长度**遍历 |
|
||||
|
||||
### P30 长度不变式的判据修正 —— ✅ 完成
|
||||
|
||||
`panels` / `panelscroll` 里第 36 轮定的"18 列必须全部等于行数"对 `MARK` 面板**是错的**
|
||||
(它对新结构是误判,只是既有场景恰好没触发)。改为按 `PanelStyleOf(kind).markerGroups` 分流:
|
||||
|
||||
* `markerGroups == true`:`rowNameRects` / `rowNameCloses` 等于分组数,
|
||||
且 `rowHeaders` / `rowChecks` / `rowLevelTracks` / `rowIterTracks` **必须为空**;
|
||||
* 其余面板:17 列(`rowRefs` + `rowHeaders` … `rowFrequencyField`)全部等于行数。
|
||||
|
||||
失败时打印 15 个计数字段,便于一眼看出是哪一列错了。
|
||||
|
||||
### 基线是否真的能抓回归(负向自检)
|
||||
|
||||
把基线副本里 `mark.markerRowCount=3` 改成 `2` 再跑 `-mode compare`:
|
||||
|
||||
```text
|
||||
[rowgeom detail] CHANGED mark.markerRowCount: baseline=2 current=3
|
||||
[FAIL] rowgeom detail geometry is unchanged
|
||||
SUMMARY checks=32 failed=1 groups=1 result=FAIL
|
||||
```
|
||||
|
||||
⇒ 检测器**不是空转**的。(正式基线所在目录始终是 `record` 生成的,未被改动。)
|
||||
|
||||
### 一个"顺手查清"的行为(不是缺陷)
|
||||
|
||||
`rowgeom` 第一次跑出 `mark.hash … rows=0`:场景 B 里固定点没了。追查结论:
|
||||
`Session::ShowSignals()` 先 `SetAllVisible(false)`,而"全部不可见 ⇒ 丢弃网格"
|
||||
是 `RebuildLanes` 的**既有设计**(MERGED 分支的 `lanes.clear()`),
|
||||
网格级状态(固定点 / 面板开关 / 滚动位置)随之清空 —— 与 `CLEAR_ALL` 行为一致,**不是新缺陷**。
|
||||
因此场景 B 改为**重新播**固定点(`addFreeMarkers(2)` lambda),
|
||||
场景 B 的 MARK 面板现在是 `rows=2 markerRows=3`,规模覆盖也补上了。
|
||||
|
||||
### 本轮验证
|
||||
|
||||
| 项 | 结果 |
|
||||
| --- | --- |
|
||||
| `make release`(全量) | 零 error / 零 warning;产物 **785,408 B 不变**(本轮只动工具与检查) |
|
||||
| `make -C Tools` | 零 error / 零 warning |
|
||||
| `UnitTests` | `checks=260 failed=0 cases=19` |
|
||||
| `OffscreenChecks -all` | **7/7 PASS**,`checks=127 failed=0`(第 36 轮为 6/6、101) |
|
||||
| `rowgeom -mode compare`(默认数据) | `detail 983 键 + scale 7 键`,0 差异 |
|
||||
| `rowgeom -mode compare -data MF4-Record.MF4` | `detail 1304 键 + scale 7 键`(566 行),0 差异 |
|
||||
| `panels -mode compare` | 7 个面板像素哈希 + 几何逐项不变 |
|
||||
|
||||
Reference in new issue
Block a user