八年前Meta就有万亿参数模型了——但一个AI agent连7000字符的Markdown都填不好。
我让AI把一篇7000字符的文章从dev.to同步到Hashnode。点击提交,返回草稿链接,一切正常。点进去一看:446个字符。第一段完整,最后一段完整,中间41段全是空的。94%的内容被静默吞掉,没有报错,没有警告,系统显示"成功"。
![]()
这是Safari MCP——我维护的macOS原生浏览器自动化工具——在Hashnode编辑器上踩的坑。如果你在做任何涉及现代富文本编辑器的浏览器自动化,这个postmortem值得看完。
![]()
先交代背景。Safari MCP要填的编辑器包括Quill、ProseMirror、Lexical、React控制的Featured.com编辑器,以及十几种变体。这次跨平台发布的流程很简单:打开Hashnode新建草稿页,填入7000字符Markdown正文,点击发布。这套流程在dev.to、Medium、X、LinkedIn(2026年Quill迁移后)、Featured.com都跑了多年。Hashnode自诩"开发者友好",本该是最简单的一站。
症状很诡异。提交后保存的草稿结构是:第一段(约280字符)→ 41个空
标签 → 最后一段(约166字符)。总共446字符,发送了6808字符。agent没报错,fill调用返回成功,点击提交也返回成功。Hashnode自己的草稿视图把这个破碎结构显示得像是故意设计的。
第一次诊断:粘贴竞态条件。我猜是合成ClipboardEvent('paste')和React useEffect周期赛跑。之前在X.com修过类似bug,解法是在粘贴前执行execCommand('delete')。照搬过来,没变化。加了一个safari_verify_state调用在fill和submit之间,验证器确认编辑器的.textContent和发送内容一致——但100毫秒后点击提交时,编辑器状态已经回退了。所以中间段落被吃掉发生在fill返回之后,agent的"成功"信号是谎言。
第二次诊断:Markdown自动转换。Hashnode编辑器会对行首特定字符做自动格式化:-变成列表,>变成引用,**变成粗体标记,[变成链接助手,#变成标题。原文有几段以这些字符开头。我推测编辑器在拒绝触发自动格式化提示的段落,留空等用户手动确认。解法:用零宽空格转义行首字符。重跑,结果还是446字符。也不是这个。
第三次诊断:React调和顺序。Hashnode的编辑器用React组件包裹ProseMirror。我怀疑多次beforeinput事件触发了React的批量状态更新,导致部分段落被覆盖。尝试把填充拆成每500字符一块,中间加 RAF(requestAnimationFrame)延迟。结果:第一段和最后一段之间的空段落变少了,但没完全消失,字符数略有波动,不可预测。
![]()
三次全错。真正的问题和粘贴、Markdown转换、React批量更新都没关系。
Root cause:Hashnode编辑器对DOM变动有"静默截断"机制。当单次操作插入的节点数超过阈值(实测约50个段落节点),编辑器会保留首尾节点,丢弃中间部分,且不触发任何错误事件。这不是竞态,不是转换冲突,是硬编码的防御性截断。我的7000字符正文被解析成约43个段落节点,刚好触发这个阈值。
解法:把填充拆成多轮,每轮插入后强制触发编辑器的change事件,让状态同步落盘。具体实现:每10个段落为一组,插入后执行document.execCommand('insertText', false, '')触发noop变更,再 RAF 等待下一帧。三轮操作后,6808字符完整保存。
这个案例的教训:现代富文本编辑器的"成功"反馈不可信。verify_state必须在关键操作后、提交前重复执行;长内容必须分块插入并强制状态同步;阈值行为不会写在文档里,只能黑盒测试测出来。Hashnode的"开发者友好"是营销话术,它的编辑器防御机制和任何商业平台一样不透明。
最后,那篇7000字符的文章?手动复制粘贴,30秒搞定。自动化省下的时间,全花在调试上了。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.