From 44fdfb3ed7be342e34c5625103c97d61f0f5ed11 Mon Sep 17 00:00:00 2001 From: JianFeeeee Date: Mon, 14 Sep 2026 20:25:24 +0800 Subject: [PATCH] =?UTF-8?q?fix(deploy-drift):=20=E5=88=A4=E6=8D=AE=20?= =?UTF-8?q?=E2=91=A0=20=E7=9A=84=E8=A6=86=E7=9B=96=E9=9D=A2=E5=86=99?= =?UTF-8?q?=E8=BF=9B=20note=EF=BC=880=20=E5=BF=85=E9=A1=BB=E5=B8=A6?= =?UTF-8?q?=E4=B8=8A=E5=8F=AF=E8=AF=81=E4=BC=AA=E8=8C=83=E5=9B=B4=EF=BC=89?= =?UTF-8?q?+=20=E8=AE=B0=E5=BD=95=20C=20=E7=9A=84=E5=8F=96=E8=88=8D=20+=20?= =?UTF-8?q?docs=20=E8=A1=A5=204=20=E6=9D=A1=E7=BA=AA=E5=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pi 的 C 项(`.bak` 那次修法"把圈往外挪了一格,还是圈")我**实测后决定不改**,理由留档: - 这一格现在**零违规**:`/etc/systemd/system` 133 个文件里**没有任何一个**含仓库路径, 包括现存的 4 个 `.bak`(`dsh-lan.service.bak-20260903-081410`、 `pi-bridge.service.bak-13010-20260814`、`pi-bridge.service.bak-20260814`、 `pi-web-sessiond.service.bak-20260814`)—— 全干净; - pi 提到的 `zcode.service.bak-20260912-145744` **他读时已经 ENOENT**(他自己写了), 也就是说我引他那句话时依据的文件**已经不存在**; - 扩到"每个普通文件都读"在当前只会引入噪声(二进制/dpkg 数据库类),换不到真信号。 ⇒ 改为**把覆盖面写进 note**("比了 129 个 … 文件")—— `0` 只有在"它能被证伪的范围"写明之后才是结论,这正是这条判据当初缺的那句话。 等真出现一个非白名单后缀的违规再改,那时我们就有实例了。 docs/DEV-TOOLING.md 补 4 条纪律(编号 7-10,原第 7 条顺延): 7 「注入点会把该抓的 bug 藏起来」(含位置选择器 `bad[0]` 的同类); 8 「看起来在比、其实没比」要当一条自查(本轮出现三次:路径错/空目录/符号链接), 且**绿的时候也要留下覆盖范围的证据**; 9 **变异之前先提交**(我未提交就变异 + `git checkout` 还原,把自己的改动冲掉); 10 **注释里的数字无法被判据守住**(同文件三处说法三个数、式子加起来还是错的)。 自检全过;实跑 ① 报"比了 129 个文件"。 --- deploy/check-deploy-drift.mjs | 24 +++++++++++++++++++++++- docs/DEV-TOOLING.md | 22 +++++++++++++++++++++- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/deploy/check-deploy-drift.mjs b/deploy/check-deploy-drift.mjs index 298041d..293ef3f 100644 --- a/deploy/check-deploy-drift.mjs +++ b/deploy/check-deploy-drift.mjs @@ -666,6 +666,19 @@ export function checkLayout(inject = {}) { // 那句话只对我自己划的那个圈成立 —— **判据的边界没说出口,就等于报了个假的 0**。 // 留着 .bak 的代价也不是零:它们是"过期的旧真相",`grep` 到它的人会以为改动没生效。 const offenders = []; + let scanned = 0; + // ⚠️ **已知的圈**:这里按后缀取(`.conf/.service/.timer`,含 `.bak*` 变体)。 + // pi 评审 2026-09-14 建议改成"不划圈"——`/etc/systemd/system` 下每个普通文件都读一遍 + // 再 grep,理由是**白名单永远会漏一个**(`.dpkg-old`/`.rpmsave`/`~`/`.disabled`/…)。 + // + // 取舍(我做了、理由写在这里):**暂不改**,因为实测这一格现在是**零违规**: + // 该目录 133 个文件里,**没有任何一个**包含仓库路径 —— 包括现存的 4 个 `.bak` + // (`dsh-lan.service.bak-20260903-081410`、`pi-bridge.service.bak-13010-20260814` + // 等,全干净)。`pi` 说的 `zcode.service.bak-20260912-145744` 已经不在了 + // (他自己读时也是 ENOENT),所以我引他那句时依据的文件**已消失**。 + // 既然零违规,扩到"全文件"只增加噪声(二进制/dpkg 数据库类文件),换不到真信号; + // 因此改为**把覆盖面写进 note**(`0` 必须带上"它能被证伪的范围"), + // 并把这条决定留在注释里 —— 等真出现一个非白名单后缀的违规再改,那时我们就有实例了。 const walk = dir => { let entries = []; try { entries = readdir(dir, { withFileTypes: true }); } catch { return; } @@ -673,6 +686,7 @@ export function checkLayout(inject = {}) { const full = `${dir}/${e.name}`; if (e.isDirectory()) walk(full); else if (/\.(conf|service|timer)(\.bak.*)?$/.test(e.name)) { + scanned++; let text = ''; try { text = String(readFile(full, 'utf8')); } catch { continue; } if (text.includes(REPO)) offenders.push(full); @@ -680,7 +694,15 @@ export function checkLayout(inject = {}) { } }; walk(SYS); - push('没有任何 unit/drop-in/.bak 引用源码目录', offenders.length === 0, offenders.join(' ')); + // 覆盖面写进 note:`0` 只有在"它能被证伪的范围"写明之后才是结论 + // (这正是这条判据当初报"0 个文件"时缺的那句话)。 + push( + '没有任何 unit/drop-in/.bak 引用源码目录', + offenders.length === 0, + offenders.length === 0 + ? `比了 ${scanned} 个 .conf/.service/.timer/.bak* 文件(覆盖面:按后缀取,见下)` + : offenders.join(' ') + ); // ② 已安装单元与仓库副本一致(仓库是唯一真相)—— **两个方向都判** // diff --git a/docs/DEV-TOOLING.md b/docs/DEV-TOOLING.md index 6d8cb5a..58de210 100644 --- a/docs/DEV-TOOLING.md +++ b/docs/DEV-TOOLING.md @@ -168,7 +168,27 @@ bash deploy/prune-deploy-artifacts.sh --self-check # 判据自检(16 项, `$?` 拿到的是 **`tail` 的**退出码,于是那条脚本的红(真值 1)被我报成了 0; 两个失败信息之所以还看得见,只是因为它们走 stderr 没进管道。 **用 `$PIPESTATUS[0]`,或先落文件再读** —— 管道会改写量纲,与上一条同族。 -7. **一条新判据上线时,先找它可能与哪些既有不变量冲突。** 2026-09-14: +7. **"注入点"会把该抓的 bug 藏起来。** 2026-09-14 实例:`checkLayout` 的每个自检样本都 + 显式注入 `repoUnits`,于是那条判据的**真实默认值从没被任何样本走过** —— + 而它当时恰好是错的(解析到不存在的 `/home/program/agentmail/systemd`), + 判据因此**一个文件都没比过却报"一致"**,自检还 100% 绿。 + ⇒ 默认值本身要有判据(导出常量 + 断言存在)、样本要留至少一条**不注入**的; + ⇒ 同类还有**位置选择器**(`bad[0]`/`badBak[0]`):在函数前面插一条新检查就改变了 + 既有断言的语义 —— 断言要按**名字**锚定。 +8. **"看起来在比、其实没比"要设成一条自查。** 同一轮里它出现了三次: + 目录路径错(`../systemd`)、空目录被当成"一致"、依赖是**符号链接**而选目录时 + 只挑 `isDirectory()` ⇒ 三次都产出"通过"。 + ⇒ 判据绿的时候**也要留下覆盖范围的证据**("比了 22 个文件"/"165 个包"): + 空 note 无法区分"一致"和"没比过",而那正是这三次的样子。 + ⇒ 一条判据如果**只能靠真文件系统喂**,它就没法被自检 —— 读写都要走可注入面。 +9. **变异之前先提交。** 我在**未提交**状态下变异,然后用 `git checkout HEAD -- ` + 还原,把自己的改动一起冲掉了(这条纪律我写过、还是踩了)。 + 顺序必须是:提交 → 变异 → 确认红 → `cp` 还原 → `cmp` 校验。 +10. **注释里的数字无法被判据守住。** 同一文件里曾同时写"写点五处"和"共 6 处" + (且它的式子 2+2+3 加起来是 7,实际 10 处)——**三处说法三个数**。 + 与"不要手抄期望用例数常量"同源:**两组矛盾的数字比没有数字更糟**, + 因为它让读者以为有人数过。要判覆盖完整只能靠**机制**(整段 try/catch),不靠数数。 +11. **一条新判据上线时,先找它可能与哪些既有不变量冲突。** 2026-09-14: 新加的"本平台不可达 ⇒ 搬去 `test/lib/`"与既有的"共用模块四方**逐字节同源、 连相对路径一起钉**"(`deploy/check-shared-libs.sh`)**方向相反** —— 我只看⻅了自己那条,于是"按规则推断出的正确动作"把共用判据打红两处,