decision-log:把守卫范围与本地限制两条裁定写进既有条目

- 取舍 bullet 补裁定理由:保持「整个 workspace」检查、不换成只查暂存文件(rustfmt --check --skip-children 与 cargo fmt 的口径不一致);本批两次 CI 红的共同根因是「本地绿 ≠ CI 绿」,守卫必须与 CI 同口径
- 取舍 bullet 补流程归因:共树里的硌人是「共享 worktree」的症状、不是守卫的问题,对应流程修正是「一条线一个 worktree」
- 本地限制 bullet 定性为「本机环境限制,不是 CI 会红」,写明根因(Windows/WSL 跨 /mnt/c 的临时目录句柄在子进程退出后仍被持有)、删除重试实测无效且已还原、残留目录会累积(清前 16 个、最早 2026-09-05,本轮已清 0),并给出「Linux CI 上通过」的依据
- 明确裁定:接受该用例在 Windows 上红,不改它语义、本批不再修
This commit is contained in:
2026-09-12 01:15:11 +08:00
parent 9fea60b850
commit 21da95387b
@@ -20,8 +20,8 @@
- 背景:本 PR 已因 `check:rustfmt` 红过一次(`15660a98b` 修掉本批遗留的 8 处格式偏差)。根因是 `.husky/pre-commit` 只跑 `lint-staged`,而它的 glob 只覆盖 `*.{js,mjs,cjs,ts,tsx}` —— **Rust 格式在本地没有任何守卫**,唯一防线是 CI 那一侧(`check-repository-ci.sh``npm run lint``check:rustfmt`);本地没人跑得到的门禁等于没有门禁,「本地全绿、CI 才红」就会反复发生。
- 决策:lint-staged 增 `"*.rs": ["node scripts/lint-staged-rustfmt.mjs"]``cargo fmt` 只按 workspace 粒度格式化、**不接受文件参数**(lint-staged 会把命中的暂存路径追加到命令末尾),所以用包装脚本忽略 argv,对 `server-rs``apps/ai-game-creator-shell/src-tauri` 两个 workspace 各跑一次 `cargo fmt --all --manifest-path <m> -- --check`**只查不改** —— pre-commit 不应该自动改写别人正在改的 Rust 文件。workspace 路径与既有 `check:rustfmt` 一样写成 cwd 相对,因为 lint-staged 以 git 根为 cwd 运行任务。
- 连带维护点:`scripts/git-hooks.test.mjs``assert.deepEqual` **钉住 lint-staged 的整份配置形状**,增键必须同步该用例,否则 `check:git-hooks`(在 `npm run lint` 内)会以 `deepStrictEqual` 失败 —— 本次就是这样红了 Repository checks。该文件第 2 个用例里的 `lintStagedConfig` 是 temp repo 的测试替身,不随之增键:temp repo 没有 Rust 文件,`.rs` 只会命中 0 个。
- 已知影响(**刻意保留**):没有暂存 `.rs` 时守卫完全不触发(lint-staged 报 `[SKIPPED] *.rs — no files`);一旦暂存了 `.rs`,它检查的是**整个 workspace** 而不只是暂存文件 —— 这是为了与 CI 完全同口径而接受的取舍,副作用是「别人工作树里未格式化、且尚未暂存的 `.rs` 会挡住本次提交」,此时应按报错里的文件去找该文件的作者,不要顺手 `cargo fmt`(那会连带格式化别人的在途代码)。
- 已知本地限制(**未修,非本守卫引入**):`check:git-hooks` 第 2 个用例(`pre-push runs repository parity only for master updates`)在 Windows 本机会红,形态是 `finally``rmSync``EBUSY: resource busy or locked`**断言全部通过,红在清理**)。本机 `%TEMP%` 堆积 16 个 `genarrative-pre-push-*` 残留目录、最早到 2026-09-05,说明该现象长期存在且每次运行都发生;给 `rmSync``maxRetries: 10`(约 5.5s 线性退避)实测**无效**,疑为 WSL bash 跨 `/mnt/c` 的句柄在子进程退出后仍被持有。CI 在 Linux 上不受影响,故不在本批修。
- 已知影响(**刻意保留**):没有暂存 `.rs` 时守卫完全不触发(lint-staged 报 `[SKIPPED] *.rs — no files`);一旦暂存了 `.rs`,它检查的是**整个 workspace** 而不只是暂存文件 —— 这是为了与 CI 完全同口径而接受的取舍,副作用是「别人工作树里未格式化、且尚未暂存的 `.rs` 会挡住本次提交」,此时应按报错里的文件去找该文件的作者,不要顺手 `cargo fmt`(那会连带格式化别人的在途代码)。**裁定:保持「整个 workspace」不变**,不换成「只查暂存文件」(`rustfmt --check --skip-children``cargo fmt` 的口径不再一致)。理由是「本地绿 ≠ CI 绿」正是本批两次 CI 红的共同根因,守卫必须与 CI 走同一条口径;共树里那种硌人是**共享 worktree 的症状、不是守卫的问题**,对应的流程修正是「一条线一个 worktree」。
- 已知本地限制(**本机环境限制,不是「CI 会红」**):`check:git-hooks` 第 2 个用例(`pre-push runs repository parity only for master updates`)在 Windows 本机会红,形态是 `finally``rmSync``EBUSY: resource busy or locked`**断言全部通过,红在清理**)。根因是 Windows/WSL 跨 `/mnt/c` 的临时目录句柄在子进程退出后仍被持有(本机 `bash` 是 WSL 的 GNU bash 5.2.21 `x86_64-pc-linux-gnu`),该句柄存活时间超过删除重试窗口 —— 给 `rmSync``maxRetries: 10`(约 5.5s 线性退避)实测**无效**,已逐字还原。**后果是残留目录会累积**:清理前本机 `%TEMP%` 有 16 个 `genarrative-pre-push-*`、最早到 2026-09-05(即每次运行都发生;本轮已清到 0),看到残留目录直接删即可。判定它是本机环境限制而非 CI 会红的依据:残留目录已持续一周,而这一周 CI 上该用例是绿的 ⇒ **Linux CI 上该用例通过**。**裁定:接受它在 Windows 上红,不改该用例语义、本批不再修。**
- 验证方式:`node scripts/lint-staged-rustfmt.mjs` exit 0lint-staged 分派层面确认 `*.rs` 任务真被触发(对 6 个 `.rs` 跑通)且无 `.rs``[SKIPPED]``npm run check:rustfmt``npm run check:encoding``git diff --check` 均 exit 0。变异验证:把 `*.rs``package.json` 摘掉 → `check:git-hooks` 第 1 个用例以同样的 `deepStrictEqual` operator 变红;还原(`package.json` 字节级哈希一致)后该用例回 `ok`
- 关联文档:`docs/project-memory/shared-memory/pitfalls.md``cargo fmt --all` 会扫到别人未提交半成品那条)。本文件 2026-08-12「Repository checks 采用 CI 与本地共用的单一门禁入口」条的「提交门禁」一行只描述当时的 JS/TS 范围,按本文件顶部口径历史条目只用于追溯,不再回改;提交 `dd7cf401a`(守卫本体)、`2a7bfadd7`(形状断言跟进)。