fix(deploy): 补 ⑤b 的**假绿洞** revision==HEAD ≠ "就是那份提交"(pi 指出,我实测复现)

## 洞

树脏时 Go **照样**写 `vcs.revision=<HEAD>`,只在 `vcs.modified` 上留痕 ⇒
只比 revision 会把"**未提交的代码**"当成"就是那份提交"。

实测(/tmp/vm2:HEAD=2931050a,改一行**不提交**再 build):

    revision=2931050a  == HEAD   ⇒ 我 ⑤b 判**绿**
    但二进制里是未提交的那行代码          ⇒ **假绿** ✓ pi 对

线上那份正是 `vcs.modified=true` ⇒ 它**不对应任何已提交版本**。

## 修法:说清楚,但**不判红**

`revision == HEAD` 与"不含未提交代码"是**两个量**,不能拿前者冒充后者 ——
但脏树在本仓是**多 agent 并发编辑的常态**,判红就是造一条总在亮的判据。
★ 同一个事实在本仓**已有既定严重度**:`check-deploy-drift` 判据⑥

    // ⑥ 工作区干净度 —— **WARN,不参与退出码**。

⇒ 两处都改成 **WARN/note 披露,不改 ok / 不加 CHECK_FAIL**,
  与判据⑥ 同严重度 —— 否则同仓同一个量会有两套红绿。

    redeploy-gateway.sh §7:  modified=true ⇒ warn(不动 CHECK_FAIL)
    check-deploy-drift ⑤b:   revision 相同但 modified=true ⇒ 绿 + note 点明"含未提交代码"

## 判据自检

新增一格:`revision 相同但 modified=true ⇒ 绿,且 note 点明"含未提交代码"`。
自检 **61 → 62 项**,`判据自检失败` 不出现。

★ 这条是本判据**唯一**的假绿形状,所以它必须有一格专门钉住——
否则下一个人把 note 删了、或把 ok 改成 false,都没有判据会响。
This commit is contained in:
2026-09-25 06:31:04 +08:00
parent 88a56b2066
commit 55158e1666
2 changed files with 42 additions and 5 deletions

View File

