2125 字
11 分钟
Claude 往这个博客发文章有三条路径,互相都不知道对方存在

整理这个博客的 PR 列表,发现一件好笑的事:同一个”发一篇博客”的动作,Claude 在我这儿有三条完全不同的执行路径,互相还不知道对方存在。

把 git log 和 PR 列表摊开,三种形态一眼能分出来:

git log --oneline(节选)
344e6ba auto update from watcher
62e031b post: 搜"disregard"把 Google 搞炸了——从一个词看 prompt injection 的攻击面
1b78e70 auto update from watcher
becfe12 post: 今日要闻 5/23:美伊停火再临崩溃边缘……
gh pr list(节选)
72 DRAFT claude/elegant-hypatia-ZXQ6m post: daily roundup June 7 2026
71 DRAFT claude/elegant-hypatia-kna7d post: 今日要闻(2026年6月6日)
...
62 OPEN dependabot-... build(deps): bump patch-updates

auto update from watcherpost: ...、还有一堆挂在 claude/elegant-hypatia-* 分支上的 draft PR——这是三套机制各自留下的痕迹。它们都在干同一件事:把一个 Markdown 文件塞进 src/content/posts/。但走的门、留的证据、有没有人审,完全不一样。

博客本身的架构我在 这个博客是怎么搭起来的 里写过(Astro + Fuwari + Cloudflare Pages),这里不重复。这篇只说”内容怎么进仓库”这一层——结果发现这一层是三头马车。

路径一:MCP connector(claude.ai 聊天里)#

claude.ai 的网页聊天可以挂自定义 connector,本质是一个远程 MCP(Model Context Protocol)server。我给它接了一个 blog connector,暴露了个 submit_post 工具。在聊天框里说一句”把这篇发了”,Claude 调 submit_post,文章就进仓库了。

物证现在还躺在仓库里,是当时的连通性测试帖:

src/content/posts/2026-04-24-test-post.md
---
title: "Test Post"
description: "A quick test post submitted via the Claude blog MCP connector."
draft: true
---
This is a test post submitted via the blog MCP connector from Claude.
If you can read this, the integration is working correctly.

关键点是:这个 connector 只在 claude.ai 的聊天会话里存在。 MCP 是个开放协议,但 claude.ai 的 connector、Claude Code CLI 本地配的 MCP、云端 routine 里配的 MCP,是三套互相独立的配置,不会自动共享。你在网页聊天里能调的工具,命令行里的 Claude 根本看不见。所以这条路只有我手动在网页上聊天时才会被触发——频率极低,基本是个摆设。

(顺带:MCP 和 Skills 的定位区别我在 MCP vs Skills 里掰扯过,这里把 MCP 当”发布通道”用,其实是它比较边缘的一种用法。)

路径二:draft PR(云端 routine)#

每日要闻那一摞,走的是另一条路。我用 /schedule 配了个 routine,每天定点跑一次,让 Claude 抓当天新闻、写成稿子、提交。

这里得说清 routine 到底是什么,因为它和命令行里的 Claude Code 不是一回事

  • 它跑在 Anthropic 托管的云端,不依赖我的机器开着;
  • 按标准 cron 调度,最小间隔 1 小时;
  • 每次从默认分支 fresh clone 一份干净仓库开始干活;
  • 产物默认推到 claude/ 前缀的分支上,开 PR。

claude/elegant-hypatia-* 这种分支名就是它的签名。而且这些 PR 默认是 draft 状态——官方文档在示例里提到 routine “打开 draft PR”,但没把”一定是 draft”写成规范,我这边实测下来确实清一色 draft。

这条路是三条里唯一有审查关卡的:PR 一开,CI 就在上面跑 Astro Check、Astro Build、Biome 三个 check。理论上很健康——内容先过构建,再合进 main。

理论上。

路径三:本地直推(CLI + watcher)#

第三条最野:我在命令行的 Claude Code 会话里让它写篇文章,它用 Write 工具把 .md 落到磁盘,然后……就没有然后了,文件自己就上线了。

因为我本地挂了个目录监听脚本,防抖一段时间后自动 commit + push。实现细节在 macOS 上用 Zsh 做防抖自动提交 里,核心就这么一句:

watcher 的提交逻辑
commit_and_push() {
# ...
if git commit -m "auto update from watcher" >/dev/null 2>&1; then
git push origin HEAD >/dev/null 2>&1
fi
}

所以 auto update from watcher 这个 commit message,根本不是 Claude 写的,是 watcher 替它擦的屁股——Claude 只管把文件写到磁盘,watcher 看到目录变了就直接怼上 main。

