docs(判据): ★★ 「重构建」是**两步** —— 只做第一步,红会从 build-stamp 搬到 packaging

**实测(2026-10-03,每步退出码取自不接管道的运行)**:
    提交 ⇒ HEAD 前进 ⇒ build-stamp 红(产物记旧 rev)
    ① npm run build                      ⇒ build-stamp 绿、**packaging 红**
    ② npx electron-builder --linux …    ⇒ 两条同时绿
★ 只做①的人会以为「修好了」(前一条确实绿了),而红只是**换了个位置**。

**为什么①会让 packaging 转红**:packaging 比的是「asar 里的 dist 文件名」
vs「当前 dist/index.html 引用的文件名」,而 Vite 输出带**内容哈希**
⇒ 重构建一换文件名,asar 立刻过期。两条判据是**同一条链的两环**,
而 package.json 里**没有** beforeBuild 钩子把两者串起来 ⇒ 必须人记得做两次。

**改了三个地方,因为「报错文案」比文档更容易被看到(§14 同一条道理)**:
· build-stamp 的报错文案**自带第二步**。原文只写「正确修法只有一个:重跑构建」——
  实测证明那句话**只完成了一半**,而只看文案的人会照做然后以为好了。
  已用变异测试确认:把产物 gitRev 改成 0000000 ⇒ 变红且两条步骤都出现,
  改回 ⇒ 绿。
· CRITERIA.md 新增 §6.0.6「同一个红会自己搬家时,要把它变成有界的」。
  它讲的是**读数的信噪比**:一条结构性长期红与真缺陷**同屏**,
  会把整屏红的价值抵消("看到了 ⇒ 当没看见")。
  明确**不能**靠加跳过名单解决(那是把它变成看不见),
  正解是到期条件可机检 + 报错文案自带下一步。
· DEBTS.json 的 build-stamp-stale-artifact-blocks-verification:
  把 due/where/note 补上「两步」的实测事实,并把**第二环 packaging**
  补进 where —— 原笔只记了 build-stamp 那一环。

**边界 / 未做**
· 没有把两步串进 npm 脚本或 beforeBuild —— 那是**构建流程**的改动,
  而本工作区正被多个会话并发使用;由人决定。
· 这笔债**仍未结算**:它会在**下一次提交**时重现(结构性的,与代码无关)。
This commit is contained in:
2026-10-03 11:17:18 +08:00
parent 6df3c356d6
commit 9d40816230
3 changed files with 48 additions and 4 deletions

View File