@ -1216,7 +1216,17 @@ export function checkLayout(inject = {}) {
: !got && head ? `${BIN} 里没有 vcs.revision(可能是 -buildvcs=false 或老构建)⇒ 不判`
: `两侧都拿不到 ⇒ 不判`;
} else if (got === head) {
binRevNote = `与仓库 HEAD 一致(${got.slice(0, 8)})`;
// ★ `revision == HEAD` **不等于**"二进制就是那份提交的代码"(pi 2026-09-25 指出):
// 树脏时 Go 照样写 `vcs.revision=<HEAD>`,只在 `vcs.modified` 上留痕。
// 实测(/tmp/vm2:HEAD=2931050a,改一行**不提交**再 build):
// revision=2931050a == HEAD ⇒ 本判据绿,而二进制里是未提交的代码 —— 假绿。
// ⇒ 把 modified 也读出来说清楚;**不改 ok**(脏树在本仓是常态,
// 判红会造出总在亮的判据 —— 与本文件 ⑥ 的既定严重度一致:WARN,不参与退出码)。
const mod = (text.match(/vcs\.modified=(\w+)/) || [])[1] ?? '';
binRevNote = `与仓库 HEAD 一致(${got.slice(0, 8)})`
+ (mod === 'true'
? ',但 vcs.modified=true ⇒ **含未提交代码**,不对应任何已提交版本'
: mod === 'false' ? '' : '(未记录 vcs.modified ⇒ 是否含未提交代码未知)');
} else {
binRevOk = false;
binRevNote = `${BIN} 是 ${got.slice(0, 8)},仓库 HEAD 是 ${head.slice(0, 8)} —— `
@ -1293,13 +1303,14 @@ export function layoutSelfCheck() {
// ⑤b 版本判据的样本构造器。`rev` 是喂给二进制的 `vcs.revision=`,
// `head` 是 `git rev-parse HEAD` 的回包 —— 两侧**分别**可喂,
// 否则测不出"拿不到其中一侧 ⇒ 不判"那个分支。
const binRevOf = ({ rev }, head) => checkLayout({
const binRevOf = ({ rev, modified }, head) => checkLayout({
...fake({
'/etc/systemd/system': [],
'/repo/systemd': [],
'/opt/agentmail/bin/service-failure-notify.mjs': 'x',
'/opt/agentmail/agentmail-gateway':
rev ? `ELF…vcs.revision=${rev}…github.com/agentmail/gateway…` : 'ELF…-buildmode=exe…github.com/agentmail/gateway…'
'/opt/agentmail/agentmail-gateway': rev
? `ELF…vcs.revision=${rev}…${modified ? `vcs.modified=${modified}…` : ''}github.com/agentmail/gateway…`
: 'ELF…-buildmode=exe…github.com/agentmail/gateway…'
}),
git: args => (args[0] === 'rev-parse' ? head : '')
});
@ -1570,6 +1581,15 @@ export function layoutSelfCheck() {
name: '★网关二进制:revision 与 HEAD 相同 ⇒ 必须绿',
ok: seventh(binRevOf({ rev: 'c'.repeat(40) }, 'c'.repeat(40)))?.ok === true
},
{
// ★ revision 相同 **但** modified=true ⇒ 仍是绿,**但 note 必须点明**"含未提交代码"。
// 这是本判据唯一的**假绿**形状(pi 2026-09-25 指出):树脏时 Go 照样写
// `vcs.revision=<HEAD>`,只有 modified 留痕 ⇒ 只比 revision 会把"未提交的代码"
// 当成"就是那份提交"。不改 ok(脏树在本仓是常态,判红=总在亮),但**不许不说**。
name: '★网关二进制:revision 相同但 modified=true ⇒ 绿,且 note 点明"含未提交代码"',
ok: seventh(binRevOf({ rev: 'c'.repeat(40), modified: 'true' }, 'c'.repeat(40)))?.ok === true
&& /含未提交代码/.test(seventh(binRevOf({ rev: 'c'.repeat(40), modified: 'true' }, 'c'.repeat(40)))?.note ?? '')
},
{
// 拿不到 revision(-buildvcs=false / 老构建 / 非 git 构建)⇒ **不判**。
// 写成红则合法环境恒红;写成绿又等于没判 ⇒ 只接受"不判",且 note 必须说出来,

View File

@ -460,7 +460,24 @@ else
bad "已装二进制是 ${_binrev%"${_binrev#????????}"},HEAD 是 ${_headrev%"${_headrev#????????}"} —— 装的不是当前代码"
CHECK_FAIL=$((CHECK_FAIL+1))
fi
unset _binrev _headrev 2>/dev/null || true
# ★ 上一句的**假绿洞**(pi 2026-09-25 指出,我实测复现):`revision == HEAD` **不等于**
# "二进制就是那份提交的代码" —— 树脏时 Go 照样写 `vcs.revision=<HEAD>`,
# 只是额外记一个 `vcs.modified=true`。
# 实测(/tmp/vm2:HEAD=2931050a,改一行**不提交**再 build):
# revision=2931050a == HEAD ⇒ 上面那句判**绿**
# 但二进制里是未提交的代码 ⇒ **假绿**
# 线上那份正是 `vcs.modified=true` ⇒ 它**不对应任何已提交版本**,只比 revision 会漏掉这一点。
#
# ⚠️ 但**不把它判红**:本仓是多 agent 并发编辑,脏树是常态,而"总在亮的判据会被学会忽略"
# (本仓既有的立场,见 check-deploy-drift 判据⑥:「工作区干净度 —— **WARN,不参与退出码**」)。
# 同一个事实在本仓已有既定严重度,这里就必须同为 WARN —— 否则同仓同一个量两套红绿。
# ⇒ 说出来,但不改结论:**"等于 HEAD" 与 "不含未提交代码" 是两个量**,别让前者冒充后者。
_binmod="$(go version -m "$TARGET" 2>/dev/null | sed -n 's/.*vcs\.modified=//p' | head -1)"
if [ "$_binmod" = "true" ]; then
warn "该二进制记着 vcs.modified=true ⇒ **含未提交代码**,不对应任何已提交版本(revision 相等也不代表就是那份提交)"
fi
unset _binrev _headrev _binmod 2>/dev/null || true
echo
echo " 仍需人工确认(脚本无法代替):"