★ pi 指出我"只修了一半": check-require-declaration.sh 收集违规行那处仍是管道;实测 -n→-q 静默漏掉全部 3 个违规 ⇒ 判据判定落在**输出通道**上

★ pi 的实验我复现: 现行 `grep -n` 报 3 个违规/rc=1;改成 `grep -q` ⇒ 报 **0** 个/rc=**0**(3 个全漏)
   ⇒ 它**目前**不出事只因"恰好选了不早退的那个选项"(-n 要读完全部输入 ⇒ 无 SIGPIPE)
     ⇒ **那不是正确性,是运气**。与我上一版在 :78-82 修的是同一条,我却漏了 :102。
★ 修法**三样一起做**(我做前两样后**实测仍有洞**,故补第三样):
   ① 去管道(<<< here-string)⇒ 生产者不再收 SIGPIPE,-n/-q 都不再有 141
   ② 两条**独立通道**: "有没有违规"用 `grep -q` 的**退出码**;"哪几行"用 `grep -n` 的**输出**
   ③ ★ **不一致时 fail-closed**: 若"退出码说有"而"行号通道没给出行号" ⇒ 判据内部自相矛盾
      ⇒ **按"有违规"计**并报出该矛盾。
      实测缺 ③ 会怎样: 只做 ①② 时,把取行号那处改成 `-q` ⇒ 报 0 个、rc=**0**(又是全漏)
      ⇒ **"计数"与"证据"走同一条通道时,那条通道静音就等于"没有"** ⇒
        要么分通道,要么规定**静音=失败侧**(本处取后者)。
★ 验证(全部真跑,含三个变异):
   · 基线 rc=0(3 个调用者,裸赋值 0 处)
   · [A] 注入 3 处裸赋值                 ⇒ rc=1 ✓
   · [B] 判"有没有"那处 -q→-n           ⇒ rc=1 ✓
   · [C] 取行号那处 -n→-q(pi 的变异)   ⇒ rc=1 ✓ 且 **fail-closed 报出 3 次** ✓
   · 每个变异后还原并复测 rc=0;`git diff --numstat` 仅剩我的文件
     (同目录另有并发会话在改 check-deploy-drift.mjs —— 未 add 它的改动)
   · criteria-hygiene 7/7 绿、pi 的 env-guard 17/17 绿、bash -n 过;生产未动
This commit is contained in:
2026-09-25 07:01:34 +08:00
parent e63a5ba81d
commit 5d9a41c3d3

View File

@ -99,13 +99,42 @@ fi
fails=0
for f in "$@"; do
hits="$(strip_comments "$f" | grep -nE '^[[:space:]]*AGENTMAIL_REQUIRE=' || true)"
[ -z "$hits" ] && continue
while IFS= read -r h; do
[ -z "$h" ] && continue
printf ' [FAIL] %s:%s 用了裸赋值(应改为 `agentmail_require <命令…>`)\n' "$f" "${h%%:*}" >&2
fails=$((fails + 1))
done <<< "$hits"
# ★★ 这里**也不能**写成 `strip_comments "$f" | grep -nE …`(我第一版就是):
# 它**目前**不出事只是因为选了 `-n`(`grep -n` 要读完全部输入才退出 ⇒ 没有早退
# ⇒ 生产者不会收到 SIGPIPE)。**换一个字母就翻面** —— pi 2026-09-25 做了可判实验:
# `-n` → `-q` 后,三个调用者的裸赋值**全部被静默漏掉**(rc 由 1 变 **0**)。
# 我复现同一实验:现行 `-n` 报 **3** 个违规 / rc=1;改成 `-q` 报 **0** 个 / rc=**0** ✓
# ⇒ "恰好选了不早退的那个选项"**不是正确性,是运气** —— 而它考的是同一件事:
# 判据读输入的**那一步**能不能把对象静默丢掉(`-n` 与 `-q` 只差"要不要早退")。
# ⇒ 与 :78-82 一致:**先收集到变量,再对变量匹配(无管道)**。
#
# ★★ 第二版(pi 指出我"只修了一半"之后重做)—— 三样一起做,才真的闭掉:
# ① **去管道**(`<<<` here-string)⇒ 生产者不会收 SIGPIPE,`-n`/`-q` 都不再有 141;
# ② **两条独立通道**:"有没有违规"用 `grep -q` 的**退出码**;
# "哪几行"用 `grep -n` 的**输出**。两者各自可靠 ⇒ 输出为空不再能冒充"没有违规"。
# ③ ★ **不一致时 fail-closed**:若"退出码说有"而"行号通道没给出行号"
# (例如有人把取行号那处的 `-n` 写成 `-q` —— `-q` 什么都不打印),
# 则**判据内部自相矛盾** ⇒ **按"有违规"计**,并明确报出这个矛盾。
# 我实测过少了 ③ 会怎样:只做 ①② 时,把取行号那处改成 `-q`
# ⇒ 报 **0** 个违规、rc=**0**(3 个违规**全部静默漏掉**)—— 判定又一次落在输出通道上。
# ⇒ 记法: **"计数"与"证据"若走同一条通道,那条通道静音就等于"没有"**;
# 要要么分通道、要么**规定静音=失败侧**(本处取后者)。
body="$(strip_comments "$f")"
_had=0
grep -qE '^[[:space:]]*AGENTMAIL_REQUIRE=' <<< "$body" && _had=1
_cnt=0
while IFS=: read -r _ln _rest; do
[ -z "${_ln:-}" ] && continue
printf ' [FAIL] %s:%s 用了裸赋值(应改为 `agentmail_require <命令…>`)\n' "$f" "$_ln" >&2
_cnt=$((_cnt + 1))
done < <(grep -nE '^[[:space:]]*AGENTMAIL_REQUIRE=' <<< "$body")
if [ "$_had" -eq 1 ] && [ "$_cnt" -eq 0 ]; then
printf ' [FAIL] %s 有裸赋值,但**取行号的那条通道没给出任何行号** —— 判据内部不一致;\n' "$f" >&2
printf ' 按 fail-closed 记作 1 处违规(两条通道必须都给得出,否则判定就落在单条通道上)。\n' >&2
_cnt=1
fi
fails=$((fails + _cnt))
unset body _had _cnt _ln _rest
done
if [ "$fails" -gt 0 ]; then