diff --git a/deploy/prune-deploy-artifacts.sh b/deploy/prune-deploy-artifacts.sh index 40c6a91..a3627ef 100755 --- a/deploy/prune-deploy-artifacts.sh +++ b/deploy/prune-deploy-artifacts.sh @@ -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' diff --git a/docs/API.md b/docs/API.md index 214e3f7..21ce4aa 100644 --- a/docs/API.md +++ b/docs/API.md @@ -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 '处理失败:%'`,不靠子串)后实测 (**同一分钟内**两次读数,用于演示漂移):