Files
MailUI4Agents/deploy/check-require-declaration.sh
JianFeeeee 8922ddc468 ★★ 修掉 pi 95e50126 所指**同一形状的第三处**(:407/:408 的 grep | head -1)—— 但实测它**不是同一个缺陷**(失效方向相反),并纠正我自己两次误判
★★ 形状: `_ph="$(grep -F "${_am_nonce}HEAD" <<< "$_probe_out" | head -1 | cut -d: -f1)"`
   与 pi 批的 `:102`(`strip | grep -n`)**逐字同构**: 都是"管道 + 会早退的读者"
   ⇒ 我若只报不改,就是**第三次**"知道规则、写在注释里、自己没照做"

★★★ 但它与 `:102` **不同族** —— 我实测把"形状相同"与"缺陷相同"分开了:
   · `:102` 静音 ⇒ `hits` 空 ⇒ 报"裸赋值 **0** 处" ⇒ 落**绿**侧(fail-open)✗
   · 本处静音 ⇒ `_ph` 空 ⇒ `:410 [ -z "$_ph" ]` ⇒ 落**红**侧(fail-closed)✓
     逐场景验证: `_ph` 空 / 取到错行 / 顺序反 ⇒ **三种都判红**
   · ★ 且生产者**输出上界=1 行是构造性的**(`_am_nonce` 运行期随机 ⇒ 静态 `$f` 不可能预先含它),
     不是"尺寸恰好装得下" ⇒ 我把巨量输出强喂给它(40 次): 141 出现 **40/40**,
     而**取值仍恒正确**(`head` 已拿到它要的那一行)—— 与 `:102` 的"值被丢掉"也不同
   ⇒ 记法: 修一条规则时,**"这处违不违规"与"这处会不会因此出错"是两个问题** ——
     前者看形状,后者看**失效方向**;**只有后者决定它是不是同一个 bug**
   ⇒ 仍改成无管道写法(`grep -m1` + `${x%%:*}`,成本为零),并让规则在全文件一致,
     免得下一次有人动掉 nonce 那个界时这处才第一次暴露

★★ 同时纠正我自己的**两次误判**(都在本轮现场抓到,记下来):
   ① 我报"放宽探针谓词 ⇒ 探针静默失效(rc=0)" —— **错的**: 我那次变异的
      `grep -E "." | head -1` 取到的**第 1 行正是探针行** ⇒ 它是**等价变异**,不是失效
   ② 我报"141 时取值损坏 40/40" —— **是我把期望值写错了**(写成 `1`,实际该是 `AMHEAD`);
      实测取值正确 **40/40**,取值错 **0/40**
   ⇒ 两次都是**判据写错**而非对象出错 —— 与我 `15c8f50` 那次"0 命中还写行号"同族

★ 验证: `bash -n` ✓;基线 rc=0(连测 5 次);注入裸赋值 ⇒ rc=1(非空转);
  探针目标变异仍被抓(删中间行 ⇒ rc=1);`criteria-hygiene` **10/10** 绿;生产未动
2026-09-26 01:39:56 +08:00

