Files
MailUI4Agents/client/electron/test/lib/read.mjs
JianFeeeee 4af083e8b8 test(判据): 4 个判据读的是**另一棵树** —— 修掉硬编码的仓库根,并让 stripComments 保持行号
pi 2026-09-15 实测出来的,**这次长在判据自己身上** —— 正是我们前几轮一直在消的那个形状。

## 一、`const ROOT = '/home/program/agentmail'`:规则进来了,对象没进来

`harmony-arkts.test.mjs` 把仓库根写成了绝对路径。后果我按 pi 的步骤亲手复现了:

```
$ git worktree add --detach /tmp/wt-verify 7f4fa26      # 那个检出里 import 顺序**确实**违规
  (核对:最后 import 在第 80 行,而第 63 行已是 `const NAV_MATERIAL_OF…`)
$ cd /tmp/wt-verify/client/electron && node --test test/harmony-arkts.test.mjs
ok 1 / ok 2 / ok 3   # pass 3  # fail 0      ← **在一个明显违规的检出上 3/3 全绿**
```

因为它读的不是 `/tmp/wt-verify`,是 `/home/program/agentmail`(那份早已修好)。
两层后果,第二层最糟:
① 它**永远无法验证任何别的 checkout / CI / 镜像** —— 换目录不是"红",是 `readdirSync` 直接抛;
② 在本机做 worktree 复核时,它**静默读另一棵树并报绿**。
**判据的逻辑是对的、对象是错的** —— 这比"判据写错了"更难发现,因为它在原地永远是绿的。

同一个毛病在 4 个文件里,**恰好全是最近这几笔新写的**(另 10 个鸿蒙判据写法是对的):

```
harmony-admin / harmony-imageprep / harmony-presets / harmony-arkts  →  const ROOT = '/home/program/agentmail';
其余 10 个                                                          →  const ROOT = join(HERE, '..', '..', '..');
```

已全部照邻居改掉。**修好之后在同一个违规检出上:`# fail 1`** —— 它终于会红了。

## 二、修这条时又牵出一个:`stripComments` **改变了行号**

修好路径后,判据报出"最后一个 import 在第 64 行、第 47 行已是语句",
而**真实文件里是第 80 / 63 行**。成因:`stripComments` 把块注释整块抹成 `''`,
而块注释**自带换行** ⇒ 它之后所有行号整体前移。

这不是小节:全仓判据都用 `文件:行号` 定位(`grep -n`、编辑器跳转、`git show` 核对),
**报出来的行号必须能直接用**,否则读者第一步得先猜"这是剥过的还是没剥的"。
改成"块注释里的每个换行换成等量空行"。修完报的就是 **80 / 63**,与文件逐字对上。

## 三、新增两条判据,让这两个形状不能再回来

1. **`★ 判据不许把仓库根硬编码成绝对路径`** —— 扫判据目录里**真代码**
   (`code()` 剥注释,否则本文件自己的说明文字就会误报),找
   `const X = '/绝对路径'` 且**看着像仓库内**的声明。
   **例外按名字放行**(含 `TOOLCHAIN`/`SDK`/`HDC` 的常量)—— 工具链本来就不在仓库里、推不出来;
   按**值**做白名单会逼着下一个人为了过判据去改那个路径的写法。
2. **`★ stripComments 必须保持行号`** —— 造含多行块注释的样本,断言剥完
   **行数不变**、且第 N 行仍是原来的第 N 行;**同时**断言注释内容确实被去掉了
   (别为了保行号把注释留下)。

两条都做了**变异验证**:
- 把 `harmony-admin` 的 ROOT 改回硬编码 ⇒ 新判据**红**,并点名那个文件;还原后绿。
- 在 `MainPage.ets` **import 之前**插一条语句 ⇒ `harmony-arkts` **红**
  (第 79 行 vs 第 1 行);还原后绿。**这条同时证明了"读的是自己那棵树"** ——
  同样这个变异,在修路径**之前**是绿的。

## 四、未做 / 未验

- 到期闸门那 7 条**没动**(要真装真点,是另一件活)。
- **"把 build 做成一条判据"我探了,两个硬障碍**(详见给 pi 的回信):
  ① `client/harmony/oh_modules` 被 `.gitignore` 排除且未入库 ⇒ **全新检出没有它**,
     构建会先死在装依赖上;② 本沙箱**拒写 `/root/.hvigor`**(`mkdir` Permission denied),
     `hvigorw` 在 worktree 里直接 `EACCES: mkdir '/root/.hvigor/project_caches/…'`。
  所以它在本仓能编过、在干净检出编不过 —— 作为判据它现在会**假红**。
2026-09-15 12:06:15 +08:00

52 lines
2.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 判据目录里**唯一**允许读文本文件的两个入口 —— 名字自己解释该选哪个。
*
* # 为什么要有这个模块(pi 2026-09-14 §4:同一处坑我踩了两次)
*
* 规范里写着"判代码读剥离版、判理由读原文",我 P5 写过一次、当天又踩了一次
* (`'rejected'` 那段**注释**里正好写着 `allowed-once`,被当成"这里会放行"误报)。
* **第二次犯规说明问题不在记性,在形态**:靠人记得执行的规范一定会有下一次。
* 所以把"用哪个读取器"从**记忆**变成**代码里的一个词**,并且可以被判据检查。
*
* - `code(path)`:**剥掉注释**。判"代码里有没有这个调用/这个值"时必须用它 ——
* 否则解释性注释("这里写 'rejected' 而不是 'denied',因为只认 allowed-once")
* 会被当代码读,产生假红/假绿。
* - `prose(path)`:**原文**。判"理由写清了没/文档里有没有这句话"时用它。
*
* 选错的典型症状:断言里的标识符恰好在同文件的注释里出现过(这类误报几乎都集中在
* "解释性注释与它解释的标识符同名"的地方)。
*/
import { readFileSync } from 'node:fs';
/**
* 剥掉注释:只用于"代码里有什么"。
*
* ★ **行号必须保持不变** —— 这一条是硬要求,不是风格问题。
* 原来块注释是用 `''` 直接抹掉的,而块注释**自带换行**,抹掉它就把后面所有行的行号
* 整体前移。后果实测(我自己的 `harmony-arkts` 判据报违规时):
* 报出"最后一个 import 在第 64 行、第 47 行已是语句",而**真实文件里是第 80 / 63 行** ——
* 全仓的判据都在用 `文件:行号` 定位(`grep -n`、编辑器跳转),**报出来的行号必须能直接用**,
* 否则读者第一步就得先猜"这是剥过的还是没剥的"。
* 修法:块注释里的每个换行都**换成等价数量的空行**(而不是整块删掉)。
*/
export function stripComments(src) {
return src
.replace(/\/\*[\s\S]*?\*\//g, (m) => '\n'.repeat((m.match(/\n/g) || []).length))
.replace(/(^|[^:])\/\/[^\n]*/g, '$1'); // 行注释(避开 https:// 这类;它不含换行,行号天然不变)
}
/** 读文件并**剥掉注释** —— 判"代码里有什么"用这个 */
export function code(path) {
return stripComments(readFileSync(path, 'utf8'));
}
/** 读文件**原文** —— 判"注释/文档里写了什么"用这个 */
export function prose(path) {
return readFileSync(path, 'utf8');
}
/** 读**二进制**(安装包、图片等)—— 需要 Buffer 时用它,别在判据里裸用 readFileSync */
export function bytes(path) {
return readFileSync(path);
}