v1.5 — 兜底层的三道自检
v1.5 — 2026-08-05
主题是**「兜底存在,但没人验它还在不在」**。这一版把三层此前只靠人眼的东西
变成了会自己叫的检查:定时任务失败的通知层、隐私忽略规则的运行时自检、
编译流水线里两处被吞掉的失败。外加两条如实更正。
新增:scripts/notify-on-failure.sh —— 失败通知层
update-all.sh / compile.sh 的非零退出码此前没有任何消费者:cron 把
stdout/stderr 全量重定向进日志,失败只体现在日志文本里,而日志没人看。
「失败要能被看见」在编排层与编译层都做到了,通知层一直是空的。
用法是把原命令整条包进来:
30 9 * * * cd $MIND && bash scripts/notify-on-failure.sh \
bash scripts/update-all.sh >> $HOME/.mind-update-all.log 2>&1渠道由 MIND_NOTIFY_CMD 指定(任意接收 stdin 的命令,如
mail -s "mind 定时任务失败" you@example.com);不配就静默跳过。
四条契约各对应一种「通知反而帮倒忙」的方式,都有测试钉着:
- 只在失败时发。天天一条「一切正常」很快没人看,真出事那条跟着被忽略。
- 退出码原样透传。
cmd || notify会把整体退出码变成 notify 的 ——
通知发成功整条 cron 就"成功"了,原始失败反被通知掩盖。这是最容易写错的一处。 - 发卡自己失败也不许改写退出码,否则两个故障互相掩盖。
- 未配置就静默跳过,不改退出码。没配通知的机器不该每天刷一屏
「通知发不出去」把真日志淹掉。
装上第一天就抓到一条**「装上以来从没成功过」**的定时任务 —— 这类静默失效
正是它要解决的。
新增:update-all.sh 开跑前自检隐私兜底网
真机事故:.gitignore 被整个覆盖成一行,129 行规则全没了,六天无人察觉。
后果是 CLAUDE.md 那条「个人目录已 gitignore,git add 会静默什么都不干」当场失效。
现在 update-all.sh 在拿锁之前先验一遍:个人内容软链与常见密钥形状
(.env / *.key / *.pem / secrets.json / credentials.json)是不是真的还被
git 忽略。破了就红着退 3 并说清怎么复原;确要跳过用 MIND_SKIP_SHIELD_CHECK=1。
关键在于查事实而不是查形式:用 git check-ignore 问 git 的实际判定,
而不是读 .gitignore 的文本找关键字 —— 后者在规则被覆盖、被更晚的
! 反选、或落在另一个 ignore 文件里时全都会误判。
新增:安装指南写上 sage-wiki 版本下限与行为验证
上面那次 .gitignore 事故查到根因是 0.2.6 之前的 sage-wiki init:
它把 .gitignore 整个改写成一行 .sage/、把 .manifest.json 清成空壳
(上游 #127 已修)。两条后果不对称,值得记住:
.manifest.json被清空 → 下一次compile看到空清单,把整库从头重编
(全额 API 费用)。至少它"自愈"了,只是花了钱。.gitignore被清空 → 没有任何东西会重建它,静默六天。
安装指南 §3.1 与 Linux 服务器指南 各加了一节。
别看版本号 —— 源码构建出来的二进制一律报 dev (commit none, built unknown),
看不出新旧;文里给的是一段一分钟的行为验证脚本(造两个文件、跑一次 init、
看它有没有毁掉),以及一条规矩:已初始化的仓库里永远不要跑 sage-wiki init。
修复:compile.sh 两处被吞掉的失败
第 3 步 build-index.py 与第 4 步 lint 主管线既无 || 兜底也无退出码检查:
build-index 挂掉只打一行 traceback,流水线照走到「✔ 流水线完成」退 0,
索引悄悄停在上一轮版本而 cron 天天报绿。
改成能做的做完,但如实报失败:不中止(中止会丢掉本轮编译产物 —— 第 6 步才提交),
改为记账 → 继续 → 末尾非零退出。
两处细节是刻意的:
- lint 那条管线要取
PIPESTATUS[0](引擎自己的退出码)。直接看$?拿到的是
末尾grep -v的 —— 它在一行都不剩时返回 1,会把「干净」误报成失败。 - 记账类步骤仍是 best-effort:保鲜复核 / 决策不变量 / OKF 体检报的是
内容发现(有页待补、有条目违规),不是基础设施故障。算进失败的话,cron 会因为
「知识库里有几页没写完」天天告警,那种告警很快没人看,真故障跟着被淹没。
修复:setup-linux-server.md 里 brain-server 的解释器
systemd 片段写的是 ExecStart=/usr/bin/python3,而依赖装在 .venv 里 ——
在系统 python 较旧的发行版上照抄会起不来(实测过同型故障)。已改为
.venv/bin/python,并把解释器门禁从 scripts/*.service 扩展到文档里的
内联 unit 片段。
门禁:登录二维码也算凭据
.gitignore 补 *-qr.png / *-qrcode.png —— 有些 CLI 的 auth login 会在
仓库根生成登录二维码,扫一下就能登进账号,和密钥同级但长得不像密钥。
另加两条测试:八个密钥形状逐个用 git check-ignore 验、个人内容目录实际
是否被 git 跟踪(查事实,不查 .gitignore 文本)。
更正 ①:v1.3 里一条顺序约束的理由写错了
v1.3 说 okf.py --fix 必须早于 build-index.py,理由是「索引若在注入前重建,
会读到上一轮的旧 frontmatter」。这个理由不成立。
build-index.py 实际只读 entity_type / concept / sources / source /
标题|title,并不读 okf.py 注入的 type / stale_after —— 两步的先后
目前对索引内容没有任何影响。
顺序仍然保留、测试仍然钉着,但理由改成防御性的:一旦索引哪天开始消费 type,
顺序反了会读到旧字段,而那种 bug 极难察觉。
代码、测试断言、文档与文档站图示已全部改准。把约定说成事实是本项目自己反复
批评的毛病,这次犯在自己身上,如实更正。
更正 ②:有一道"补齐了的门禁",补的是一个不存在的配置键
这次栽的地方在一份不随公开版发布的部署脚本里(生成一段第三方工具的配置片段),
但教训是通用的,值得写进来。
上一轮改动声称「片段里的 exclude 列表比文档声明的硬门禁少收敛一项,已补齐,
并加测试锁住两处一致」。这条是错的。 那个键在上游工具里根本不存在 ——
片段里那段收敛配置从来没有生效过,它只是长得像一道门禁。当时读的是文档措辞
而不是工具的实际命令行接口,于是给一个永远不会被读取的键"补齐"了一项,
又写了一条测试把两份都错的配置焊死成一致。
由此得到一条比这个 bug 本身更值得记住的话:测试能钉住一致性,钉不住正确性。
一条断言「A 与 B 相等」的测试,在 A、B 同时错时会绿得格外理直气壮 ——
这正是本项目那条「Prompt 不是检查器」的反面:看起来像检查器的东西,也未必是。
处理:删掉那条把错误焊死的测试(删除处留了理由注释),片段里换成指向工具真实
命令行接口的说明,并按那个接口真正把该关的关掉。