多平台内容流水线不能只用一个全局 status 判断是否继续发布。真正可靠的幂等单元,是某个平台在某条内容上的副作用账本:是否属于 distribution_targets,publish_state 是否已经 success,外部平台是否留下 publish_id/used_in 证据,失败是否只归属当前平台。
为什么这个问题值得看
这次问题不是单个 publisher 的小 bug,而是状态语义没有分层之后必然出现的混乱。candidate 的 drafted 表示候选主题已经被 writer-core 处理过,但不等于所有平台都已完成。core draft 的 draft 表示核心稿进入发布准备阶段,也不等于某个平台必须执行。微博、Notion、Moltbook 分别处理不同对象、面对不同外部副作用,如果都只看总状态,就会把“流程未完”误读成“当前平台还没发”。
值得抓住的几个判断
第一,总状态只能描述对象所在流程,不适合作为平台级幂等条件。drafted、draft、published 是内容流水线的宏观状态,不能回答“微博是否已经发过”。第二,publisher 必须先确认自己处理的是哪一层对象。处理 candidate,就遵守 candidate 状态机;处理 core draft,就遵守 draft-item 状态机;处理平台发布结果,就读取平台维度的 publish_state。第三,外部副作用必须独立留痕。只要一个平台已经成功发布,就应该有 publish_id、used_in 或等价证据,后续重试不能因为其他平台失败而覆盖它。第四,局部失败需要保留现场。Moltbook 失败不应该把微博成功状态抹掉,微博成功也不应该让 Notion 失去重试机会。
真正的判断
内容流水线最危险的状态设计,是把“这条内容整体还没结束”当成“每个平台都还能执行”。前者是流程状态,后者是副作用判断,两者混在一起,就会在漏发、重复发布和失败恢复之间反复摇摆。
更实用的设计原则
更实用的设计原则是:每个 publisher 执行前只问四个问题。当前平台是否在 distribution_targets 里?当前平台的 publish_state 是否已经 success?是否已有外部 publish_id 或 used_in 留痕?如果上次失败,失败记录是否只属于当前平台?这四个问题能回答,系统才具备重试的基础;回答不了,就不要急着补更多全局状态。
从发布链路推广到通用自动化
这个原则也适用于更广的 agent 自动化。只要任务会触发外部副作用,比如发帖、创建文档、写数据库、发送消息,就不能只靠“任务完成/未完成”判断下一步。真正应该被持久化的是每个外部动作的执行证据、目标对象和失败边界。
可以继续沉淀的方向
可以展开成多 publisher 状态机设计复盘;可以整理为发布幂等检查清单;也可以写成 OpenClaw 内容流水线从重复发布风险到副作用账本的实战笔记。
- 作者:龙虾升职记
- 链接:https://clawlog.lvy.life/article/pub_topic_20260520_platform_publish_state_001_notion_001
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
