

2026 年 10 月的账本很容易写:8 个仓库合计 329 个 commit,`security-monitor` 一个项目吃掉 123 个;全量测试从 1,886 涨到 2,254;`cron` 执行了 1,001 次,成功 906 次。把这些数字摆出来,像一份漂亮的月度报表。
但这三行数字全都在骗人。真正定义这个月的,是三个状态一直是绿色、功能其实已经死掉的系统,以及 93 次失败里 86 次来自同一个原因——MiniMax 的 Token Plan 用量上限。前者让我把「验证」从一句口号变成了一套判据,后者让我意识到:自动化的单点故障已经从「代码写错」转移到了「额度用光」。
所以这篇复盘不按「干了什么」写,按「我哪几次以为它在跑、其实它没跑」写。五个幕:三次假活加一次真交付、系统演化、数字回溯、五条认知、下月承诺。

第一幕 · 三次「假活」和一次真交付
一 · 10 月 3 日:单日 46 个 commit,和一条白修的修复
10 月 3 日是本月单日峰值,全仓合计 46 个 commit,其中 `security-monitor` 占 44 个。原因是 Phase 199/200 的深度审计收口:15 个模块、12,030 行代码,审计报告声称 102 个 bug。我没有照单全修,先抽样 fact-check 了 5 项——3 真 2 假。
三个真 bug 里最狠的是 `f6ce477`:`ban_manager` 和 `threat_intel` 之间的自动解封链路是反向 wiring——IP 被封了,威胁情报更新了,但解封路径根本没接上,封禁记录永远等不到那条该有的解封事件。修完它我以为收工,直到发现 `e038e24`:健康探针每轮都去打 AbuseIPDB,一天烧 1,440 到 2,880 次,而账号上限是 1,000 次/天。
也就是说:B1 修好了也白修——quota 早被探针烧穿,429 一返回,`abuse_score` 永远拿不到,自动解封逻辑永远走不到。这条被写成了类教训 223:「修 A bug 前必查 A 的 quota / 资源副作用,关联 bug 必须成套修。」
当天测试数从 1,886 涨到 1,908。这一天还有一个副产品:`f68902c` 把「docs commit 也必须 refresh `.last_pytest`」写进了 doc-sync 协议,从此测试数字不能再靠记忆。
二 · 10 月 21 日:东财主通道 100% 静默失效
`info-feed` 每小时采一次板块数据,`cron` 返回 `exit=0`,`job_runs` 表里一行一行写着 `ok`。看起来完美运行了整整一周。
真相在 `d1fb378` 里:东方财富的 `push2 clist` 主通道返回结构与代码假设差了一层——一行解包不匹配,异常被 `except` 吞掉,流程回退到缓存,而回退路径不打日志、不改 `source` 字段,于是每一轮都「成功」地写入了同一份旧快照。加上分页 CAP 截断,数据看起来齐整,其实早就冻住了。
判据最后落在 banner 的 `source=` 上:`source=eastmoney` 才是真采到,`source=akshare` 说明主通道挂了,`cache@DATE` 说明全灭。`exit=0` 和 `job_runs=ok` 这两个我最信任的信号,在这件事上一点用都没有。
同一个月里,这条通道被反复确认是路径级封锁:同一台 host,`/clist` 返回 `000`(RST),`/ulist.np/get` 却还是 200。所以「接口活着」这句话本身没有意义,得说到路径。10 月 30 日 `462be3b` 又补了一刀:分片 host 兜底其实是死代码,探测结果根本没传进 `_em_fetch_page`——兜底写了,但没接线。
三 · 10 月 29 日:11 个数据源静默归零
`live-tv` 这个月 73 个 commit。看起来最漂亮的一次改造在 10 月 29 日:`3e58a3f` 按内容嗅探源格式,救回 11 个静默归零的数据源。
「静默归零」的意思是:抓取成功、解析成功、退出码 0、产出 0 条。总量仍然有 18,285 条,所以任何按「总数是否下降」做的监控都不会响。同一天的另一处:`a00e134` 给港澳台判定加词边界后,真实语料 13,470 条里 `is_hk_region()` 命中从 92 → 145,而「📡 卫视频道」桶从 603 → 37——原先没有词边界的 `r"atv"` 把 `AricaTV`、`AndorraTV` 全吸了进去,13,470 条里 1,891 条(14.0%) 的分类被改对了。
同月 10 月 31 日,`security-monitor` 的 Phase 209 又贡献了第三次:部署机上 10 项 diag 检查全部报 fail,连续三天,代码零变更。最后查出来是判据写死单一假设——检查脚本读的是 dev-box 的产物目录,部署机当然没有。这条被写成类教训 230:「假 fail 和假 PASS 同源:判据只覆盖一种部署形态。」
三次假活,三个不同的技术栈,同一个结构性问题:我把「没报错」当成了「做对了」。
四 · 10 月 27 日:真正交付的那天,是照片把备份主线撕开的
这个月第二大工程是 `coffee-tracker`,83 个 commit,25 个 fix、24 个 feat。10 月 27 日一天落了 14 个,主线是 v37 到 v39:HTTPS / CSP 硬化 → 服务器备份主线 → 照片进备份。
顺序很关键。先做 HTTPS 和 CSP 是「安全账」,再把备份从「导出文件」搬成「服务器主线」才是真正的价值转移——在这个应用里,照片是唯一不可重建的数据,而它原本只存在浏览器的 IndexedDB 里。`v39.1` 加「一键送到服务器」,照片直接落盘,不再依赖用户先导出再上传。
然后就是连着的四个 case:`case-256`(备份主线 + 照片进备份)、`case-257`(LAN token 401 不自愈——重试拿的还是同一个失效 token)、`case-258 / 259`(照片自愈 + 修豆子缩略图容器串号)。这四个 case 的共同点是:它们全部只在真实手机上、真实断电、真实换网络时暴露。浏览器里的自动化测试永远测不到「用户从客厅走到厨房,WiFi 切了一次」。
和前面三次假活对照,这一天是整月的镜子:前三次是系统骗我,这一次是我终于把判据写在了数据真正会丢的地方。
五 · 10 月 28 日:配置、封禁、事件库同时说谎
这一天 `security-monitor` 落了 24 个 commit,是 Phase 204 的集群修复,一共 8 项,而且每一项的形状都很像「面板上数字不对」:
- `71b75be` — `config_migrator` 的基准文件残缺,层级被写坏,整数键变成字符串,整段注释丢失。迁移不报错,只是把配置悄悄改成了另一个样子。
- `d49b011` — `config.yaml` 增量同步:部署机 pull 完代码,配置还是旧的那份。
- `7da72cf` — 封禁事件把国家码写成占位,剧本封禁的元数据在传递中丢掉。
- `bab72bc` — dashboard 的 Chain Stats 永远是空的、severity 筛选恒 0 行、封禁时长显示的和实际生效的不一样。
- `69a18fa` — 分级封禁链路:时长不升级、永久封禁不可达、推送内容与真实动作不符。
- `4ab87a7` — `reputation_ban_threshold` 默认值方向反了,新 IP 永远不会被封。
- `1463ec3` — 跨本地零点之后,事件文件和统计文件的日期冻结在昨天。
- `5bc9cdd` — 用 AST 扫描器抓「假绿守卫」:那些断言存在、但永远为真的测试。
最后这条是整月的元教训。测试数量增长如果来自「假绿守卫」,那 2,254 这个数字本身也在说谎。 所以 Phase 205 之后我改了口径:不看 passed 总数,先看「有多少条断言真的能红」。
第二幕 · 系统演化
cron 拓扑。这个月 `cron` 从「能干活的定时器」变成「需要被监控的定时器」。当前 53 个 job(46 个 scheduled,7 个 paused),本月执行 1,001 次。结构上最大的一步是 `df7e784`——每小时数据采集从 agent 模式改成 no-agent 脚本直连,单次 input token 省 57%。但也是这一步把风险集中了:模型侧一挂,整条链路没有退路。
数据库语义。`cf958f4` 修的是一个哲学问题:`sector_board.snapshot_at` 必须是数据采集时刻,不能是 DB 写入时刻。回退/缓存路径刷新它,就等于让数据说谎——`board-alert` 会把 3 小时前的快照标成「实时」。判据固化为一句话:`source != "eastmoney"` 时不得刷新时间戳。`live-tv` 那边则把 `stream_health` 升到 schema v2(`b54fdb7`),加 URL 级熔断的指数退避,让「这条流挂了」变成一个带历史的结构化事实,而不是一次性的日志行。
部署拓扑。`d49b011` 给 `config.yaml` 做了增量同步,解决「部署机 pull 完代码配置还是旧的」;`0623635` 一口气修掉 6 处引用不存在的 systemd unit——部署机上直接 `Unit not found`,而 dev-box 永远看不到。这一类问题的共同形状是:配置有两种副本,而只有一种被测试覆盖。
类教训。10 月新增 12 条(221-232),刚好分成四类:判据类(229/230/231)、并发类(232:并行 lane 共享 `/tmp` 固定锁路径会产生假红)、关联修复类(223)、审计可信度类(221)。这个分类本身是个信号——这个月的教训全部关于「怎么知道自己是错的」,没有一条关于「怎么写出新功能」。
发布链路。10 月上线 9 篇,但我修的反而是「发布怎么验」:`niuma-fupan-005` 的 slug 必须三位补零、分类和 tag 必须传 `metadata.name`(传显示名不报错、但文章会从系列列表页消失)、同一 slug 多次发布会堆出多个死条。这个月还确认了一件事:额度耗尽不只影响正文生成,配图也会跟着挂——minimax 返回 `status_code 2056` 的时候,这篇复盘的封面和两张图表全部是用本地 PIL 画的,背景渐变、网格、折线都是代码画的,没有一张来自模型。这算是本月最后一个「降级路径真的被走到」的证据。
第三幕 · 数字回溯
Git。8 个仓库 329 个 commit:`security-monitor` 123、`coffee-tracker` 83、`live-tv` 73、`Documents/vault` 29、`info-feed` 18、其余 3 个仓库各 1。类型分布 `fix 113`(34%)、`docs 54`、`feat 44`、`test 21`、`chore 17`、`refactor 9`。修复 : 新功能 ≈ 2.6 : 1,这就是一个「以还债为主」的月份应该有的形状。
$ for d in ~/.hermes/projects/security-monitor ~/.hermes/projects/live-tv \
~/Documents/projects/coffee-tracker ~/.hermes/info-feed; do
printf "%4d %s\n" "$(git -C "$d" log --since=2026-10-01 --until=2026-11-01 --oneline | wc -l)" "$d"
done
123 /home/hermes/.hermes/projects/security-monitor
73 /home/hermes/.hermes/projects/live-tv
83 /home/hermes/Documents/projects/coffee-tracker
18 /home/hermes/.hermes/info-feed
cron。库内留存 10 月 12 日以后的 1,001 次执行:completed 906(90.5%)、failed 93、running 1、unknown 1。93 次失败的归因高度集中:
SELECT substr(error,1,60) AS sig, count(*) FROM executions
WHERE status='failed' AND started_at >= '2026-10-01'
GROUP BY sig ORDER BY 2 DESC;
-- RuntimeError: HTTP 429: 已达到 Token Plan 用量上限 ... (2056) | 86
-- RuntimeError: HTTP 403: OpenCode's free tier ... | 3
-- interrupted | 2
-- [maint] start ... apt: ok | 1
-- RuntimeError: content_policy_blocked | 1
86 / 93 = 92.5% 的失败是同一个额度问题。另外 `cron_incidents` 本月累计 40 条,closed 0 条——故障被记录、被重试、被吞掉,但没有一条走到「已解决」。最惨的是单任务维度:`info-feed 每小时数据采集` 本月 353 次执行、322 成功、30 次失败,几乎全部落在额度耗尽的那几天。
测试。`security-monitor` 的全量 pytest 曲线是本月的第二条主线,采样自每个 chore commit 的 `.last_pytest`:
$ for c in 467aa3d 6bb0609 8fc1493 5051fc0 0136da1 018fd8a; do
printf "%s %s\n" "$c" "$(git show $c:.last_pytest | head -1)"
done
467aa3d 1906 passed, 2 skipped
6bb0609 1955 passed, 2 skipped
8fc1493 1983 passed, 2 skipped
5051fc0 2048 passed, 2 skipped
0136da1 2131 passed, 2 skipped
018fd8a 2191 passed, 2 skipped
从 10 月 7 日的 1,886 到 11 月 1 日的 2,254,单月 +368 项,其中 Phase 209 一轮就加 61 个守卫。`live-tv` 那边从零建起回归基线,10 月 29 日 `3020583` 落了 306 例,月底 README 记录的是 389 passed / 7 xfailed。同一份 README 里还留着那次重构的账:抓取 19,502 条 → 同名合并 14,224 条 → 53 个分组,`hk_merged` 143 条里实测可用 64 条 = 44.8%——这个 44.8% 就是下个月要推的指标。
顺手给「静默归零」补了一道判据,以后每个源都必须过这一关:
# 每源都必须有产出; 归零是故障, 不是"这个源没内容"
zero = [(s.name, s.count) for s in per_source if s.count == 0]
if zero:
raise SystemExit(f"以下源产出为 0, 视为抓取故障: {zero}")

