修: 我给出的 PRUNE_TMP_DIR 补救命令**自己跑不起来**(pi 实测报回)—— 已兜底修好;另补全 7 处模板

## 一★ pi 报回一个真 bug:我那条"补救命令"没被我自己跑过

我在注释里教人用 `PRUNE_TMP_DIR=/tmp/am-iso-$$ … --self-check`,**但没先建目录**。
pi 原样粘贴,实测 **1 通过 / 6 失败**。我复现:**1/6**,与它完全一致。

根因:夹具用 `mktemp -d "$TMPD/am-prune-selftest-XXXXXX"`(`:154`/`:203`),
**要求 `$TMPD` 已存在** —— `mktemp -d` **不建中间目录**。
夹具一个都没建起来 ⇒ 后面所有判据对空目录求值 ⇒ **失败项全是【干净样本】**,
而且"残留"那条**根本不出现**(容易被读成"隔离没用",实际是"没建起来")。

修法:两处 `mktree`/`mkdtree_fail` 里补 `mkdir -p "$TMPD"`(幂等),
**不再把"目录没建"的责任推给使用者**。三态实测:

    基线(默认 /tmp)                        ⇒ rc=0  23/0
    PRUNE_TMP_DIR 未预建(**原 bug 场景**)   ⇒ rc=0  23/0   (修前 1/6)
    PRUNE_TMP_DIR 已建 + 持续外部 rm 干扰     ⇒ rc=0  23/0

★ **教训**:我把一条**自己没跑过**的命令当成"实测可用"发了出去。
"机制对"不等于"命令对" —— **一条命令的价值,在于它被原样粘贴后能不能跑。**
(我上一封还在说"有这个开关≠用了这个开关",转头又犯"机制可用≠命令可用"。)

## 二、`处理失败:` 模板:7 处,我原来只列了 5 处

pi 复核后报 **7** 处,我逐处核过行号:

    pi       src/worker.mjs:619 / :711
    dsh      src/index.ts:1215 / :1719
    opencode index.js:781 / :1042          ← 我第一版**整段漏了 opencode 的 2 处**
    zcode    src/index.mjs:300

另记 pi 指出的**形似但不算**的两处:`crash-notify`
(`pi/lib/crash-notify.mjs:20`、`opencode/lib/crash-notify.js:20`)——
我核了:两处 `to: 'jianf@'` 且 **`reply_to` 出现 0 次**
⇒ **不会成为"孩子"**,对本判据无影响。
⇒ **"主题里带 `处理失败:`"是形状;"会不会成为某封信的孩子"才是判据条件。**

(病因与 `/mail/read` 那张表相同:**搜索路径没覆盖全 ⇒ 数少了也看不出来**。
这是我这轮**第二次**在"数有几处"上少数。)
This commit is contained in:
2026-09-21 07:14:41 +08:00
parent 572ddb9ca7
commit dbdef99b27
2 changed files with 32 additions and 4 deletions

View File

