修: 自检夹具 $T2 一直没被清理(每跑一次漏一个 /tmp 目录)+ 补一条守它的判据
## 病
`mkdtree_fail` 建的是 `$T2`,而清理语句是 `743e397` **之前**写的、只提了当时存在的
`$T` 和 `$B` ⇒ **加了新夹具没加清理**。每跑一次 `--self-check` 就往 `/tmp` 漏一个目录
(实测:一轮里跑 6 次 → 6 个残留;历次累计清出 **40** 个)。
★ 但真正的病是**第二层**:`$T2` 用的是**裸 `mktemp -d`**,于是它叫 `/tmp/tmp.XXXXXXXX`
—— 和任何 `mktemp -d` 的产物**长得一样**。后果:
**认不出来是谁的,就没法清理、也没法写判据。**
## 我第一版判据是假绿(变异测试当场抓住)
我按 `find -name 'am-prune-selftest-*'` 数残留。但 `$T2` 根本不匹配这个前缀
⇒ **把 `$T2` 从清理列表里删掉(复原 bug),判据照样绿 23/0。**
这就是"判据在,但走不到"—— 它守的是另一个前缀。
## 修
1. `mkdtree_fail` 改用 `mktemp -d "$TMPD/am-prune-selftest-XXXXXX"`,与 `mktree` 同前缀
⇒ 夹具**可识别**;
2. 清理列表补上 `$T2`;
3. 新增判据「自检夹具清干净(本次新建的残留 = N 个,须为 0)」,
用**集合差**(跑前快照 vs 跑后快照)而不是"有没有"或"最近 N 分钟"
⇒ 上次的残留不会误伤,也不依赖时钟。
## 变异 + 对照(都实跑)
变异:`rm -rf "$T" "$B"`(去掉 $T2) ⇒ rc=1,22 通过/1 失败,**点名残留目录** ✓
基线: ⇒ rc=0,23 通过/0 失败,/tmp 残留 0 ✓
对照:预置一个 STALE 残留 ⇒ rc=0 通过(集合差不误伤)✓
(单次自检耗时 ~39s,不是挂起 —— 我第一次用 2 次循环跑,误撞了 60s 上限。)
This commit is contained in:
@ -190,7 +190,17 @@ if [ "${1:-}" = "--self-check" ]; then
|
||||
}
|
||||
# 给"删除失败"那条样本用的最小树:两个构建暂存(窗口 1 份 ⇒ 旧的应被删)
|
||||
mkdtree_fail() {
|
||||
local t; t="$(mktemp -d)"
|
||||
# 前缀必须和 `mktree` 一致(`$TMPD/am-prune-selftest-XXXXXX`)。
|
||||
#
|
||||
# ★ 2026-09-21:这里原来是裸 `mktemp -d`,于是 `$T2` 叫 `/tmp/tmp.XXXXXXXX`。
|
||||
# 后果有两层,第二层才是要命的:
|
||||
# ① 每跑一次 `--self-check` 就往 `/tmp` 漏一个目录(清理语句里漏了 `$T2`);
|
||||
# ② 漏出来的目录**认不出来是谁的** —— `/tmp/tmp.*` 是任何 `mktemp -d` 的通用形态,
|
||||
# 既没法安全清理,也没法写出一条"残留必须为 0"的判据
|
||||
# (我第一版判据就栽在这:按 `am-prune-selftest-*` 数,永远数不到 `$T2`
|
||||
# ⇒ 变异测试把 `$T2` 从清理列表里删掉,判据**照样绿**)。
|
||||
# **夹具认不出来,就没有判据守得住它。**
|
||||
local t; t="$(mktemp -d "$TMPD/am-prune-selftest-XXXXXX")"
|
||||
mkdir -p "$t/data" "$t/tmp"
|
||||
for ts in 20260101-000000 20260105-000000; do
|
||||
mkdir -p "$t/tmp/agentmail-gateway-build-$ts"
|
||||
@ -228,6 +238,11 @@ if [ "${1:-}" = "--self-check" ]; then
|
||||
}
|
||||
SIG_BEFORE="$(_tree_sig "$ROOT")"
|
||||
|
||||
# 自检**之前**已存在的夹具目录(正常情况下是空的,但并发跑或上次崩溃会留下)。
|
||||
# 后面按**集合差**判"本次是否清干净"—— 比对"有没有新的",
|
||||
# 而不是"有没有"或"最近 10 分钟内的":前者会被上一次的残留误伤,后者依赖时钟。
|
||||
_FIX_BEFORE="$(find "${TMPDIR:-/tmp}" -maxdepth 1 -name 'am-prune-selftest-*' 2>/dev/null | sort)"
|
||||
|
||||
T="$(mktree)"
|
||||
out="$(run "$T")"; rc=$? # 干跑:看报告
|
||||
ck "干净样本:干跑成功(退出码 $rc)" "$([ "$rc" = 0 ] && echo 1 || echo 0)"
|
||||
@ -297,7 +312,21 @@ FAKERM
|
||||
|
||||
ck "自检没有碰生产根($ROOT 清单不变)" "$([ "$(_tree_sig "$ROOT")" = "$SIG_BEFORE" ] && echo 1 || echo 0)"
|
||||
|
||||
rm -rf "$T" "$B"
|
||||
# ⚠️ `$T2` 一度漏在这里(2026-09-21 发现)。夹具是三个(`T` / `B` / `T2`),
|
||||
# 而清理语句是 `743e397` **之前**写的、只提了当时存在的两个 ⇒ 加了新夹具没加清理。
|
||||
# 症状:每跑一次 `--self-check`,`/tmp` 就多留一个 `am-prune-selftest-*`。
|
||||
# **一个自检脚本自己不干净,比它测出来的红更值得先修。**
|
||||
# 判据:跑完 `--self-check` 后按前缀数目录,必须**一个都不剩**(见下面的自检后置)。
|
||||
rm -rf "$T" "$B" "$T2"
|
||||
# 后置:按**集合差**确证本次的夹具真被清掉(不是"我以为清了")。
|
||||
# 只关心"本次新出现的",所以上次的残留不会把它打成假红;
|
||||
# 这条判据正好能抓住"新夹具忘了加进清理列表"—— 那正是 `$T2` 的病史。
|
||||
_FIX_AFTER="$(find "${TMPDIR:-/tmp}" -maxdepth 1 -name 'am-prune-selftest-*' 2>/dev/null | sort)"
|
||||
_FIX_LEFT="$(comm -13 <(printf '%s\n' "$_FIX_BEFORE") <(printf '%s\n' "$_FIX_AFTER") | grep -c . || true)"
|
||||
ck "自检夹具清干净(本次新建的残留 = $_FIX_LEFT 个,须为 0)" "$([ "$_FIX_LEFT" = 0 ] && echo 1 || echo 0)"
|
||||
if [ "$_FIX_LEFT" != 0 ]; then
|
||||
printf '%s\n' "$_FIX_AFTER" | comm -13 <(printf '%s\n' "$_FIX_BEFORE") - | sed 's/^/ 残留: /' >&2
|
||||
fi
|
||||
echo
|
||||
if [ "$fails" != 0 ]; then
|
||||
echo " 判据自检失败 $fails/$total 项 —— 清理器本身不可信,不要用它删东西" >&2
|
||||
|
||||
Reference in New Issue
Block a user