Commit Graph

3 Commits

Author SHA1 Message Date
a47b42b36e 修复: 套件读数不再取决于"你怎么调用它"—— 子进程钉 cwd: ROOT + read.mjs 相对路径按包目录解析(第二层);并把"崩了"从 red 里分出来(结构判据,不加错误关键字)
pi 2026-09-17 实测报的:**同一份代码、两种调用法、两个结论**。我复现并修了两层。

## 一、复现(同 HEAD,只换 cwd)

| 调用法 | 修前 RESULT |
|---|---|
| `cd client/electron && node test/run-all.mjs`(`npm test` 入口)| `ran=29 checks=440 red=8 unreported=0` |
| `node client/electron/test/run-all.mjs`(仓库根)| `ran=27 checks=348 red=10 unreported=2` |

差 **92 条**(= 88 + 4),红 8 → 10。两次都稳定复现。

**根因**:`run-all.mjs` 的 `spawnSync` **不传 `cwd`** ⇒ 子进程继承调用者的 cwd。
而全仓有 **12 处**判据按**包内相对路径**读文件(`code('src/index.css')` 这种)
—— 它们本来就假定 `cwd = client/electron`,只是没人把那个假定钉住。
从仓库根跑 ⇒ `ENOENT` **崩在 import 期**。

## 二、修(两层,各管一段)

1. **运行器**:`spawnSync(..., { cwd: ROOT })` —— 一行覆盖**现有的与将来的所有**判据。
   方向是"让运行器去满足判据的既有假定",不是改判据去迁就调用者。
2. **`read.mjs`**(第二层):`code/prose/bytes` 里的相对路径一律按**包目录**解析
   (基准取自 `import.meta.url`,**不是** `process.cwd()` —— 前者是代码里的结构事实,
   后者正是这个 bug 的成因)。pi 报的复现步骤是**直接跑单个判据文件**,
   那条路**不经过运行器**,所以这层不是重复。
   基准 `PKG` 也**导出**了:`readdirSync`/`existsSync` 不经过 `read.mjs`,
   各处自己拼一次就是"同一个事实多份实现"(本仓反复消的形状)。

**验证(三种 cwd × 两个相位,逐字节一致)**:

```
build   /home/program/agentmail          ran=29 checks=440 red=8 unreported=0
build   /home/program/agentmail/client/electron  同上
build   /                                同上
install /home/program/agentmail          ran=27 checks=428 red=7 unreported=0
install /home/program/agentmail/client/electron  同上
```
(三种 cwd 一致、且与 `npm test` 入口一致 ⇒ 不存在"另一种调用法给别的数"。)

## 三、★ 第二处:那两条不是"红了",是"没跑完"——而它被报成了 red

`narrow-layout` 从仓库根跑:**先打完 43 条"通过",然后第 44 条 ENOENT 崩掉**,
而 `CRASH_SIGNS`(关键字表)**不认 ENOENT** ⇒ `crashed=false` ⇒ 落进 `reds`:
`- test/narrow-layout.test.mjs(退出码 1)`,**一条 `↳` 自述都没有**。
读者看到的是"这条判据不成立",真相是"**它有多少条根本没测**"。
同一次运行里真红是带 `↳ 自报 88 > 登记 64` 自述的 —— **同一种红,两种含义**,
而 `red=10` 把两者混着报。

**★ 我没有照 pi 说的往 `CRASH_SIGNS` 里加 `ENOENT|EACCES|no such file`**:
第 329 行那条注释早把方向定死了 —— **"我不再往里加更多错误关键字:那是往文本解析里加补丁"**,
而且那个表**必然漏**(今天漏 ENOENT,明天漏 ENOTDIR/ELOOP…),加上去就是**第六次**修同一形状。

**改用结构事实**:`exitCode !== 0 && checks === null` ⇒ broken。
一个跑完的文件**必然**自报条数;"跑了但没自报"是另一条**单独记账**的情形(`unreported`)。
两者不会同时成立 ⇒ 只吃掉"崩了且毫无自报"那一格。
**要害是这个区分**:`exitCode !== 0` 本身**不足以**判 broken ——
不然真红会被一起吞掉,那是**假绿方向的错**,比误报成红更坏。
(实测 `build-stamp` 是真红且有 `# tests 7` ⇒ `checks=7≠null` ⇒ **不吃它** ✓。)

**变异验证**(撤掉 `cwd: ROOT`,模拟 pi 报的原状):
`files=29 ran=27 checks=348 red=8 broken=2 unreported=2` ⇒
那两条正确归 **broken**(`(退出码 1,且一条条数都没自报)`),而 **`red` 仍是 8 —— 没吃掉任何真红**。
还原逐字节一致(sha256 相同)。