@ -151,6 +151,13 @@ if [ "${1:-}" = "--self-check" ]; then
ck() { total=$((total+1)); if [ "$2" = "1" ]; then echo " 通过 $1"; else echo " 失败 $1"; fails=$((fails+1)); fi; }
mktree() {
# ★ `mktemp -d "$TMPD/…"` 要求 `$TMPD` **已经存在** —— 它不会替你建中间目录。
# ⇒ 若使用者把 `PRUNE_TMP_DIR` 指到一个还不存在的目录(文档里就是这么教的),
# 自检会**整体崩成 1 通过 / 6 失败**,且失败项全是【干净样本】——
# 因为夹具**一个都没建起来**,后面的判据全部对空目录求值。
# ⚠️ 这个坑是 pi 实测报的(`308bc4bc`),我复现:未预建 **1/6**、加 `mkdir -p` **23/0**。
# ⇒ 在这里兜住:`$TMPD` 不存在就建(幂等),别把"目录没建"的责任推给使用者。
mkdir -p "$TMPD"
local t; t="$(mktemp -d "$TMPD/am-prune-selftest-XXXXXX")"
mkdir -p "$t/data" "$t/backups" "$t/tmp" \
"$t/plugins/demo/20260101-000000" "$t/plugins/demo/20260102-000000" \
@ -200,6 +207,7 @@ if [ "${1:-}" = "--self-check" ]; then
# (我第一版判据就栽在这:按 `am-prune-selftest-*` 数,永远数不到 `$T2`
# ⇒ 变异测试把 `$T2` 从清理列表里删掉,判据**照样绿**)。
# **夹具认不出来,就没有判据守得住它。**
mkdir -p "$TMPD" # 同上:`mktemp -d` 不建中间目录
local t; t="$(mktemp -d "$TMPD/am-prune-selftest-XXXXXX")"
mkdir -p "$t/data" "$t/tmp"
for ts in 20260101-000000 20260105-000000; do
@ -316,8 +324,18 @@ if [ "${1:-}" = "--self-check" ]; then
#
# ★ 补救已实测可用:`PRUNE_TMP_DIR`(`:37`)把夹具整体挪出共享前缀。
# 同样施加持续外部 `rm -rf /tmp/am-prune-selftest-*`,用
# `PRUNE_TMP_DIR=/tmp/am-iso-$$` 跑 ⇒ **rc=0,23 通过 / 0 失败**。
# `mkdir -p /tmp/am-iso && PRUNE_TMP_DIR=/tmp/am-iso bash … --self-check`
# ⇒ **rc=0,23 通过 / 0 失败**。
# ⇒ **跑自检(尤其并发时)应当带上 `PRUNE_TMP_DIR`。**
# ⚠️⚠️ **但这行命令我第一版写错了**(pi 实测报回,`308bc4bc`):
# 我原来写 `PRUNE_TMP_DIR=/tmp/am-iso-$$`,**没先建目录** ⇒ 实测 **1 通过 / 6 失败**。
# 根因:`mktemp -d "$TMPD/am-prune-selftest-XXXXXX"` **要求 `$TMPD` 已存在**,
# 它**不会替你建中间目录**;夹具一个都没建起来 ⇒ 后面所有判据对空目录求值 ⇒
# 失败项**全是【干净样本】**(且"残留"那条**根本不出现**,容易被读成"隔离没用")。
# ⇒ 已在两处 `mktree`/`mkdtree_fail` 里补 `mkdir -p "$TMPD"` 兜底(幂等),
# 现在**未预建也是 23/0** —— 不再把"目录没建"的责任推给使用者。
# ★ **教训**:我给别人的"补救命令"自己**没跑过**就发了出去。
# "机制对"不等于"命令对" —— **一条命令的价值,在于它被原样粘贴后能不能跑。**
T2="$(mkdtree_fail)"
_FIX_MINE="$T $B $T2"
cat > "$T2/fake-rm" <<'FAKERM'

View File

@ -529,11 +529,21 @@ curl -X POST {host}/api/v1/mail/read -H "Authorization: Bearer $AGENT_KEY"
桥代它回一封 `处理失败: <父主题>`:
```
plugins/pi-mail-bridge/src/worker.mjs:619 / :711 subject: `处理失败: ${data.subject …}`
plugins/zcode-mail-bridge/src/index.mjs:300 同上
plugins/dsh-mail-bridge/src/index.ts:1215 / :1719 同上(dsh 侧也有,pi 只列了 3 处)
**四处桥的源码各有一处或多处**(2026-09-21 全量数过,共 **7** 处):
plugins/pi-mail-bridge/src/worker.mjs:619 / :711
plugins/dsh-mail-bridge/src/index.ts:1215 / :1719
plugins/opencode-mail-bridge/index.js:781 / :1042
plugins/zcode-mail-bridge/src/index.mjs:300
```
⚠️ **我第一版只列了 5 处**(漏了 opencode 的 2 处,dsh 那 2 处是第二轮才补上的),
pi 复核后报回 **7** 处、我逐处核过。**数"有几处"时最怕的就是漏数** ——
与本节下方 `/mail/read` 那张表是同一个病因(**搜索路径没覆盖全 ⇒ 数少了也看不出来**)。
⚠️ 另有一类**形似但不算**的:`crash-notify` 的两处
(`pi/lib/crash-notify.mjs:35`、`opencode/lib/crash-notify.js:21`)——
它们 `to: 'jianf@'`、**无 `reply_to`** ⇒ **不会成为"孩子"**,对本判据无影响。
**"主题里带 `处理失败:`"是形状,"会不会成为某封信的孩子"才是判据条件。**
⇒ **那封"回信"恰恰是"没读过"的证据**,不是"读过了"的证据。
精确模板匹配(`child.subject LIKE '处理失败:%'`,不靠子串)后实测
(**同一分钟内**两次读数,用于演示漂移):