578 lines
45 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
# 政策:**调用者**声明"我依赖哪些外部命令"**必须**走动作 `agentmail_require`,
# 不许写裸赋值 `AGENTMAIL_REQUIRE="…"`。
#
# # 为什么有这条(一个实测出来的活缺陷)
#
# `deploy/lib/env-defaults.sh` 的 ③b 会检查调用者声明的命令在不在,
# 缺一个就 `exit 2` + 人话("命令不在"与"命令在但输出为空"必须分开 ——
# 后者会让下游把"工具缺失"读成"检查通过",见该文件里 journalctl 那两处假绿)。
#
# ★ 而那段检查**原先只在 `source` 期跑**,三个调用者却都是**先 source 后赋值**:
# install.sh source 131 / 赋值 135
# redeploy-gateway.sh source 42 / 赋值 46
# redeploy-plugin.sh source 52 / 赋值 56
# ⇒ source 那一刻表是**空的** ⇒ `[ -n "${AGENTMAIL_REQUIRE:-}" ]` 直接跳过
# ⇒ **那张表从未被检查过**。实测 A/B:
# AGENTMAIL_REQUIRE=__no_such_cmd . env-defaults.sh ⇒ rc=2(检查本身是好的)
# . env-defaults.sh; AGENTMAIL_REQUIRE=__no_such_cmd ⇒ rc=0(**漏过**)
#
# 修法是两处都挂检查(source 期 + report 内)。但那只堵住"**恰好忘了调 report**"——
# 病根是"**声明**(赋值)与**检查**(调用)是两个可分离的动作"。
# ⇒ 把声明本身做成**动作**:`agentmail_require <命令…>` —— 想声明就**必然**检查,
# "赋了值但那次检查没跑"这个状态**从表示上不存在**。
#
# # 为什么需要**这条判据**(而不是只靠"大家都记得用动作")
#
# 下一个新调用者仍可能写裸赋值 —— 而裸赋值**不报错、不警告**:
# 它只是让那张表在 source 后才存在,于是检查又一次静默失效(与上面那个缺陷同形)。
# ⇒ 判据的作用是**让"写裸赋值"立刻变红**,而不是靠人记住。
#
# # 射程(pi 2026-09-25 §四 要求写清它管什么、不管什么)
#
# **管的集合 = "调用者"**,定义为:`deploy/` 下 `*.sh` 里**真的 source 了**
# `env-defaults.sh` 的那些文件,**减去定义动作的那个库自己**。
# ⇒ 理由是"调用者"这个身份**由 source 这个动作定义**,不由文件名或目录猜。
# ⚠️ 我第一版按"`deploy/**/*.sh` 全扫"划范围,**实测立刻假红**:
# 它把 `env-defaults.sh:449`(`agentmail_require()` 函数体里的
# `AGENTMAIL_REQUIRE="$*"` —— **动作自己的实现**)判成违规。
# ⇒ 那是"动作的定义点",不是"调用者的使用点",两者必须分开。
#
# 管:调用者里**行首**的裸赋值 `AGENTMAIL_REQUIRE=…`(去掉注释后匹配)
# 不管:
# · 定义动作的那个库(`deploy/lib/env-defaults.sh`)—— **它不是自己的调用者**,
# 且它的 `agentmail_require()` 体内**必须**有那行赋值(那是实现,不是违规)
# · `AGENTMAIL_REQUIRE_SELF=…`(**不同名**,与 `=` 匹配天然不撞)
# · 注释里提到 `AGENTMAIL_REQUIRE` 的文字(strip 注释后再匹配 ⇒ 不误伤)
# · **没 source 本库的文件**:它们拿不到 ③b 检查,故"用哪种写法"与本政策无关
# (本政策只保证"**声明了就被检查**",不保证"人人都会声明")
# · **间接**赋值(`v=AGENTMAIL_REQUIRE; eval "$v=…"`、经 `read`/`env` 注入等)
# ⇒ 这类绕过本判据**仍可漏**。本判据盖"字面裸赋值",不声称盖住全部写法。
#
# 退出码:0 = 全走动作式;1 = 有裸赋值 **或**集合为空(防"判据静默空转");2 = 环境不足
set -uo pipefail
REPO="${AGENTMAIL_REPO:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
cd "$REPO" || { echo " [FAIL] 进不去仓库根 $REPO" >&2; exit 2; }
for c in grep awk sed find; do
command -v "$c" >/dev/null 2>&1 || { echo " [FAIL] 环境不足:缺少 $c" >&2; exit 2; }
done
LIB_REL='deploy/lib/env-defaults.sh'
# ★★ 不要写 `strip_comments "$f" | grep -q …`:`set -o pipefail` 下 `grep -q`
# 一命中就退出,`sed` 随即收到 SIGPIPE(rc=141),**管道整体返回 141** ⇒
# `if` 判**假** ⇒ 该文件被漏掉。而它退出的时机取决于缓冲区 ⇒ **结果不稳定**。
# 实测(我第一版):同一条命令两次分别找到 **2** 个与 **1** 个调用者
# (三个调用者只认出一两个),而 `rc=141` 被 `if` 静默吞掉 ——
# 一个**不稳定且偏少**的集合,正好会让"裸赋值 0 处"变成假绿。
# ⇒ 正确做法:先把去注释结果**收集到变量**,再对它做匹配(无管道、无 SIGPIPE)。
#
# ── 去注释的**唯一**实现(全文件只有这一处 `sed`)。
# ★★ pi `597086ad` 实测抓到我"说了但没做到"(这是我的第三次同形):
# 我注释写"正向对照与正式扫描**都走这一个函数**",而旧版 `_scan_text`
# **另写了一份 `sed`**、正式扫描又走 `strip_comments` ⇒ **其实有三份去注释实现**。
# 实测(只把 `strip_comments` 改成"删掉整行"):
# ⇒ 正式扫描静音,而**自检不响**(自检走 `_scan_text` 那份 sed)
# ⇒ 我上一封"自检盖住整条管线 / 已盖住 pi 标的残余"的结论**作废**。
# ⇒ 修法: 去注释收敛到**这一个函数**,匹配收敛到 `_scan_stripped`,
# 正向对照与正式扫描**都经由它们** ⇒ 任一层漂移都同时影响两者 ⇒ 自检必响。
strip_text() { sed 's/#.*$//' <<< "$1"; }
AM_SCAN_RE='^[[:space:]]*AGENTMAIL_REQUIRE='
# 对**已 strip** 的文本做匹配(唯一的匹配实现)
_scan_stripped() { grep -nE "$AM_SCAN_RE" <<< "$1"; }
# 文本 ⇒ 违规行号(= 去注释 + 匹配),供**正向对照**用
_scan_text() { _scan_stripped "$(strip_text "$1")"; }
# ★★★ 调用者判定专用的去注释:**引号感知**的逐字符扫描(不是 sed 近似)。
# pi `9bb3cc32` 实测出上一版"词首规则"`s/\(^\|[[:space:]]\)#.*$//` 有**两处方向相反**的残留:
# ① **假阴**(未全闭): `. "$A #B/deploy/lib/env-defaults.sh"` —— 引号内「空格+`#`」
# 被误当注释 ⇒ 截成 `. "$A` ⇒ 又丢了调用者身份 ⇒ rc=0、调用者数仍 3
# ② **假红**: `. /dev/null;# 提到 env-defaults.sh` —— `;` 后的 `#` **该**截而没截
# ⇒ 一个**不是**调用者的文件进了集合 ⇒ rc=1 并报它的裸赋值
# ★ 两处**方向相反**,且**单一正则/单个 sed 表达式修不了**:
# 只加元字符边界 ⇒ 修好 ②、对 ① 无改善;只追引号 ⇒ 修好 ①、对 ② 无改善。
# ⇒ 真规则需要**跨字符状态**(在引号里吗)⇒ 必须写成**自动机**,不能写成一次替换。
# ⇒ 本节实现 bash 手册的真规则: **以 `#` 开头的词**(词边界 = 行首 / 空白 / 元字符),
# 且 `#` 在**引号内**时不是词首(引号内容不被分词)。
# · `'…'` 内除 `'` 外全部字面;`"…"` 内 `\` 可转义、`"` 结束
# · `\#` 不是注释
# 实测(原型矩阵,本仓真实两行 + pi 两例 + 反例):
# `. "$REPO/deploy/lib/env-defaults.sh"` ⇒ 提到 ✓(算调用者)
# `. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/lib/…"` ⇒ 提到 ✓
# `. "${REPO#/home}/deploy/lib/env-defaults.sh"` ⇒ 提到 ✓(上一版假绿)
# `. "$A #B/deploy/lib/env-defaults.sh"` ⇒ 提到 ✓(pi ①,已闭)
# `. /dev/null;# 提到 env-defaults.sh` ⇒ 截断 ⇒ 不算 ✓(pi ②,已闭)
# `. /dev/null # 提到 env-defaults.sh` ⇒ 截断 ⇒ 不算 ✓
# `# . env-defaults.sh`(整行注释) ⇒ 空 ⇒ 不算 ✓
# `X="a#b"` ⇒ 不截断 ✓
# ⚠️ 射程: 这是**行级近似**的真 shell 规则(不处理 `$( … )` 里跨行的引号、heredoc、
# 续行拼接)—— 足以判"这一行是不是 source 行",**不**声称是通用 shell 词法器。
# ★ 与 ⑧b 的关系: 本节闭的是**调用者判定**(⑧c);正式违规扫描仍走 `strip_text`,
# ⑧b(注释语义)那一半**未**闭,本处不声称。
_strip_comments_lex() {
awk '
{
line = $0; n = length(line); out = ""; prev = 1; sq = 0; dq = 0; esc = 0
for (i = 1; i <= n; i++) {
c = substr(line, i, 1)
if (esc) { out = out c; esc = 0; prev = 0; continue }
if (sq) { out = out c; if (c == "\x27") sq = 0; continue }
if (dq) {
if (c == "\\") { out = out c; esc = 1; continue }
out = out c; if (c == "\"") dq = 0; continue
}
if (c == "\\") { out = out c; esc = 1; prev = 0; continue }
if (c == "\x27") { out = out c; sq = 1; prev = 0; continue }
if (c == "\"") { out = out c; dq = 1; prev = 0; continue }
if (c == "#") {
# 词首 = 行首,或前一个字符是空白/元字符
if (prev) break # 注释开始 ⇒ 本行到此为止
out = out c; prev = 0; continue
}
out = out c
if (c == " " || c == "\t" || c == ";" || c == "|" || c == "&" || \
c == "(" || c == ")" || c == "<" || c == ">") prev = 1
else prev = 0
}
print out
}' "$1"
}
# ★★★ "这一行/这段文本算不算 source 了本库" 的**唯一**判定实现。
# ★ 为什么必须是**唯一**的: 正式扫描与自检**都**经由它 ——
# 否则把调用点换掉(例如退回 `cat`)时自检**照样绿**,而域已经错了。
# 我第一版就是"自检直接调 `_strip_comments_lex`",实测**共模**
# (调用点被换成 `cat` 时自检不响)⇒ 抽成这一个函数,调用点也是它。
# 判法: 去注释(词法器)后,**行首**是 `.` 或 `source` 且**提到** env-defaults.sh。
_is_caller_text() {
local t
t="$(printf '%s\n' "$1" | _strip_comments_lex /dev/stdin)"
# ★★★ 谓词从"**行内出现子串**"收紧为"**指向本库**"(pi `ae164e2e` 实测打出的**假红**,我复现):
# 旧谓词 `.*env-defaults\.sh` 只要求**子串出现** ⇒
# `. /tmp/not-our-lib/env-defaults.sh.other` ⇒ rc=**1** 并报该文件裸赋值
# 而该文件**没有 source 本库** ⇒ **假红**(域偏宽)。
# ★ pi 的诊断对: **域的宽度该用谓词精度管,不该用阈值管** ——
# 空集守卫 / 下界守卫**都只兜"域偏小/空"**这个方向,加"上界阈值"只会把
# 一个**精确性**问题换成一个**调参**问题(阈值调多少都是猜)。
# ⇒ 收紧后的谓词: source 的**参数末尾**必须是 `…/lib/env-defaults.sh`(可带引号/空白),
# 即"**这一行指向的就是本库那个文件**",而不是"这一行里出现过这个字符串"。
# ① `[^[:alnum:]_.-]lib/env-defaults\.sh` ⇒ 前面必须是**路径分隔符或词边界**,
# 排除 `not-our-lib/env-defaults.sh`(`-lib` 前面是 `-`)这类同后缀异文件
# ② `["']?[[:space:]]*$` ⇒ **尾锚** ⇒ 排除 `env-defaults.sh.other` 这类**更长**的名字
# ★ 实测(本仓 3 个真实调用者 + 6 个反例):
# `. "$REPO/deploy/lib/env-defaults.sh"` ⇒ 调用者 ✓
# `. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/lib/…"` ⇒ 调用者 ✓(两处真实形态)
# `. "${REPO#/home}/deploy/lib/env-defaults.sh"` ⇒ 调用者 ✓
# `. "$A #B/deploy/lib/env-defaults.sh"` ⇒ 调用者 ✓
# `. /tmp/not-our-lib/env-defaults.sh.other` ⇒ 不算 ✓(pi 的假红例)
# `. /tmp/not-our-lib/env-defaults.sh` ⇒ 不算 ✓
# `. /tmp/x/lib/env-defaults.sh.other` ⇒ 不算 ✓
# ⚠️ 射程: 这是**行内文本**的判法,不解析变量实际取值 ——
# 若有人写 `. "$SOMEDIR/lib/env-defaults.sh"` 而 `$SOMEDIR` 指向别处,
# 本判据仍算它"调用者"(**偏宽**、方向安全,会多查一个文件而不是漏查)。
grep -qE '^[[:space:]]*(\.|source)[[:space:]].*[^[:alnum:]_.-]lib/env-defaults\.sh["'"'"']?[[:space:]]*$' <<< "$t"
}
# ① 找"调用者":非注释行里以 `. ` 或 `source ` 起头、且提到 env-defaults.sh 的文件。
callers=""
for f in $(find deploy -name '*.sh' -type f | sort); do
[ "$f" = "$LIB_REL" ] && continue # 定义点不是调用点(见射程)
# ★★★ 调用者判定**不看 strip 后的文本**(pi `30ee8ff2` 实测的**假绿**,我复现并采纳):
# 若 source 行里含 `${VAR#…}` 这类**参数展开**(`#` 不是注释),`strip_text` 会
# 从那个 `#` 起把整行截断 ⇒ 行内**不再提及** `env-defaults.sh` ⇒ 该文件**静默退出调用者集合**
# ⇒ 它的裸赋值**根本不被查**(连进都没进集合)⇒ **假绿**。实测(`d3c74ec` 的树上):
# `deploy/redeploy-nc.sh`:
# . "${REPO#/home}/deploy/lib/env-defaults.sh"
# AGENTMAIL_REQUIRE="z" ← 真违规
# ⇒ rc=**0**,仍报"3 个调用者,裸赋值 0 处"(z 被漏)
# 对照: 把 source 行换成 `. "$REPO/deploy/lib/env-defaults.sh"` ⇒ rc=**1** ✓
# ★ 而**三条不变量全过**(首尾探针 / 行号算术 / 逐行局部不变量)—— 因为删的首字符
# **确实**是 `#`、stripped 也**确实**是 raw 的前缀 ⇒ ⑧a 那套"形状"检查**看不见它**。
# ⇒ 修法: 调用者判定改用**词首注释规则**(shell 的真实规则: `#` 只在**词首**才是注释)。
# ★ 不能用"raw 原文"了事: 我实测过 raw 版会把
# `. other.sh # 注释里提到 env-defaults.sh`
# 当成调用者 ⇒ **假红**(安全方向,但仍是错的)。
# 也不能用 `strip_text`(它是 `s/#.*$//`,正是被 pi 抓到的那个错)。
# ★★ 第二版(pi `9bb3cc32` 报回**两处方向相反**的残留后重做):
# 上一版是"词首规则"的**单条 sed 近似**,它同时缺**元字符边界**与**引号状态**
# ⇒ 一处假阴、一处假红(两例见 `_strip_comments_lex` 上方的实测记录)。
# ⇒ 现改为调用 `_strip_comments_lex`(**引号感知的逐字符扫描**)——
# 理由: 真规则要"当前是否在引号内"这个**跨字符状态**,一次 `s///` 表达不了。
# ⚠️ 射程(本处**只**修调用者判定这一半): 正式违规扫描仍走 `strip_text`
# (`s/#.*$//`)⇒ 若某**违规行**本身含 `${VAR#…}` 且截断后不再匹配 `AM_SCAN_RE`,
# 那仍会漏(属 ⑧b 的**语义**半,真边界)—— 本处**不**声称那半已闭。
# ⇒ 调用点只用 `_is_caller_text`(它内部走词法器)。
# ⚠️⚠️ **射程(我实测后收窄 —— 第一版我在这里写宽了)**:
# 本对照调的是**函数** `_is_caller_text`,**不是调用点**。
# 所以它盖住的是: 词法器(引号状态/元字符边界)+ 那条正则。
# 它**盖不住**: 有人把**调用点**改掉(`_is_caller_text "$(cat "$f")"` → `true`/`false`)——
# 那种改法**不改函数**,自检照样绿(我实测确认)。
# ⇒ 该方向由别的层兜: 域**空**由 ② 空集守卫、域**偏窄**由 ②′ 下界守卫;
# 而域**偏宽**(把非调用者算进来 ⇒ 假红)**当前没有守卫** —— 我**不**声称它已被盖住。
if _is_caller_text "$(cat "$f")"; then
callers="$callers $f"
fi
done
# shellcheck disable=SC2086
set -- $callers
n_callers=$#
# ② 防空转:集合为空 ⇒ 判**红**,不是"通过"
if [ "$n_callers" -lt 1 ]; then
echo " [FAIL] 一个 source env-defaults.sh 的调用者都没找到 —— 大概率是路径/目录变了。" >&2
echo " 本判据的集合是「调用者」,集合空了它就**静默空转**(恒绿)⇒ 必须判红。" >&2
exit 1
fi
# ②′ ★★ **下界守卫**(2026-09-25 我自己实测出来的洞,不是 pi 报的):
# ② 只判"集合**空**",而**把发现域收窄**(例如把 `find deploy -name '*.sh'`
# 改成 `find deploy -name 'redeploy*.sh'`)时集合**非空**(2 个)⇒ ② 不响。
# 而收窄会让**域外的违规整批消失** ⇒ 判据报"裸赋值 0 处" ⇒ **假绿**。
# 实测: 树上 install.sh:135 是裸赋值,把 find 收窄到 `redeploy*.sh` ⇒
# rc=**0**、打"(2 个调用者,裸赋值 0 处)",且**自检 0 次**(自检看不见域的问题)。
# ⇒ 与 ② 的关系: ② 管"**全丢**"(集合空),本守卫管"**部分丢**"(域被收窄)。
# ⇒ 判法用**下界**: 调用者 ≥ 3(现状: install.sh / redeploy-gateway.sh / redeploy-plugin.sh)。
# 与 `client/electron/test/criteria-hygiene.test.mjs` 里 `gates.length >= 3` 同一做法。
# ⚠️ 残留(如实记): 下界只挡住"收到 3 个以下";若有人收到**恰好 3 个但换掉其中一个**
# (比如只扫 install.sh + 两个不存在的东西),本守卫仍看不出 —— 补它需要
# "已知调用者名单"这种**会漂**的硬编码,我选择不写,改在这里申报。
AM_CALLER_MIN=3
if [ "$n_callers" -lt "$AM_CALLER_MIN" ]; then
echo " [FAIL] 只找到 ${n_callers} 个调用者(下界 ${AM_CALLER_MIN})—— 发现域被**收窄**了。" >&2
echo " 本判据的域是「deploy/ 下所有 source env-defaults.sh 的 *.sh」;" >&2
echo " 域一收窄,域外的违规就整批消失,而集合仍非空 ⇒ **防空转守卫看不见** ⇒ 假绿。" >&2
echo " (实测: 收窄 find 后 rc=0 且打'裸赋值 0 处',而树上确有违规。)" >&2
exit 1
fi
fails=0
for f in "$@"; do
# ★★ 这里**也不能**写成 `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 个违规**全部静默漏掉**)—— 判定又一次落在输出通道上。
# ⇒ 记法: **"计数"与"证据"若走同一条通道,那条通道静音就等于"没有"**;
# 要要么分通道、要么**规定静音=失败侧**(本处取后者)。
#
# ★★★ 第四版(pi `3be8a091` 指出 ③ **有个我没写出来的前提**,我复现并采纳):
# ③ 的 fail-closed 只在"**两通道不一致**"时触发 ⇒ 它默认两条通道**失效不相关**。
# 而我的两条通道**共享 2 个东西**:(a)**同一个正则字面量**(两处逐字相同)
# (b)**同一个 `$body`**(都来自 `strip_comments "$f"`)。
# ⇒ ★ **单点改动同时哑掉两条时,它们"一致"(都沉默)⇒ `_had=1 ∧ _cnt=0` 不成立
# ⇒ fail-closed 恰好不触发**。pi 的实测我照做复现:
# 把两处正则字面量一起改成 `'^NOMATCH='`(树上**注入着 3 处裸赋值**)
# ⇒ rc=**0**,并打出"裸赋值 **0** 处"—— **与真实情况相反**。
# ⇒ 记法: **"两条通道"要成为独立证据,须"不存在单点改动能同时哑掉两条"** ——
# 条数 ≥ 2 **不是**独立性的判据;要检查它们共享的**字面量/变量/函数/上游命令**。
#
# ★ 补法是**正向对照**(判据自检),且**让它走与正式扫描同一个函数**:
# 用一个**已知违规样本**跑 `_scan_text`,若它匹配不到 ⇒ 说明"沉默"是**共模**的
# ⇒ 按**红**计(不能因为两条通道都不说话就当成"没问题")。
# ★ 为什么走同一函数而不是抄一份正则:抄一份 ⇒ 又出现"两套实现",而
# pi 标的残余正是"正则对、别的环节(body 被过度 strip)静音"——
# 走同一函数能把**整条管线**(strip + 匹配 + 行号输出)一起对照到,
# 而不只对照那个正则字面量。
_pc="$(_scan_text 'x=1
AGENTMAIL_REQUIRE="git go"' || true)"
case "$_pc" in
*AGENTMAIL_REQUIRE*) : ;; # 自检通过:能匹配到已知样本
*)
printf ' [FAIL] 判据自检失败:违规扫描连一个**已知样本**都匹配不到 —— 这是**共模失效**\n' >&2
printf ' (两条通道因同一原因一起沉默 ⇒ fail-closed 的不一致条件不成立)。按红计。\n' >&2
exit 1
;;
esac
unset _pc
# ★★★ 调用者判定的**正向对照**(2026-09-25,pi `9bb3cc32` 之后加):
# 为什么必须加: 调用者判定在**每条命令**开头就跑,是**域的定义**——
# 若它静音(认不出任何人)或过宽(把非调用者认成调用者),
# **正式扫描不会报错**,只会"少查/多查文件" ⇒ 与"共模失效"同型。
# ★★ 第一版我写成 "直接调 `_strip_comments_lex`",**实测在维度上更窄**(我自己抓到):
# 它只对照"词法器"这一环,**不**对照 `_is_caller_text` 里那条正则;
# ⇒ 抽成 `_is_caller_text` 后,对照覆盖"词法器 + 正则"**两环**(仍未覆盖调用点,见 ① 处射程)。
# ★ 走同一函数才能对照到"引号状态 + 元字符边界"这两处(而不是只对照一个正则)。
_pc_ok=0; _is_caller_text '. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/lib/env-defaults.sh"' && _pc_ok=1
_pc_ok2=0; _is_caller_text '. "$A #B/deploy/lib/env-defaults.sh"' && _pc_ok2=1
if [ "$_pc_ok" -ne 1 ] || [ "$_pc_ok2" -ne 1 ]; then
printf ' [FAIL] 判据自检失败(调用者判定):**已知调用者样本**没被认出 ——\n' >&2
printf ' ⇒ 域会**静默偏小**,域外的违规整批不查(防空转守卫只在"空集"时响)。按红计。\n' >&2
exit 1
fi
_pc_bad=""
_is_caller_text '. /dev/null;# 提到 env-defaults.sh' && _pc_bad="分号后的注释(该截没截)"
_is_caller_text '# . env-defaults.sh' && _pc_bad="${_pc_bad}整行注释"
# ★ pi `ae164e2e` 打出的**假红**(谓词过宽: 子串出现 ≠ 指向本库)—— 这条必须在反向对照里:
_is_caller_text '. /tmp/not-our-lib/env-defaults.sh.other' && _pc_bad="${_pc_bad}同名后缀的**别的**文件"
if [ -n "$_pc_bad" ]; then
printf ' [FAIL] 判据自检失败(调用者判定,反向):**非调用者样本**被判成了调用者(%s)——\n' "$_pc_bad" >&2
printf ' ⇒ 域会**偏大**,与该库无关的文件被拖进来报红(假红)。按红计。\n' >&2
exit 1
fi
unset _pc_ok _pc_ok2 _pc_bad
# ★ 反向对照(2026-09-25 我补,pi 的 §五 让我去看这一维)
# ⚠️ **我实测后降级了它的说法 —— 它不改门禁判定,只改"报的是哪一句"**:
# 把 `AM_SCAN_RE` 放宽到能匹配**合规**行(例如 `'='`)时:
# 旧版(无反向对照): rc=**1**,但报的是"deploy/install.sh:135 裸赋值" ——
# **指错了地方**(树上那条是**合规**的 `agentmail_require …`),
# 运维会去改一个**没有错**的地方。
# 新版: rc=**1**,报"判据自检失败(反向):**合规**样本被判成违规 —— 匹配太宽(假红)"。
# ⇒ ★ 退出码**两者相同** ⇒ 按我自己收的 ⑰′ 标准("被别的守卫顺手抓住 ≠ 这条路径有守卫"),
# 这条**不算新防线**,只算**诊断改进**(把"指错地方"变成"指对地方")。
# 我**不**声称它补了一个洞。
# ⚠️ 它**盖不住** pi 标的 §五 残余(`strip_text` 吞违规 ∧ 自检失败分支被打平):
# 那一对的成因是"**匹配太窄**",而反向对照管的是"**匹配太宽**" ⇒ 方向相反、互不覆盖
# (我实测: 那一对仍 rc=0 静默)。⇒ 我把 §五 记为**边界**,见文件末尾。
_nc="$(_scan_text 'x=1
agentmail_require git go' || true)"
case "$_nc" in
"")
: ;; # 反向对照通过:合规样本**不**被匹配 ✓
*)
printf ' [FAIL] 判据自检失败(反向):**合规**样本被判成了违规 —— 匹配太宽(假红)。\n' >&2
printf ' ⇒ 上面若也报了"裸赋值",那几处**不是真违规**,先查 `AM_SCAN_RE`。\n' >&2
exit 1
;;
esac
unset _nc
# 正式扫描:**先 strip 一次**(唯一实现 `strip_text`),两条通道都用这同一份结果。
#
# ★★ 逐文件探针(2026-09-25 pi 的 `head -132` 单点变异逼出来的,**我实测后加到两个探针**):
# pi 的变异: `strip_text` 尾接 `| head -132` —— **一行改动**同时打穿自检与下界守卫:
# ① 自检样本只有 **2 行** ⇒ `head -132` 对它**恒等** ⇒ 自检照样匹配 ⇒ 通过
# ② 下界守卫看的是"有几个文件 source 了"⇒ install.sh 的 source 在 **131** 行、
# 违规动作在 **135** 行 ⇒ `head -132` 保住 source、砍掉违规 ⇒ 仍是 3 个调用者 ⇒ 通过
# ③ 而正式扫描看不到违规行 ⇒ `_had=0 ∧ _cnt=0` ⇒ "一致" ⇒ fail-closed 不触发
# ⇒ 实测 rc=**0**,打"裸赋值 0 处"(树上注入着 1 处)⇒ **静默漏**
# ⇒ 打穿窗口 = **[source 行, 动作行) = [131,134],宽 4**(我逐个 head -N 量过)
# ★ 根因(pi 的记法,我收): **"共用同一实现"只在"样本能触发该变异"时才看得见** ——
# 一个**输入相关**的变异(head/truncate/长度阈值/locale)对**短样本**是恒等的,
# 于是"共用实现"反而保证了**两者一起静默**。
# ★ 补法: **对每个真文件也跑一次探针**(拿"真文件 + 已知注入"当样本),
# 这样"样本的形状"就是**被检对象本身的形状**,长度相关变异无法对它恒等。
# ⚠️ **我为什么用两个探针而不是一个**(我实测出来的,比 pi 的建议多一步):
# 只放**一个**探针时,它只能抓**从反方向**来的截断 ——
# 探针在**尾部**: `head -132` 砍掉它 ⇒ 能抓;`tail -132` **保住**它 ⇒ 抓不住
# 探针在**首部**: `tail -132` 砍掉它 ⇒ 能抓;`head -132` **保住**它 ⇒ 抓不住
# (我逐条实测:单探针在 head/tail 上各漏一半)
# ⇒ 所以**首尾各一个**,并**要求头探针的行号 < 尾探针的行号**:
# 截断必砍掉其中一端;`tac` 之类保内容但**反转顺序**的变换则由"顺序"这条抓。
# ⚠️ 申报射程: `sort -u` / `grep -v '^$'` 抓不到(它们保留违规行、不改变"有/无");
# 但实测它们对本文件**不构成假绿**(只可能改变**计数**,而计数类失效由 fail-closed 兜)。
_stripped="$(strip_text "$(cat "$f")")"
# ⚠️⚠️ **只靠首尾两个探针还有一个洞,是我实测出来的(pi 的建议里没有这一步)**:
# 把**中间某一行**(例如那条违规行)删掉、首尾都保住 ⇒ 两个探针**都在**、顺序也对
# ⇒ 探针通过,而违规行没了 ⇒ **假绿**。实测: `awk 'NR!=135'` / `sed '135d'` ⇒ rc=**0**。
# ⇒ 补一条**行号算术不变量**: 读入是逐行原位 ⇒ 尾探针行号必须**恰好等于**
# 头探针行号 + 内容行数 + 1(+1 是尾探针自己那一行)。
# 中间少一行 ⇒ 尾探针**上移** ⇒ 等式不成立 ⇒ 抓住。
# 实测(`probe` 原型): 干净 `h=1 t=802 n=800`(802 = 1+800+1 ✓);
# `sed 135d` ⇒ `t=801` ≠ 802 ⇒ 抓到;`head/tail -N` ⇒ 缺探针 ⇒ 抓到;
# `tac` ⇒ `h=802 t=1`(顺序反)⇒ 抓到;`grep -v '^$'` / `sort -u` ⇒ `t` 偏离 ⇒ 抓到。
# ★ 三个条件合起来 = "**这个过滤器是逐行原位的**"(保行数、保顺序、保两端)。
# ★★★ 但**固定标记**本身有个洞(pi `551d7d85` 实测打出的**假红**,我复现):
# 标记 `__AM_P_TAIL__` 是**字面量**,而它**出现在本判据自己的源码里**(:374/:379 两处)。
# 于是当**本判据文件自己被当成数据读**(域偏宽 / 自指)时,`grep -F … | head -1`
# 匹配到的是**源码里那行**,不是文件末尾之后的真尾探针 ⇒ 行号算术失败 ⇒ **假红**。
# ★ 这不是"域偏宽"的问题被误报 —— 是**探针的记号与数据不可分辨**。
# ⇒ 修法: 用**运行时 nonce**(`$RANDOM` + pid)代替固定字面量 ⇒
# 该记号**不可能**预先出现在任何静态文件里 ⇒ 碰撞**不可表示**(比"再多加几个下划线"根本)。
# ⚠️ 射程: 这消掉"记号与数据碰撞"这一类;**不**改变"域偏宽"本身是否该红(那是谓词的事,见 `_is_caller_text`)。
_am_nonce="AM${RANDOM}${RANDOM}${$}Z"
_probe_in="AGENTMAIL_REQUIRE=\"${_am_nonce}HEAD\"
$(cat "$f")
AGENTMAIL_REQUIRE=\"${_am_nonce}TAIL\""
# 内容行数与探针**同一条读取路径**(都经 `printf '%s\n'` 补回被命令替换吃掉的尾换行)
_probe_n="$(printf '%s\n' "$(cat "$f")" | awk 'END{print NR}')"
_probe_out="$(_scan_stripped "$(strip_text "$_probe_in")" || true)"
# ★★ 这里也**不能**写成 `grep -F … <<< "$_probe_out" | head -1 | cut -d: -f1`(我第三版就是):
# 与上面那条**同一形状** —— `head -1` 一拿到就退出 ⇒ 生产者收 SIGPIPE ⇒ pipefail 下整体 141。
# ★ 它的安全性**与 :102 不同族**,我实测分清了(别把"形状相同"当成"缺陷相同"):
# · :102 静音 ⇒ `hits` 空 ⇒ 报"裸赋值 0 处" ⇒ 落**绿**侧(fail-open)✗
# · 本处静音 ⇒ `_ph` 空 ⇒ 下面 `[ -z "$_ph" ]` ⇒ 落**红**侧(fail-closed)✓
# (我逐场景验过: `_ph` 空 / 取到错行 / 顺序反 ⇒ **三种都判红**)
# · 且生产者的**输出上界=1 行是构造性的**(`_am_nonce` 是运行期随机 ⇒ 静态 `$f` 不可能预先含它),
# 不是"尺寸恰好装得下"⇒ 我把巨量输出强喂给它(40 次): 141 出现 **40/40**,
# 而**取值仍恒正确**(`head` 已拿到它要的那一行)—— 与 :102 的"值被丢掉"也不同
# ⇒ 结论: **同形状、不同缺陷**。我仍改成无管道的写法(成本为零,且让规则在全文件一致,
# 免得下一次有人动掉 nonce 那个界时这处才第一次暴露)。
# ⇒ 记法: 修一条规则时,**"这处违不违规"与"这处会不会因此出错"是两个问题** ——
# 前者看形状,后者看**失效方向**;只有后者决定它是不是同一个 bug。
_phead="$(grep -m1 -F "${_am_nonce}HEAD" <<< "$_probe_out" || true)"; _ph="${_phead%%:*}"
_ptail="$(grep -m1 -F "${_am_nonce}TAIL" <<< "$_probe_out" || true)"; _pt="${_ptail%%:*}"
_pexp=$(( ${_ph:-0} + _probe_n + 1 ))
if [ -z "$_ph" ] || [ -z "$_pt" ] || [ "$_ph" -ge "$_pt" ] || [ "$_pt" -ne "$_pexp" ]; then
printf ' [FAIL] %s 逐文件探针失败:读入这一环**不是逐行原位**的 —— 它在丢弃或重排内容。\n' "$f" >&2
printf ' head 探针=%s,tail 探针=%s,内容行数=%s(要求 tail == head + 行数 + 1 = %s)。\n' \
"${_ph:-空}" "${_pt:-空}" "$_probe_n" "$_pexp" >&2
printf ' ⇒ 三个条件缺一不可: 两端都在(抓 head/tail 截断)、顺序对(抓 tac)、\n' >&2
printf ' 行号算术成立(抓"只删中间某一行"—— 这一条只靠首尾探针**抓不到**)。\n' >&2
printf ' ⇒ 这一环静音时,**自检看不出来**(自检样本只有 2 行,长度相关变异对它恒等),\n' >&2
printf ' 下界守卫也看不出来(它只数"有几个文件 source 了",不看行号)。\n' >&2
printf ' ⇒ 按红计(绝不能因为两条通道都不说话就当成"没问题")。\n' >&2
exit 1
fi
unset _probe_in _probe_out _probe_n _ph _pt _pexp _am_nonce
# ★★★ 逐行局部不变量(2026-09-25 **pi 提的非循环补法**,我实测后采纳):
# 上一节的首尾探针只锁"**行数 / 顺序 / 两端**"(**形状**),锁不住"**某一行内容被抹**"。
# pi 的补法: 对**每一行**要求
# ① stripped_i 是 raw_i 的**前缀**(只许删尾部)
# ② 被删的**第一个**字符必须是 `#`(只许删注释后缀)
# ⇒ 为什么**不循环**(这是关键,我上轮把它的可闭性判错了):
# raw 由 `cat` **独立**读,stripped 由 `strip_text`(**被检对象**)读 —— 两次**独立读取**;
# 而"只删注释后缀"是**规格声明**,**不是**被检对象的产物。
# ⇒ 所以**不需要**"先知道正确内容"(我上轮的循环论证不成立)。
# ⇒ 实测(注入 1 处违规的树上):
# `sed '135s/.*//'`(**我上轮申报的 ⑧ 盲区**)⇒ **抓到**(第135行首删字符='A' ≠ '#')
# `sed '56s/.*/ /'` ⇒ 抓到(不是前缀);`sed 's/AGENTMAIL_REQUIRE//'` ⇒ 抓到(不是前缀)
# 真 strip ⇒ **合规**(**非恒红** ✓)
# ⚠️ 射程(pi 也这么申报,我照抄): 它盖"**删的不是注释后缀**"这一类
# (= **形状**盲区,**现已闭**);对"**删对了注释、但那个 `#` 本不该被当注释**"
# (如字符串字面量里的 `#`)**仍不管** —— 那才是真需要"正确内容"的**语义**盲区。
# ★★ 我上轮把这两类**合并成一条**、整条申报成"真边界",是**错的**:
# 形状那半**可闭**(本节即闭),语义那半才是边界。⇒ 记法:
# **申报"闭不了"之前,先问缺的信息是"形状"(换次读取就有)还是"语义"(要先有正确内容)** ——
# 我上轮就是拿语义的循环性去论证形状那半也不可闭,于是**多申报了一个边界**。
_loc_bad="$(
awk '
NR==FNR { r[FNR]=$0; rn=FNR; next }
{ s[FNR]=$0; sn=FNR }
END {
m = (sn<rn)?sn:rn
for (i=1;i<=m;i++) {
if (substr(r[i],1,length(s[i])) != s[i]) { printf "第%d行 不是前缀(改了行中间的字符)\n", i; continue }
d = substr(r[i], length(s[i])+1)
if (d != "" && substr(d,1,1) != "#") printf "第%d行 首删字符=%s(不是注释后缀)\n", i, substr(d,1,1)
}
for (i=m+1;i<=rn;i++) {
t = r[i]; sub(/^[ \t]*/, "", t)
if (t != "" && substr(t,1,1) != "#") printf "第%d行 被整行丢弃且非注释\n", i
}
if (sn > rn) printf "stripped 比 raw 多 %d 行\n", sn-rn
}' <(printf '%s\n' "$(cat "$f")") <(printf '%s\n' "$_stripped")
)"
if [ -n "$_loc_bad" ]; then
printf ' [FAIL] %s 逐行局部不变量失败:读入环**改了行的内容**(不只是丢行/重排)。\n' "$f" >&2
printf '%s\n' "$_loc_bad" | sed 's/^/ /' >&2
printf ' ⇒ 首尾探针锁"行数/顺序/两端",锁不住"某一行被抹/被改"。按红计。\n' >&2
exit 1
fi
unset _loc_bad
_had=0
grep -qE "$AM_SCAN_RE" <<< "$_stripped" && _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 < <(_scan_stripped "$_stripped")
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 _stripped _had _cnt _ln _rest
done
if [ "$fails" -gt 0 ]; then
cat >&2 <<'MSG'
为什么必须走动作:`AGENTMAIL_REQUIRE="…"` 只是**赋值**,而检查在
`env-defaults.sh` 的 ③b(source 期)—— 赋值在它**之后** ⇒ 那次检查读到空表
⇒ **整张表从未被检查过**(2026-09-25 实测的活缺陷)。
后续那次检查(report 内)只接住"**记得调 report**"的情形;
写成动作则"赋值与检查"**不可分离** ⇒ 忘不忘都不影响。
改成:
agentmail_require git go npm node curl systemctl flock
MSG
exit 1
fi
awk -v n="$n_callers" 'BEGIN{printf " 通过 调用者声明全走 `agentmail_require` 动作(%d 个调用者,裸赋值 0 处)\n", n}'
exit 0
# ══════════════════════════════════════════════════════════════════════════════
# 申报的边界(不是"待修项",是**今天不判**的地方)
# ══════════════════════════════════════════════════════════════════════════════
# ①②③ 是"判据读输入之后"的失效;④ 是"哪些对象进入判据";⑤ 是"谁来验验证器"。
#
# ⑤ ~~正向对照**保护不了它自己**~~ ⇒ ★ **已闭(2026-09-25 晚,逐文件探针)**,
# 但保留这段历史,因为**我当时的"闭不了"判断本身是错的**,而错法有教学价值:
# 当时(a)`strip_text` 尾接吞掉违规行 ∧(b)自检失败分支不可达 ⇒ rc=0 静默。
# 我判"要闭需一条独立于本文件的检查 ⇒ 无限后退 ⇒ 记为边界"。
# ★ 错在哪: 我把"独立"理解成"**另一个文件**",而真正需要的是
# "**另一次观察**" —— 逐文件探针(见上文)用**被检对象自己的形状**当样本,
# 它仍在本文件里,却**不经过自检那条判断**(直接看 `_scan_stripped` 的行号算术)。
# ⇒ 实测: 同一 ①∧② 合取,现在 rc=**1**(报"逐文件探针失败")。
# ⇒ 记法: **"无限后退"论证要先问"上一层与这一层是不是同一次观察"** ——
# 若新一层换了**观测对象**(从"已知样本"换成"被检文件本身"),它不是后退,是**换面**。
# 我当时把"换面"误判成了"后退",于是把一个可闭的点申报成了边界。
# ⑥ 间接赋值不判(eval/read/env 注入),只判**字面裸赋值**。
# ⑦ 下界守卫只挡"收到 <3 个调用者";"恰好 3 个但换掉其中一个"不判(需会漂的名单)。
# ⑧ ★★ **已闭一半 —— 我上轮整条申报成"真边界"是错的**(pi `02e3fd46` 纠正,我复现后采纳)。
# 我原写: "只抹掉违规行内容(`sed '135s/.*//'`)⇒ 首尾探针看不见 ⇒ **真边界**"。
# ★ 错在哪(我自己的论证有洞): 我说"要判它需逐行内容对照,而那要求判据先知道正确内容 ⇒ 循环"。
# pi 指出**不需要"正确内容",只需要"strip 的规格"** —— 两者不同。补法(我已落盘,见上文):
# 对每行要求 ① stripped_i 是 raw_i 的**前缀** ② 被删的**第一个**字符必须是 `#`
# ★ 为什么**不循环**: raw 由 `cat` **独立**读、stripped 由 `strip_text`(被检对象)读
# ⇒ **两次独立读取**;而"只删注释后缀"是**规格声明**,不是被检对象的产物。
# ⇒ 实测: `sed '135s/.*//'`、`'135s/.*/ /'`、`'56s/.*/ /'`、`'s/AGENTMAIL_REQUIRE//'`
# **全部 rc=1**;真 strip ⇒ rc=**0**(非恒红)✓ ⇒ **形状盲区已闭**。
# ★★ 于是原 ⑧ 要**拆成两半**(这是本轮真正的记法):
# ⑧a **形状**盲区("某行的字符被抹/被改")⇒ **可闭**(已闭,见上文逐行局部不变量)
# ⑧b **语义**盲区("删对了 `#`,但那个 `#` 本不该当注释" —— 如 `X="a#b"` 里引号内的 `#`)
# ⇒ **真边界**。实测: `X="a#b"` ⇒ 真 strip 截成 `X="a`(**规格本身错**),
# 而局部不变量**通过**(删的确实是 `#`)⇒ 它看不见。要判它需要**正确内容/真正的解析器**。
# ⇒ 记法: **申报"闭不了"之前,先问缺的信息是"形状"还是"语义"** ——
# 形状: "换一次读取就有"(可闭);语义: "要先有正确内容"(真边界)。
# ★ 我上轮就是**拿语义的循环性去论证形状那半也不可闭**,于是**多申报了一个边界**。
# ⚠️ 与 ⑤ 的关系: ⑤ 也是"我把可闭的误判成边界"(拿"同一次观察的后退"当理由)——
# ⑧a 与 ⑤ 是**同一类错误**(论证里的"循环/后退"其实不成立),⑧b 才与 ⑥⑦ 同级。
# ⑧c ★★★ **⑧b 还要再拆一半**(pi `30ee8ff2` 实测的**假绿**,我复现后已修 —— 见 ① 调用者判定):
# pi 指出 ⑧b 不只是"规格本身错"(无害),它有一条**真实可达的假绿**:
# 当"不该当注释的 `#`"出现在**唯一那处 `env-defaults.sh` 提及之前**时,
# 整行 strip 后**不再提及** env-defaults.sh ⇒ **调用者身份丢失** ⇒ 该文件被跳过
# ⇒ 它的裸赋值**连查都不查**(不是漏判,是**根本没进集合**)⇒ **假绿**。
# 实测(`d3c74ec` 的树上,我复现): `deploy/redeploy-nc.sh`
# `. "${REPO#/home}/deploy/lib/env-defaults.sh"` + `AGENTMAIL_REQUIRE="z"` ⇒ rc=**0**
# 现实形态同样命中: `. "$(dirname "${BASH_SOURCE[0]#/x}")/lib/env-defaults.sh"` ⇒ rc=**0**
# ★ 而**三条不变量全过**(首尾探针/行号算术/逐行局部不变量)—— 因为删的首字符
# **确实**是 `#`、stripped 也**确实**是前缀 ⇒ ⑧a 那套"形状"检查**看不见它**。
# ⇒ ★ 已修(① 调用者判定改用**词首注释规则** `s/\(^\|[[:space:]]\)#.*$//`):
# 参数展开 `#` / 引号内 `#` **不再**被当注释 ⇒ 调用者不再丢失 ⇒ 上面两例都 rc=**1** ✓
# 且**不引入假红**: `foo.sh # 注释里提到 env-defaults.sh` **仍不算调用者** ✓
# ⇒ ★★ 于是 ⑧b 拆两半:
# ⑧c **调用者判定**(判"哪一行是 source 行"只用到**行首形态**,与注释后缀无关)
# ⇒ **可闭**(已闭)—— 我原先把"strip 的实现"与"调用者判定"**耦合**在一起
# ⑧b **注释语义本身**(strip 对**行内**`${V#x}` / 引号内 `#` 的截断会不会
# 让**违规行自己**不再匹配 `AM_SCAN_RE`)⇒ **真边界**
# 实测残余: `AGENTMAIL_REQUIRE#="z"` 被 strip 成 `AGENTMAIL_REQUIRE` ⇒ 不再匹配 ⇒ rc=0
# (但这行**本来就不是合法赋值** ⇒ 落在射程边缘;合法形态 `AGENTMAIL_REQUIRE="${V#x}"`
# 截断后仍是 `AGENTMAIL_REQUIRE="${V` ⇒ **仍匹配** ⇒ rc=1 ✓ 不漏)
# ⇒ ★★★ 记法(本轮第三格): **"耦合"本身会被误报成"边界"** ——
# 我原来说"⑧b 是语义边界",其实里面**掺着一处没必要的耦合**(调用者判定依赖 strip)。
# 判法: 申报边界前问一句 **"这个信息真的只能由被检对象给出吗,还是我顺手复用了它?"**
# ⇒ ⑧a 是"换错观察对象"、⑧c 是"**多余的依赖**" —— 两次都把可闭的说成了边界。
# ══════════════════════════════════════════════════════════════════════════════