八个模块请求,七个返回 200,只有 scripts/dom.js 一个 404。这是 Earl Grey 在决定让第一个网站 threadkeeper.io 死掉、又临时转身把它抓下来的时候,亲眼看到的事实。更准确地讲,那台 Spaceship 服务器还剩四天就到期,世界上仅存的一份副本还在上面跑,而他此前从未把任何一行代码存进版本控制。所以他要抢救,只能从浏览器抓页面,顺手给每个存档文件挂上 SHA-256。一个连 git init 都没跑过的人,最后靠哈希值来证明自己从线上扒下来的文件没被改动。这个行为本身,就很像是给自己过去十二个月做的一次尸检。
页面在浏览器左边渲染完好。右边控制台只有一条红字:加载 scripts/dom.js 返回 404。就一条。
翻译成人话是:这个应用从头到尾执行过零行 JavaScript,没绑上任何一个事件监听器,表单、复制、导出、主题切换、拖放,全都没跑起来。它有一个落地页、一个博客、三篇文章、一个给 AWS 黑客松做的 Ariadne Clew 应用,视觉上什么都不缺。但它从没活过。一个网站顶着“Don't commit without context”的青色标语,本人从未提交过那句标语所在的代码。
这还不是最荒诞的部分。最荒诞的是作者根据 2025 年 9 月那篇链接到应用的博客文章倒推,这个 bug 至少已经存在了十一个月,而且因为没有任何部署时间戳,实际可能更久。他上线了一个死物,整整一年不知道。
为什么会死得这么安静,分三层看。
第一层是技术机制。ES module 在执行任何代码之前要先解析完整 import 图,一个文件 404,整条链不执行,规范里没有降级这个选项。main_js.js 开头有六个 import,第三条要从 './dom.js' 拿 getElement、getValue 这些东西,可服务器上实际存在的文件名是 dom_js.js。就一个字符之差,把剩下五个好好的 import 全部判了死刑。更讽刺的是,dom_js.js 这个名字本身就带着拙劣的复制粘贴痕迹——后缀叠后缀,像从聊天窗口里拖出来直接落地的。你几乎能看见那个场景:一个人让模型生成代码,然后把每个文件一一粘进浏览器,粘完最后一个,自己也没再打开确认过。
第二层是这套技术栈没有任何环节能捕获这个问题。没有构建步骤,没有打包工具,没有带导入解析的 linter,没有 TypeScript,没有测试。托管在 CDN 上的原生 JavaScript,一写完就上线。这个选择放在 2026 年看依然合理——小项目不想为基础设施花时间,这我完全理解。但账要算清楚:它把“验证”这个动作从流程里删掉了,默认交付即完成。于是缺失的 dom.js 没有被任何东西拦截,直接滚到了线上。
第三层是用户没来。一个按钮全死的应用产生不了任何用户行为,没有点击记录,没有报错回传,没有一个真实用户会来告诉你“你这东西不工作”。反馈闭环是断的。没人来骂你,不代表你做对了,只说明可能根本没人用。
三层合起来,把一个本应直冲脸面的 bug 变成了透明的。页面渲染正常这件事,恰好就是那个最危险的假阳性:文档加载完整,静态资源全在,视觉上一切如常,于是没人会再往前多走一步。一个真正崩溃的模块会白屏、会抛一堆错误,容易发现。一个只是没绑上事件的应用,谁打开都不会觉得不对劲,除非你伸手点一下。
这事的本质不是“作者太新手”。新手犯错、漏配路径,谁都有过。真正的问题是:在他选择的这套工作流里,没有任何一个下游动作能区分“已交付”和“能工作”。我这句话不是在骂他没上 CI。CI 自己也没那么神——多少团队的 CI 只跑 lint 和单测,从不在一个真实环境里把页面加载起来、把关键交互点一遍。系统架构师 Mustafa ERBAY 在评论里说了一句更准的:真正的问题不是缺失的 dom.js,而是缺失的验证层。这个判断我同意。只是我想把边界再压窄一点:验证不是测试覆盖率,也不只是加个冒烟测试,它是“每个输出都要被一个能让它当场露馅的下游接住”这件事。
作者后来在回复里承认,重建存档时跑的冒烟测试其实只要二十行左右,就能立刻把原始版本和修复版本区分开。二十行,能把一个潜伏了十一个月的谎言当场戳穿。但这个冒烟测试的珍贵不体现在代码量,而体现在它的位置:它在部署之后、交付之前,主动去把一个“看起来能跑”的页面跑一遍,让那个 404 亮出来。这个动作在整个 AI 辅助开发的生产线上,恰恰是最稀缺的。
道理在这里开始往上拔。AI 这两年把“生成”这一端的成本压到了极低,代码、文章、方案、PPT、会议纪要,全都出得又快又顺。但模型交付的东西从来没有被真实环境执行过,不管是代码还是文字。它只对 token 的输出质量负责,不对“这段 token 在你的上下文里到底能不能运行”负责。于是生成越便宜,验证就越贵;生成越像样,验证就越容易被跳过。你拿到一段漂亮的代码,但它可能在第一次被真实点击时才会暴露那个 404。你拿到一份通顺的报告,但它可能有一条关键逻辑根本没接上,只是文字层面读起来成立,就像页面渲染正常但应用从未启动。
这个隐患在文字交付物里比代码更阴。代码至少还有浏览器控制台,一个红色 404 就在那里,看见了就看见了。文字没有控制台。一个 AI 写的决策建议、一份自动生成的客户洞察,没有人能一眼看出它内部那条 import 是断的。它只会显得更专业、更完整、更无可挑剔。于是“渲染完好”在文字场景下成了比代码场景更昂贵的假阳性,因为这里连那个 red console 都不会出现。
到这儿我得把自己前面的话往回拉一下。我写到“生成更便宜,验证更贵”,这个说法有点太顺了。准确一点讲:生成的成本会继续跌,验证的成本也会跌,但验证成本的降低速度,会长期慢于生成端。原因在于验证需要一个真实的下游环境——一个代码要跑起来的浏览器、一个决策要被执行的真实上下文、一个结果要被追责的具体场景。这些环境的供给本身就稀缺,因为它们不叫 GPU,不叫模型,不叫工具,它们叫“有人在乎这东西到底能不能用”。只要在乎这个的人没有随生成力一起通胀,验证就永远是瓶颈。
利益格局摆开来更清楚。第一个接刀的是写作者本人,他被自己项目的假阳性骗了整整一年,最后用存档和哈希去做临终关怀。第二个可能接刀的是用户,如果这个应用真有人访问,那些人点了一通按钮却没有一个响应,他们不会知道是缺了 dom.js,只会觉得这产品烂,然后走人,什么都不说。第三方是模型提供者,位置微妙:它们不为 import 图解析失败负责,也没法负责,它们的交付边界到输出为止,但正是这个边界把风险整体转移到了用户侧。谁来为“已交付但未工作”买单?只能是那个按下发布按钮、却没有设置任何验证门槛的人。
这条通则可以直接拿去套别的案例。任何一个人跟你说“我用 AI 上线了一个某某”,你第一要问的不是他用了什么模型,而是他有没有一个环节能证明那东西真的跑过一遍。对代码,这个环节可能是一个冒烟测试;对内容,这个环节可能是一个让第三方复核的事实检查;对产品,这个环节可能是一次真实用户的任务完成测试。如果没有,那他的“上线了”和“从未运行过”之间,只有一条每个人都不愿去查的 404。
我的判断摆在这儿:AI 辅助开发接下来真正拉开差距的,不是谁的模型写得更快,也不是谁用的提示词更巧妙,而是谁能在“生成”和“上线”之间插进一个便宜到愿意为小项目都跑一遍的验证层。二十行的冒烟测试,一次简单的交互演练,一个能让模块图真被加载的环境,这些才是真正卡住产量和可靠性之间那个缺口的零件。现在行业里几乎所有人都在卷 token 价格和模型大小,但没有人认真给“一个只能渲染、无法运行的应用”这类东西标价。等那些便宜到可以忽略的验证手段普及到默认工作流里,这条 404 才会彻底变成过去时。
这个判断可能会错。证伪条件很具体:如果哪一天主流的开发环境、部署平台或模型自身,把“输出必须先在一个真实沙箱里执行并通过一次最小可用性检查”变成默认前置动作,模型在交付前就得自己面对那条 import 图,跑不通就直接打回,那验证的稀缺性就被抹平,我上面这套判断作废,到那天我会改口。但在那之前,那些死在服务器到期前也没有人发现的假阳性应用,会一版一版地上线,一个接一个地过期。
作者在把网站抓下来、挂出存档的那个页面里,给了每个文件一个 SHA-256。这个动作有它残忍的一面:他从来没给原始代码建过仓库,却给尸体的每个部分都做了防篡改标记。他后来还在做一件叫 Clew Chronicles 的事,十个假设被冻结,其中一条就是“我对什么重要的记忆很可能是错的”。账算到这一步,结论自己出来了。
