采纳 pi 反提案: 声明动作化 agentmail_require + 新判据 check-require-declaration.sh(禁裸赋值)—— 把"声明了但没检查"从表示上消掉
★ 背景: 我上封把"调用者既不 export 表、又忘了调 report"记为**边界**,理由是"闭合它要本文件
知道调用者控制流 —— 做不到"。pi 指出**前提可以换掉**(我复核采纳):
病根不是"控制流不可知",而是"**声明**(赋值)与**检查**(调用)是两个可分离的动作"。
⇒ 把声明做成**动作**: `agentmail_require <命令…>` = 赋值 + 立刻检查 ⇒ 该状态**不存在**。
★ 改动:
· env-defaults.sh 加 `agentmail_require()`(内部就是赋值 + agentmail_env_check_require)
· 三个调用者由 `AGENTMAIL_REQUIRE="…"` 改为 `agentmail_require …`(各 1 行)
也顺带把"重新导出为环境变量"的写法去掉了 —— 表只在本进程内被 ③b 读,不需要 export
· 新判据 `deploy/check-require-declaration.sh`(755): deploy/ 下的**调用者**不得写裸赋值
· install.sh 接线,走 `CHECK_GATE_RC` **累积**通道(照 check-file-modes.sh 的既有做法:
直接调会在 set -e 下中止,把后面的收尾诊断全吃掉)
★★ 判据自己的两个 bug,都是**实测**抓出来的(不是审出来的):
① 假红: 我第一版按"`deploy/**/*.sh` 全扫"划范围 ⇒ 立刻把 `env-defaults.sh:449`
(`agentmail_require()` 函数体里的 `AGENTMAIL_REQUIRE="$*"` —— **动作自己的实现**)
判成违规。⇒ 改为按**性质**划: "调用者"= **真的 source 了本库**的文件,
减去定义动作的那个库自己("调用者"这个身份由 source 这个动作定义,不由目录猜)。
② ★ 假阴且**不稳定**: `strip_comments "$f" | grep -q …` 在 `set -o pipefail` 下,
`grep -q` 一命中就退出 ⇒ `sed` 收 SIGPIPE(rc=141)⇒ **管道整体 141** ⇒ if 判假 ⇒ 漏文件。
实测同一个脚本两次分别找到 **2** 个与 **1** 个调用者(实际 3 个),
而 rc=141 被 if 静默吞掉 ⇒ 集合**偏少且不稳** ⇒ "裸赋值 0 处"会是**假绿**。
⇒ 改为先收集到变量、再 `grep <<<"$body"`(无管道)。修后重复 10 次**稳定 3 个**。
★ 验证(全部真跑):
· 基线 rc=0(3 个调用者,裸赋值 0 处);重复 10 次稳定 3
· 变异①: 把 redeploy-plugin.sh 改回裸赋值 ⇒ rc=**1** ✓
· 变异②: 注释掉一个调用者的 source(仍剩 2)⇒ rc=0 ✓ 非空转
· 变异③: 三个 source 全注释 ⇒ **集合空** ⇒ rc=**1** ✓(防空转,不是恒绿)
· 每个变异后**还原**并复测 rc=0;`git diff --numstat` 确认只有预期行
· criteria-hygiene **7/7** 绿(新 check-*.sh 已被 install.sh 真调用)
· pi 的 env-guard **17/17** 绿;五个脚本 bash -n 全过;生产未动
★ 残留(判据头已如实写): 盖"字面裸赋值",**不盖间接赋值**(经 eval/read/env 注入)——
补它需 shell 语义分析,超出"一条 grep 型判据"的射程。
This commit is contained in:
@ -420,9 +420,13 @@ unset _am_avail_kb
|
||||
# (三个调用者都在赋值后**立刻**调它)。两处都留,因为两条路都要接住:
|
||||
# · source 期那次 —— 接"表由**环境**提供"(父进程 export / CI 注入)
|
||||
# · report 期那次 —— 接"表由**调用者**赋值"(本仓三个调用者都是这一种)
|
||||
# ⚠️ 残留缺口(如实申报,不假装已闭合):若某个调用者**既不 export 表、又忘了调
|
||||
# `agentmail_env_report`**,则两次都不跑 ⇒ 仍会漏。闭合它需要"report 没被调也要红",
|
||||
# 而那要求本文件知道调用者的控制流 —— 做不到。⇒ 记为已知边界,不写成"已修好一切"。
|
||||
# ⚠️ 残留缺口(**已由 pi 的反提案闭合,见下** —— 原文如实保留一段):
|
||||
# 若某个调用者**既不 export 表、又忘了调 `agentmail_env_report`**,则两次都不跑 ⇒ 仍会漏。
|
||||
# 我原判"闭合它需要本文件知道调用者的控制流(report 没被调也要红)—— 做不到"。
|
||||
# ★★ pi 2026-09-25 指出**前提可以换掉**(我复核采纳):病根不是"控制流不可知",
|
||||
# 而是"**声明**(赋值)与**检查**(调用)是两个可分离的动作"。
|
||||
# ⇒ 把声明本身做成**动作**:`agentmail_require <命令…>` —— 想声明就**必然**检查,
|
||||
# "声明了但没检查"这个状态**从表示上不存在**。
|
||||
agentmail_env_check_require() {
|
||||
[ -n "${AGENTMAIL_REQUIRE:-}" ] || return 0
|
||||
local _am_missing="" _am_c
|
||||
@ -436,12 +440,22 @@ agentmail_env_check_require() {
|
||||
exit 2
|
||||
fi
|
||||
}
|
||||
|
||||
# ★★ 调用者**用这个**声明自己依赖的命令(而不是裸 `AGENTMAIL_REQUIRE=…`)。
|
||||
# 声明与检查**同一个动作** ⇒ "赋了值但那次检查没跑"不再是可表示的状态。
|
||||
# (旧写法 `AGENTMAIL_REQUIRE="…"` + 稍后调 report 仍然有效 —— report 里还有一次检查,
|
||||
# 留它接"表由**环境**提供"(父进程 export / CI 注入)那条路。)
|
||||
agentmail_require() {
|
||||
AGENTMAIL_REQUIRE="$*"
|
||||
agentmail_env_check_require
|
||||
}
|
||||
agentmail_env_check_require
|
||||
|
||||
# 调用者打一行,让"兜了什么"**可见**(也是"忘了 source"的信号 —— 少了这行就不对)。
|
||||
agentmail_env_report() {
|
||||
# ★ 在这里**再跑一次** ③b(见上 ★★ 相位缺陷):调用者的 `AGENTMAIL_REQUIRE=` 是
|
||||
# source **之后**才赋的,source 期那次看不到它。三个调用者都在赋值后立刻调本函数。
|
||||
# ★ 在这里**再跑一次** ③b(见上 ★★ 相位缺陷):调用者的 `AGENTMAIL_REQUIRE` 若由
|
||||
# **环境**(父进程 export / CI 注入)提供,source 期那次就能看到;若由**调用者赋值**,
|
||||
# 则靠 `agentmail_require` 动作或本行这次。三条路都接住。
|
||||
agentmail_env_check_require
|
||||
if [ -n "$AGENTMAIL_ENV_DEFAULTS" ]; then
|
||||
printf ' 环境兜底:%s\n' "$AGENTMAIL_ENV_DEFAULTS"
|
||||
|
||||
Reference in New Issue
Block a user