## 四、★ 我自己在实现里踩的两个坑(都实测抓到)

1. **`abspath()` 用 `isAbsolute(p)` 判绝对路径 ⇒ 传 `URL` 对象抛 `ERR_INVALID_ARG_TYPE`。**
   `narrow-layout.test.mjs:8` 正是 `prose(new URL(p, import.meta.url))` ——
   一个**本来正确且与 cwd 无关**的调用,被我改崩了(两个 cwd 都崩,所以**看起来像"我修好了"**)。
   ⇒ 判据改成 `URL` 对象**原样透传**:它已是解析过的位置,转换才是引入 bug 的那一步。
   教训:**改一个共用入口时,要先把调用方的所有形态列全**,否则"修 A 破 B",而 B 看起来无关。
2. **`join` 忘了 import** ⇒ `ReferenceError`。补上。
   (正是 `run-all.mjs` 里那条 `CRASH_SIGNS` 注释讲的老故事。)

另外:我在 `run-all.mjs` 新注释里写了带括号的入口名,**被 `criteria-hygiene` 第 3 条抓到**
(它不剥注释)—— 这是我上一轮刚记下的坑的**第二次**,已改掉。

## 五、测量

| 相位 | RESULT(三种 cwd 一致)|
|---|---|
| build | `files=29 ran=29 checks=440 pass=438 fail=2 red=8 broken=0 unreported=0` |
| install | `files=29 ran=27 checks=428 pass=427 fail=1 red=7 broken=0 unreported=0` |

重新接线后 `narrow-layout` 自报 **88** 条(登记 64)⇒ 它那条"登记数没跟上"的红变成**有自述**的了。
剩下 8 条红都不是本次改动(清一色并发会话的工作:`harmony-nav` 正在被改等)。
2026-09-17 19:00:11 +08:00
f14f2d6fa7 chore: 动画盘点判据接入套件 + 让其合规 + 对齐参照物重新登记(清工程收尾)
用户:「清理一下tmp和工程吧」。

- 新判据 test/animation-audit.test.mjs 接入 test/run-all.mjs:原先**写了却不会跑**
  (套件自带的那条闸门当场报「这些判据文件没接进套件」)。
- 该文件改用 test/lib/read.mjs 的具名入口(code/prose/bytes),不再裸用 readFileSync ——
  criteria-hygiene 抓到:读原文判代码会被解释性注释骗,今天已踩过两次。
- docs/ALIGN-REFS.json:CalendarView 的对齐登记按规矩**重新核对后再登记**
  (差异只有动画类 rise-in → pane-rise,骨架/布局/圆角来源未变;鸿蒙侧本无日历动效
  ⇒ 不产生新的对齐义务),不是抄一处新哈希。
2026-09-15 09:16:05 +08:00
d81ab319e4 fix(webui): 动画全量盘点 —— 两处死动画 + 一处过宽;并把盘点变成常驻判据
用户:「全面检查整体的动画」。查出来的不是"好不好看",而是**接线断了**(三种形态都很难靠肉眼发现):

① @keyframes pane-in **定义了两处**、挂在 `html.view-switch .pane-enter` 上,而 `.pane-enter`
   **没有任何组件在穿** ⇒ 规则看着像"页面有入场动画",一次都不会播(9aa702b 删整面板入场时的遗留)。
   已连规则与 keyframes 一起删干净(注释里我把"连它一起删"写了却没做,被新判据当场抓到)。
② `.animate-menu-in`(菜单入场)keyframes 与规则都写好了,**同样没人穿** ⇒ 所有下拉/候选菜单
   其实是"啪"地出现。已接到 AddressInput 的候选菜单上;线上实测捕捉到 menu-in @140ms。
③ `html.view-switch .glass-control` 把"菜单入场"错当成"控件档入场" ⇒ 每次切视图,页面上
   **所有**按钮与输入框一起淡入位移(几十个元素同时动,闪与卡顿的现成来源)。已收窄为
   只给 `.animate-menu-in`;线上实测切视图只剩 pane-rise + 导航项的颜色过渡。

判据化:新增 test/animation-audit.test.mjs —— 每个 @keyframes 都必须有人穿(死动画闸门)、
弹层必须带菜单入场类、不得再出现"整档控件一起动"的切视图规则、reduced-motion 必须显式
覆盖挂载即播那档。写判据的过程本身抓到两处我自己的不一致:删了规则却留下孤儿 keyframes;
以及正则把 reduced-motion 名单里的 `html.view-switch .glass-control` 误判成"还在让它动"
(已改为只扫顶层规则,媒体查询块里的"关掉名单"不算)。
2026-09-15 08:51:18 +08:00