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
@@ -271,3 +271,39 @@
|
||||
|
||||
本轮先用 `AppendRow()` 把**真实隐患**("某分支漏推一列")消除,
|
||||
完整结构合并留待"逐行几何基线"检查就绪后再做。
|
||||
|
||||
---
|
||||
|
||||
## 九、第 37 轮追加(2026-11-04):逐行几何基线(§13.6 第 1 项)
|
||||
|
||||
> 触发:用户要求「按照你的建议继续推进」。§13.6 第 1 项 = 逐行几何基线,
|
||||
> 它是 §13.3「完整结构合并」与「重写布局算式」的前置护栏。
|
||||
|
||||
| 节点 | 内容 | 验收标准 |
|
||||
| --- | --- | --- |
|
||||
| **P29** | 新增 `OffscreenChecks -run rowgeom`:把 7 个面板**每个矩形的每个字段**导出为文本基线,逐项比对 | `-mode record`/`-mode compare` 无差异;**篡改基线必须报错**(负向自检) |
|
||||
| **P30** | 修正长度不变式的判据:从"18 列全等"改为**按排版类别**判定 | `panels` / `panelscroll` 仍全绿,且对 `MARK` 面板不再误判 |
|
||||
|
||||
### 9.1 基线的两层覆盖
|
||||
|
||||
| 场景 | 数据 | 记录内容 | 用途 |
|
||||
| --- | --- | --- | --- |
|
||||
| **A:细节** | 前 6 条信号合并成 1 个网格,7 个面板全开、参数行展开、3 个固定点、折起/展开各一份 | 每行每列的精确矩形(`Temp/tools/rows-baseline/rowgeom-baseline.txt`,983 条键) | 定位级:一眼看出"哪一行哪一列偏了几个像素" |
|
||||
| **B:规模** | **全部**信号合并成 1 个网格(MF4 = 566 行) | 每个面板一个 FNV-1a 摘要哈希 + 行数/固定点行数 | 覆盖级:任何一行变动都会改哈希(不产生几十万行基线) |
|
||||
|
||||
### 9.2 本轮新增硬约束
|
||||
|
||||
1. **改面板几何(宽高算式 / 行距 / 内边距 / 折起规则)前后必须跑**
|
||||
`OffscreenChecks -run rowgeom -mode record|compare`。
|
||||
基线是**检测器**不是黄金图:变化是**预期**时要重新 record,并在提交信息里说明原因。
|
||||
2. **`rowRefs` 是重载的**:「列表式面板」用 `rowRefs` 表示"每个信号一行",
|
||||
而「自由标记面板」用它表示"每个信号一个分组",只填 `rowNameRects` / `rowNameCloses`,
|
||||
其余 14 列**必须为空**(真正的条目行在 `markerRows`)。因此任何"平行数组长度"不变式
|
||||
**必须按 `PanelStyle` 分类判定**,不能写"全部 18 列全等"。
|
||||
3. **`ExecuteToolbarAction(MERGE_LANES / SPLIT_LANES)` 之后、读 `LaneCount()` 之前,
|
||||
必须先 `SynchronizeView(true)`**:`SetMode()` 只重建 `TabConfig` 的网格配置,
|
||||
`PlotView::m_lanes` 要等下一次布局/绘制才刷新。
|
||||
4. 检查里**不要依赖"上一个场景的网格级状态还在"**:`Session::ShowSignals()` 先
|
||||
`SetAllVisible(false)`,而"全部不可见 ⇒ 丢弃网格"是既有设计
|
||||
(`RebuildLanes` 的 MERGED 分支),固定点/面板开关会随之清空 —— 该场景要**重新播**。
|
||||
|
||||
@@ -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 个面板像素哈希 + 几何逐项不变 |
|
||||
|
||||
@@ -555,3 +555,58 @@ Tools/README.md 目录结构、作用、测试项、验收标准、A/B
|
||||
可在 `AppendRow` 里保留但标注"仅为兼容保留",或在下一轮与文档一起清掉。
|
||||
3. **`PaintPanelListRows` 仍约 470 行**:可再按"条目栏 / 参数行 / 重采样行"拆三段,
|
||||
但当前已不在"改一次就出错"的路径上,优先级低于第 1 项。
|
||||
|
||||
---
|
||||
|
||||
## 十四、第 37 轮:逐行几何基线(§13.6 第 1 项)
|
||||
|
||||
### 14.1 结论
|
||||
|
||||
| §13.6 建议 | 结果 |
|
||||
| --- | --- |
|
||||
| 1. 逐行几何基线 | ✅ 新增 `OffscreenChecks -run rowgeom`(`record` / `compare`),并做了**负向自检**证明它能抓回归 |
|
||||
| 2. `rowPoints` / `rowIterations` 清理 | ⏸ 未做(本轮与该建议无耦合,留给下一轮) |
|
||||
| 3. `PaintPanelListRows` 再拆 | ⏸ 未做(纯可读性,优先级低) |
|
||||
|
||||
`-all` 从 6 个检查 / 101 条断言 → **7 个检查 / 127 条断言**,全绿。
|
||||
|
||||
### 14.2 基线的两层设计(为什么不是"一行一个文件")
|
||||
|
||||
| 层 | 场景 | 记录 | 键数 | 用途 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 细节 | 6 信号 1 网格、7 面板全开、参数行展开、3 个固定点、折起/展开各一份 | 每个矩形的每个字段 | 983(MF4 上 1304) | **定位**:直接看出哪一行哪一列偏了几像素 |
|
||||
| 规模 | 全部信号 1 网格(MF4 = 566 行) | 每面板一个 FNV-1a 摘要 + 行数 | 7 | **覆盖**:任何一行变动都改哈希,且不产生几十万行基线 |
|
||||
|
||||
两层互补:哈希层保证"改动一定被看见",明细层保证"看见了能定位"。
|
||||
|
||||
### 14.3 这次护栏暴露的两个结构事实
|
||||
|
||||
1. **`rowRefs` 是重载的字段**。
|
||||
「列表式」面板里 `rowRefs[i]` = 第 i 行的信号;
|
||||
「自由标记」面板里 `rowRefs[i]` = 第 i 个**信号分组**,而它真正的条目行在 `markerRows`,
|
||||
且只填 `rowNameRects` / `rowNameCloses`,另外 14 列**必须为空**。
|
||||
⇒ 第 36 轮定的"18 列全等"不变式对 `MARK` 是**错的**,本轮改为按 `PanelStyle` 分流判定。
|
||||
同一个"字段重载"也让导出器必须**逐列按自身长度**遍历(否则越界崩溃 —— 这正是本轮第一次
|
||||
`0xC0000005` 的来源)。
|
||||
2. **`SetMode()` 不同步 `m_lanes`**。`ExecuteToolbarAction(MERGE_LANES/SPLIT_LANES)` 之后
|
||||
`TabConfig` 的网格配置已是新的,但 `PlotView::m_lanes` 要等下一次布局/绘制才刷新 ——
|
||||
任何"合并后立刻读 `LaneCount()`"的代码都必须先 `SynchronizeView(true)`。
|
||||
|
||||
### 14.4 一个被误判成缺陷的既有行为
|
||||
|
||||
场景 B 一开始 `mark` 面板是空行:`Session::ShowSignals()` **先** `SetAllVisible(false)`,
|
||||
而"全部不可见 ⇒ 丢弃网格"是 `RebuildLanes` 的既有设计(与工具栏「清空」一致),
|
||||
网格级状态(固定点 / 面板开关 / 滚动位置)随之清空。⇒ 不是新缺陷;
|
||||
检查改为在该场景**重新播**固定点,顺带把规模覆盖补全(`rows=2 markerRows=3`)。
|
||||
|
||||
### 14.5 下一步(按价值排序)
|
||||
|
||||
1. **`rowPoints` / `rowIterations` 清理**(确认已无绘制方使用,仅 `BuildBoxes` / 命中键残留):
|
||||
这是当前唯一"写了没人读"的列,删掉能减少每次 `AppendRow` 的 2 次 push 与 2 个 vector。
|
||||
判据:`rowgeom -mode compare`(细节基线会逐项证明几何没变)+ `panels -mode compare`。
|
||||
2. **完整结构合并(`std::vector<PanelRow>` + 列视图)**:前置护栏(本轮)已就绪,
|
||||
但 §13.3 的**失败可见性**问题仍未解决 —— 列视图拷贝后忘记重绑是静默悬垂。
|
||||
若要做,必须同时给 `EdgePanelLayout` 加"视图自检"(例如保存 `rows` 容量并与视图长度比对),
|
||||
否则不建议动。
|
||||
3. `PaintPanelListRows` 再拆三段(纯可读性)。
|
||||
|
||||
@@ -10564,3 +10564,77 @@ release/debug **零警告**;Release `TargetCheck.exe` **890,880 B**(< 1 MB
|
||||
* **逐行几何基线**(导出每行 18 个矩形做逐项比对)—— 完整结构合并与布局算式重写的前置护栏。
|
||||
* `rowPoints` / `rowIterations` 已无绘制方使用(旧式输入框排版早已删除),可标注/清理。
|
||||
* `PaintPanelListRows` 仍约 470 行,可再拆三段,但已不在"改一次就出错"的路径上。
|
||||
|
||||
---
|
||||
|
||||
## 2026-11-04 · 第 37 轮(`Code_Review` 分支):逐行几何基线(§13.6 第 1 项)
|
||||
|
||||
> 提交:见本轮提交信息;规则原文 `Documents/Code_Review_Report.md` §14、`Code_Review_Plan.md` §九、
|
||||
> `Code_Review_Progress.md` 第 37 轮、`Tools/README.md` §4.1/§4.3/§4.4。
|
||||
|
||||
### 1. ✅ 新增 `OffscreenChecks -run rowgeom`(`record` / `compare`)
|
||||
|
||||
两层覆盖:
|
||||
* **细节层**(6 信号 1 网格、7 面板全开、参数行展开、3 个固定点、折起+展开各一份):
|
||||
每个矩形的每个字段一条键,`Temp/tools/rows-baseline/rowgeom-baseline.txt` 共 **983** 条
|
||||
(`-data MF4-Record.MF4` 时 1304 条)。
|
||||
* **规模层**(全部信号 1 网格,MF4 = 566 行):每面板一个 FNV-1a 摘要 + 行数,7 条键。
|
||||
|
||||
键格式:`<panel>.<field>` / `<panel>.<列名>[NNN]=x,y,w,h` / `mark.markerGroup[NNN].header=…`。
|
||||
比对双向(MISSING / ADDED / CHANGED),差异 0 才 PASS。
|
||||
|
||||
### 2. 🔴 新硬规则一:`rowRefs` 是**重载字段**,长度不变式必须按类别判定
|
||||
|
||||
| 面板类别 | `rowRefs` 含义 | 真正的行在 |
|
||||
| --- | --- | --- |
|
||||
| 列表式(mean/center/peak/resample/clip) | 每个信号一行 | `rowRefs` 自身 + 其余 15 列 |
|
||||
| 边沿计数(count) | 每个信号一行(表格行) | 同上 |
|
||||
| 「自由标记」(mark) | 每个**信号分组**一项 | `markerRows` / `markerGroups` |
|
||||
|
||||
MARK 面板**只填** `rowNameRects` / `rowNameCloses`,其余 14 列**必须为空**。
|
||||
⇒ 第 36 轮定的"18 列全部等于行数"对 MARK 是**错的**,本轮已改为:
|
||||
|
||||
* `PanelStyle::markerGroups == true` ⇒ `rowNameRects`/`rowNameCloses` 等于分组数,
|
||||
且 `rowHeaders`/`rowChecks`/`rowLevelTracks`/`rowIterTracks` 必须为空;
|
||||
* 其余面板 ⇒ `rowRefs` + 16 列全部等于行数。
|
||||
|
||||
任何"遍历 18 列"的代码(导出器、不变式、批量处理)**都必须逐列按自身长度**遍历,
|
||||
不能拿 `rowRefs.size()` 当统一行数(否则 MARK 面板直接越界 —— 本轮第一次 `0xC0000005`)。
|
||||
|
||||
### 3. 🔴 新硬规则二:`SetMode()` 不同步 `m_lanes`
|
||||
|
||||
`ExecuteToolbarAction(MERGE_LANES / SPLIT_LANES)`(以及 `TabConfig::SetMode`)只重建
|
||||
`TabConfig` 的网格配置,`PlotView::m_lanes` 要等下一次布局/绘制才刷新。
|
||||
⇒ **合并/拆分后、读 `LaneCount()` 或 `EdgePanelLayoutFor()` 之前,必须先
|
||||
`app.SynchronizeView(true)`(必要时再 `RenderFrame()`)**。
|
||||
|
||||
### 4. ⚠️ 已知行为(不是缺陷):全部不可见 ⇒ 丢弃网格 ⇒ 网格级状态清空
|
||||
|
||||
`Session::ShowSignals()` 先 `SetAllVisible(false)`;`RebuildLanes` 的 MERGED 分支在
|
||||
"无可见信号"时 `lanes.clear()`(与工具栏「清空」一致)。
|
||||
⇒ 固定点 / 面板开关 / 面板滚动位置 / 面板显隐 **全部丢失**,不是新 BUG。
|
||||
检查里跨场景依赖这些状态时,必须**重新播**(`rowgeom` 用 `addFreeMarkers(wanted)` lambda)。
|
||||
|
||||
### 5. 基线的定位
|
||||
|
||||
**检测器,不是黄金图。** 预期内的几何变化要重新 `-mode record`,并在提交信息里写明原因。
|
||||
负向自检已做:篡改基线中 `mark.markerRowCount=3 → 2`,`-mode compare` 会
|
||||
`CHANGED … mismatches=1` 并 FAIL ⇒ 护栏不是空转。
|
||||
|
||||
### 6. 本轮数字
|
||||
|
||||
* Release **785,408 B 不变**(本轮只动工具/检查与文档);零 warning。
|
||||
* `UnitTests` 260 断言 / 19 用例;`OffscreenChecks -all` **7 检查 / 127 断言**(原 6/101)。
|
||||
* `rowgeom -mode compare`:默认数据 983+7 键、MF4 1304+7 键,0 差异。
|
||||
* `panels -mode compare` 7 个面板像素哈希 + 几何逐项不变。
|
||||
|
||||
### 7. 遗留(下一步候选,按价值)
|
||||
|
||||
1. **`rowPoints` / `rowIterations` 清理**(确认已无绘制方使用,仅 `BuildBoxes` / 命中键残留)——
|
||||
当前唯一"写了没人读"的列,删掉可省每次 `AppendRow` 的 2 次 push。
|
||||
判据:`rowgeom -mode compare` + `panels -mode compare`。
|
||||
2. **完整结构合并(`std::vector<PanelRow>` + 列视图)**:护栏已就绪,但 §13.3 的
|
||||
**失败可见性**问题未解(拷贝后忘重绑 = 静默悬垂)。若要做,必须同时给
|
||||
`EdgePanelLayout` 加"视图自检"(比对 `rows` 容量与视图长度)。
|
||||
3. `PaintPanelListRows` 再拆三段(纯可读性)。
|
||||
|
||||
Reference in new issue
Block a user