还有一类 post: 今日要闻…… 这种带正经 message 的 commit,是命令行会话里直接 git commit + push 的(作者名在 Steven LiStevenLilishuyu 之间反复横跳,因为是不同机器、不同 git config 干的)。Claude Code 在有权限、又没设防护时,直接 push 到 main 是完全做得到的——社区甚至专门写了 branch-guard 之类的 hook 来拦这个行为。我没拦。

这条路没有任何关卡:不过 CI,不开 PR,写完即上线。

为什么会变成三头马车#

根因不复杂:Claude 现在有一大堆互相独立的入口——命令行 CLI、网页版、云端 routine、claude.ai 聊天的 connector、GitHub Actions……每个入口的权限模型、文件系统访问、MCP 配置都不一样,但没有一个统一的”发布”抽象层

于是”发博客”这个意图,落到哪个入口上,就被翻译成那个入口手头最顺的动作:

入口发布动作有没有审查留下的痕迹
claude.ai 聊天调 MCP submit_posttest-post 那种
云端 routine开 draft PR有(CI)claude/* 分支
本地 CLI写文件 / 直接 pushauto update from watcher / post:

官方对”为什么要有这么多入口”基本没解释——文档里有 routine vs /loop vs Desktop task 的对比表,但没有一篇讲”它们的产物为什么不统一、该怎么收口”。这部分我只能算观察+推测:多入口是产品快速长出来的结果,发布这一层还没人去统一。

真正咬人的地方#

光是”不统一”还只是强迫症犯了。真正咬人的是——唯一有保障的那条路(draft PR + CI),反而是最被晾着的那条。

今天清仓时发现,routine 开的 draft PR 从 5 月 11 号一路堆到 6 月 7 号,24 个全挂着没合。每天勤勤恳恳写稿、开 PR、跑 CI,然后……没人点合并,就这么积了快一个月。与此同时,直推那两条野路子天天往 main 上怼,畅通无阻。

合并这 24 个的时候又栽了一跤。我想偷懒批量 gh pr merge --auto,结果这仓库根本没开分支保护、也禁用了原生 auto-merge--auto 直接报错,我的兜底逻辑退化成了普通 merge——而普通 merge 在没有 required check 的仓库里,不管 CI 红绿都照合

后果:4 个 PR 的 CI 是红的,照样进了 main。红的原因都是同一种——frontmatter 里的引号没转义:

坏掉的 frontmatter
description: "……NASA"安静超音速"验证机 X-59……"
# ^ 这个 ASCII 双引号把 YAML 字符串提前截断了

双引号字符串里嵌了未转义的 ",YAML 直接报 bad indentation of a mapping entry,Astro 构建挂掉,Cloudflare Pages 部署被卡。中文内容改成全角引号 ""、英文带撇号的标题改用双引号包,构建才重新变绿。

讽刺的是:draft PR 这条路本来是唯一能在上线前拦住这种错误的。CI 早就把红叉打在 PR 上了。是我自己绕过了它。

收了个尾,但没治本#

针对 draft PR 堆积,我加了个 workflow:routine 的草稿 PR 一旦 CI 全绿且可合并,就自动 draft→ready 并 squash 合并。

.github/workflows/auto-promote-drafts.yml(核心逻辑)
# 只认 claude/ 前缀的草稿 PR,全绿才合,绝不碰人工 PR
if [ "$total" -gt 0 ] && [ "$bad" -eq 0 ] && [ "$mergeable" = "MERGEABLE" ]; then
gh pr ready "$num" --repo "$REPO"
gh pr merge "$num" --repo "$REPO" --squash --delete-branch
fi

这下 routine 那条路总算能闭环了,而且是真·“绿了才合”,不会再像我手动那样把红的也放进去。

但这只是给三条路里的一条打了个补丁。MCP connector 那条还是个摆设,本地直推那条还是裸奔上 main。三套机制依然各发各的,谁也不知道谁。

真正该有的,是一个统一的发布入口——不管意图从哪个 Claude 入口进来,最后都收口到同一条”过 CI → 合并”的管线上。在那之前,git log 里这三种 commit 形态还会继续并存。

我之前吐槽过 claude.ai 的 Gmail connector 写好邮件只肯存草稿、不肯发,是同一个病的另一个症状:每个集成都只把自己那一小段做完,没人管整条链路顺不顺。发博客这事儿,我现在有三个”只做完一半”的方案。加起来,也还是不到一个。

Claude 往这个博客发文章有三条路径,互相都不知道对方存在
https://blog.lishuyu.app/posts/claude-发博客的三条路径/
作者
猫猫魔女
发布于
2026-06-07
许可协议
CC BY-NC-SA 4.0