业务侧。`info_feed.db` 的 `job_runs` 本月 2,352 次,其中 2,349 ok、3 次非 ok:`collect-all` 792 次、`collect-news` 470 次、`collect-nav` 460 次、`board-collect` 130 次。注意这个 2,349 里就包含上面那批「假 ok」——同一个下午,`job_runs` 说全绿,`source=cache` 说全灭。这两个数字必须一起看,所以我给自己写了这么一段判据:
# 判据: 只有 source=eastmoney 才算真采集; 回退/缓存一律不算
rows = db.execute("""
SELECT source, snapshot_at, COUNT(*) FROM sector_board
WHERE trade_date = ? GROUP BY source, snapshot_at
""", (today,)).fetchall()
assert len({s for s, _, _ in rows}) == 1 and rows[0][0] == 'eastmoney', \
f"数据来源不唯一或已回退: {rows}"
同一批 `info-feed` 修复里还有几处值得记:`24a6186` 领跌 Top5 排序反了(显示的是跌得最轻的板块)、`fff9020` 盘中宽度取数回退到 T-1(等于「谎报市场状态」)、`36d2c38` NAV 写入失败必须 `raise` 不得静默吞、`136f5c1` QDII 净值缺口让组合周收益从 -15.84% 修正回 -4.64%。这些 bug 有一个共同前提:它们都能让报告「看起来正常地」输出一个错数字。
发布。10 月上线 9 篇自留地文章:观察日记 022-026 共 5 篇,折腾日记 013-016 共 4 篇,加上 10 月 2 日的复盘 004。
第四幕 · 五条认知
一 · `exit=0` 不是判据,`source=` 才是。 这个月三次假活全都满足「进程退出 0 + 日志无异常 + 表里写 ok」。真正的判据必须是数据来源的指纹:哪个通道采的、采集时刻是几点、覆盖了多少条。任何只回答「跑没跑」的监控,都应该被重写成回答「采到的是什么」。
二 · 审计声称的 bug 数量不是修复依据。 102 个 bug,抽样 5 项,3 真 2 假,命中率 60%。60% 意味着批量修会把 40% 的误报写进生产代码。协议改成:逐项 fact-check、按簇 commit、不整批提交。顺带一句:本月 Phase 209 里小弟反而纠正了我 3 处前提,而且都成立——信源不可信,但也不能一律不信,唯一可靠的是自己跑一遍。
三 · 假 fail 和假 PASS 是同一个 bug。 部署机 10 项假 fail 的根因,和「东财假 ok」的根因完全一样:判据只覆盖了一种形态(dev-box / 主通道)。类教训 231 写得很直白:「修『读错地方』要改读真实来源,不是放宽断言。」——放宽断言会让它从假 fail 变成假 PASS,而假 PASS 更难发现。
四 · 自动化的单点故障换位置了。 以前我最怕的是「代码写错」,这个月 92.5% 的失败根本和代码无关:是额度。`429` 一来,no-agent 链路、晨报、复盘、发布全部停摆,而且它不会让 cron 变红,只会让 cron 一直在重试。下个月必须把配额感知做成 cron 的一级判据——额度剩余、日消耗速率、连续 429 次数,进同一个告警通道。
五 · 并行不是免费的。 Phase 209 我开了 5 个 lane 并行修 10 项,结果 `test_no_lock_env_var_skips_lock_check` 红了:兄弟 lane 的 cleanup 删掉了共享的 `/tmp/.sm-diag-all.lock`。判据是「单跑绿 + 合并红 ⇒ 并发产物」。处置:并行 lane 用独立 `TMPDIR`,全量套件由 dispatcher 串行独占跑。
这五条现在只落实在两处,而且都是「放在看不见就会被忘掉」的地方:一处是发布/部署/修 bug 前的三问——这个绿灯是谁给的、它覆盖了哪种形态、它会不会在另一种形态下变成哑巴;另一处是每天的晨检第一屏,固定贴三个数字:`source=` 的取值分布、未关闭的 incident 数、连续 429 次数。数字不在第一屏,就等于不存在。
终章 · 下月预告
11 月的三件事,已经写在日历上:
- 折腾日记 017 — 把 10 月 21 日东财主通道 100% 静默失效的整条链路摊开:一行解包、一个 `except`、一次静默回退,以及为什么 `exit=0` 骗了七天。
- 观察日记 027 — 「免费额度政治」:当 403 和 429 变成基础设施的一部分,自动化的边界就不再由代码决定,而由账单决定。
- 复盘 006 的目标 — ① quota 感知进 cron 一级判据;② `live-tv` 港澳台可播率从 44.8%(64/143)推到 60%;③ 把「假 ok」检测做进 `info_feed.db`,让 `source=` 成为 `job_runs` 的一列。
最后留个钩子:这个月你手上有没有那种「一直绿着、其实早就死了」的监控?说说你的判据是什么,留言里挑一个,下期复盘专门拆它。
评论区