@ -277,8 +277,8 @@
"count": 1,
"kind": "**产物自证长期红**:`build-stamp` 要求 `BUILD_INFO.json` 的 `gitRev` 精确等于 HEAD,而该文件**未被 git 跟踪**、内容长期停在某次旧构建上 ⇒ 这条判据只能靠「重构建」恢复,**任何代码改动都无法让它转绿**",
"due": "有人重跑一次客户端构建(产出新的 `BUILD_INFO.json`)时。★ 到期动作**不是**改 `BUILD_INFO.json` 里的 `gitRev`/`srcHash` —— 判据自己的报错文案就写明那样做等于把它废掉;也不是把它加进跳过名单;唯一正确的动作是重跑构建。",
"where": "`client/electron/test/build-stamp.test.mjs:93`(`★ 产物必须自报来源:BUILD_INFO 精确比对`);产物记录在 `client/electron/BUILD_INFO.json`(★ **未被 git 跟踪**);实测 2026-09-28 产物记 `87c55ac`、HEAD 已走到 `f1c74fc`",
"note": "★★ 2026-09-28 登记(pi 实测)。\n\n## 形状:**一条判据长期红,且与被测代码无关**\n\n`build-stamp` 断言产物自报来源必须精确等于当前 HEAD。实测:\n产物记 `87c55ac`,HEAD 当时是 `359cb43`、随后因本轮工作走到 `f1c74fc`\n⇒ **无论谁提交什么,这条判据都不会自己变绿** —— 它只能被一次重构建救。\n\n## 为什么值得单独记一笔:它是**共享工作树债的直接产物**\n\n同一个 `HEAD` 上(`shared-workspace-unserialized-deploy`):产物落后于源码,\n因为多个会话连续提交而**没人重跑构建**。`d3a7873` 那笔 HIGH 记的正是\n「判据说干净而构建物不干净」;本条是它的**日常形态** —— 不危险,\n但它会长期挂在套件的红名单上,让「红了」这件事开始钝化。\n\n★ 真正的危害不是这条判据本身,是**它会训练人忽略红色**:\n套件汇总里 `build-stamp` 与另两条真缺陷并列显示,人一旦习惯\n「哦又是 build-stamp」,就会把同一行里的真缺陷一起放过。\n这与本仓反复消的「看不到 ⇒ 绿」是同一族,方向相反:**看到了 ⇒ 当没看见**。\n\n## 已做的核对(避免把别人的问题算成新的)\n\n· 提交 `f1c74fc` 之前就在**干净 HEAD** 上复现过 ⇒ 不是本轮引入;\n· `BUILD_INFO.json` 经 `git ls-files` 确认**未被跟踪** ⇒ 缺的那次构建\n 没人提交过,不是被谁回滚;\n· 判据报错文案本身已警告「别去改 gitRev/srcHash 了事」 —— 本条认同,\n 并把那个正确修法(重跑构建)写进 `due`。\n\n## 未做\n\n没有触发构建、没有改 `BUILD_INFO.json`、也没有把它加进任何跳过名单。\n构建是部署动作,而本工作区正在被多个会话并发提交(已有两个\n`server/internal/repo/zz_*_probe_test.go` 不属于本会话)—— 由人决定何时构建。"
"where": "`client/electron/test/build-stamp.test.mjs:93`(`★ 产物必须自报来源:BUILD_INFO 精确比对`);产物记录在 `client/electron/dist/BUILD_INFO.json`(★ **未被 git 跟踪**);**同一条链的第二环**是 `client/electron/test/packaging.test.mjs:73`(asar 里的 dist 必须与当前构建一致)· 实测 2026-09-28 产物记 `87c55ac`、HEAD 已走到 `f1c74fc`· 实测 2026-10-03 重现:提交后产物记 `5ad75fb`、HEAD 已是 `9c66c35`",
"note": "★★ 2026-09-28 登记(pi 实测)。\n\n## 形状:**一条判据长期红,且与被测代码无关**\n\n`build-stamp` 断言产物自报来源必须精确等于当前 HEAD。实测:\n产物记 `87c55ac`,HEAD 当时是 `359cb43`、随后因本轮工作走到 `f1c74fc`\n⇒ **无论谁提交什么,这条判据都不会自己变绿** —— 它只能被一次重构建救。\n\n## 为什么值得单独记一笔:它是**共享工作树债的直接产物**\n\n同一个 `HEAD` 上(`shared-workspace-unserialized-deploy`):产物落后于源码,\n因为多个会话连续提交而**没人重跑构建**。`d3a7873` 那笔 HIGH 记的正是\n「判据说干净而构建物不干净」;本条是它的**日常形态** —— 不危险,\n但它会长期挂在套件的红名单上,让「红了」这件事开始钝化。\n\n★ 真正的危害不是这条判据本身,是**它会训练人忽略红色**:\n套件汇总里 `build-stamp` 与另两条真缺陷并列显示,人一旦习惯\n「哦又是 build-stamp」,就会把同一行里的真缺陷一起放过。\n这与本仓反复消的「看不到 ⇒ 绿」是同一族,方向相反:**看到了 ⇒ 当没看见**。\n\n## 已做的核对(避免把别人的问题算成新的)\n\n· 提交 `f1c74fc` 之前就在**干净 HEAD** 上复现过 ⇒ 不是本轮引入;\n· `BUILD_INFO.json` 经 `git ls-files` 确认**未被跟踪** ⇒ 缺的那次构建\n 没人提交过,不是被谁回滚;\n· 判据报错文案本身已警告「别去改 gitRev/srcHash 了事」 —— 本条认同,\n 并把那个正确修法(重跑构建)写进 `due`。\n\n## 未做\n\n没有触发构建、没有改 `BUILD_INFO.json`、也没有把它加进任何跳过名单。\n构建是部署动作,而本工作区正在被多个会话并发提交(已有两个\n`server/internal/repo/zz_*_probe_test.go` 不属于本会话)—— 由人决定何时构建。\n\n## ★★ 2026-10-03 实测修正:正确动作是**两步**,不是一步\n\n本条原写「唯一正确的动作是重跑构建」—— 今日实测证明那只完成了一半。\n完整链条(每一步都实测过,且**退出码取自不接管道的运行**):\n\n```\n① 提交 ⇒ HEAD 前进 ⇒ build-stamp 红(产物记旧 rev)\n② npm run build ⇒ build-stamp 绿、**packaging 红**\n (原因:dist/index.html 引用了新的 index-<hash>.js,而 asar 里还是旧的)\n③ npx electron-builder --linux ... ⇒ packaging 绿(两者同时绿)\n```\n\n★ **为什么第②步会连带把 packaging 打红**:`packaging` 比的是「asar 里的 dist 文件名」vs「当前 `dist/index.html` 引用的文件名」,而 Vite 输出带内容哈希 ⇒ 重构建换文件名 ⇒ asar 立刻过期。\n⇒ 两条判据是**同一条链的两环**,只重构建会让红从一条**搬到**另一条。\n⚠️ 而 `package.json` 里**没有** `beforeBuild` 钩子把两者串起来,所以它必须由人记得做两次。\n\n## 为什么这条值得改:它的危害是**训练人忽略红色**\n\n原 note 已写准了这一点(「看到了 ⇒ 当没看见」)。今日现场证据:套件汇总里它与真缺陷**并列显示**,而它每次都红 ⇒ 读的人会习惯性跳过那一行。\n★ 这正是 `CRITERIA.md` §6.0 的**读数**问题(不是「怎么写判据」的问题):**一条长期红如果与真红同屏,会降低整屏红的信噪比**。\n⇒ 不能靠「加进跳过名单」(那是把它变成看不见),正解是**把它变成有界的**:见 §6.0.6(本笔的 `due` 条件就是那个界)。\n\n## 边界 / 未做\n\n· 本笔**仍未结算**:它会在**下一次提交**时重现(结构性的,与代码无关)。\n· 没有把两步串进 `npm` 脚本或 `beforeBuild` —— 那是**构建流程**的改动, 而本工作区正被多个会话并发使用;由人决定。\n· `BUILD_INFO.json` 在 `dist/` 下(gitignore),`release/` 同样未被跟踪。"
},
{
"id": "delivered-but-never-dispatched-silent",