背景
「矩阵媒体自动发布」流水线里挂着两条每日定时:一条 10:00 写掘金日更文章,一条每小时检测 Codex 额度。这两条都托管在 WorkBuddy 的 automation store 里------也就是 automation_update --mode create 创建出来的那批任务。
2026-09-03 早上 10:35,我打开会话例行核查掘金日更:
matrixmedia-agent/juejin/目录下没有今天的文章pushData/2026-09-03.json不存在ledger.json里没有2026-09-03|掘金条目
以往这种情况的经验是先看 MatrixMedia 主进程日志,排查 publish-article 是否报错。但这次根因藏在更上游。
失误点
直接拉一下 automation 列表:
bash
automation_update --mode list
返回 automations: []------空数组。
不是今天的任务运行失败,是整个 automation store 在某次会话迁移后被清空了。联想到上一次 WorkBuddy 升级(2026-08-30 从 1.x 跳到当前版本),迁移流程没有对 automation store 做 export/import 处理。它依赖主进程某条内存路径;一旦迁移、重启,列表就成空白,且不报错、不通知、不备份。
这条隐式规则在运维手册里只字未提。
三个教训
1. 「已设了自动化」是一个不可观测的状态
上一轮会话里 automation_update --mode create 返回 id,并不代表明天那条还在。它会丢在:
- 主进程 OOM 被杀后
- 系统升级 / 重启之后
- 会话窗口关闭再开
- 跨会话迁移之后
任何一种都会让它丢失,且不报错,不告警。
2. 多源验证还要加上「自动化存在性」这条源
过去我依赖的是「按时间窗扫 pushData + 主进程日志,看这次的产物有没有」。每条都在。但它只能验证「这次跑了 」,验证不出「昨天那条还在」。
新的做法是把 automation 的可见性纳入健康检查------与现有的 pushData / 日志 / CLI 退出码多用验证套同一个框架:
python
EXPECTED_AUTOMATIONS = [
{"name": "掘金 10:00 日更", "kind": "recurring",
"rule": "FREQ=DAILY;BYHOUR=10;BYMINUTE=0"},
{"name": "Codex 额度 2h 检查", "kind": "recurring",
"rule": "FREQ=HOURLY;INTERVAL=2"},
]
async def heartbeat_automations():
listed = await automation_update(mode="list")
names = {a["name"] for a in listed["automations"]}
missing = [e for e in EXPECTED_AUTOMATIONS
if e["name"] not in names]
if missing:
raise RuntimeError(f"automations lost: {[m['name'] for m in missing]}")
EXPECTED_AUTOMATIONS 不写在 automation store 里------它独立写在版本管理仓库的代码或清单上。每次心跳查 missing 项就走告警,而不是靠「这个告警自动化本身还在」(那等于自己告警自己)。
3. 心跳要走在动作之前
心跳不仅能告警,还要能「自动重建」:
python
async def rebuild_one(spec):
new = await automation_update(
mode="create",
name=spec["name"],
prompt=spec["prompt_text"], # 不在 store 里,版本管理里查
schedule_type=spec["kind"],
rrule=spec["rule"],
status="ACTIVE",
)
listed = await automation_update(mode="list")
if spec["name"] not in {a["name"] for a in listed["automations"]}:
raise RuntimeError(f"rebuild {spec['name']} did not take effect")
return new["id"]
「震一震会重新起来」是会话/机器迁移后的最后一道防线。重建过程也得走与 mmcli.cmd 同样的谨慎原则:进程生命周期脱耦,输出重定向到文件再轮询,关心实际产物(本次的 pushData + 日志)而不是退出码。这条经验在矩阵媒体发布流水线里已经反复验证过(见运维手册 §4 「两个必踩的坑」)。
当前焦点的补动
发现丢失后立刻手动补写了今天这篇错开选题的文章(同会话后半段),但这只是治标。下一步要做的事:
- 公挂
EXPECTED_AUTOMATIONS:写在仓库代码里,不写进 automation store,避免「期望列表也跟着丢」 - 加一条独立心跳 :21:00 起,与番茄审核检查同一时间另起一条,心跳结果不走 automation store,而是落
pushData/heartbeat-YYYY-MM-DD.json,便于按日期检索 - missing 后先告警、再重建、最后以 pushData / ledger / automation.list 三源可验作收尾,确保重建真的生效
小结
不假设、不隐含、不「上次跑过」。 一个完整的自动化流水线不仅要在「看起来在跑」时真在跑,还要在「看起来不在」时有路径换回来。手动补一篇重要,但心跳机制更重要;前者是救火,后者是防止下次再烧。
补一篇不算完,把期望清单、心跳、重建三件套立起来,才算把今天这一课学完。