开发者 Edwin Vermeer 用 Claude Code 做了一款立体四子棋,上架后几乎没人下载。他索性公开了全部 prompt 和一份逐条编号的决策日志,里面记着测试悄悄挂住、检查读错文件这类坑。
Edwin Vermeer 用 Claude Code 做了一款手机游戏,订的是每月 €15 的 Pro 计划,前后 8 个工作日,摊在 4 周里。游戏叫 Quadrim,在 4×4×4 的立方体里下四子棋,64 个格子、76 条能连成线的路,最强的电脑对手按 1980 年的一份数学证明走,先手必胜。
上了 App Store 之后几乎没人下载。他干脆把仓库公开当教程,10 月 13 日在 DEV 社区写了篇复盘,标题就说:决策日志才是这个项目最值钱的产出。日志一共 396 条,每条带编号。
我把仓库克隆下来翻了一遍。他发给 Claude Code 的 32 段 prompt 按顺序收在 workflow.md 里;决策记录分在三个文件,光前两个加起来就 600 多 KB,比很多项目的代码还长。翻代码时我数了一下,注释里引用这些编号的地方有 390 处左右。
Quadrim 项目数字卡片和一个 4×4×4 立方体
✿ ✿ ✿
大半 prompt 都要求记决策
他的流程分六步,写在另一个仓库 CodingAgentOrchestration 里:
- 01
澄清:把粗糙的想法交给 agent,让它复述目标、列出假设和未知项,一直问到没有歧义,这一步不写代码 - 02
对每个开放问题想几种方案 - 03
写带验收标准的 spec - 04
切成小工单,每张都能单独合并 - 05
写代码之前,让架构师、安全工程师、产品经理三个专家 agent 审 spec 和工单 - 06
一张工单写完跑一次 code review,提交,再做下一张
他在文章里说,每段 prompt 都带着一句:Add all decisions to the decisions file。我在 workflow.md 里对了一下,32 段里有 18 段提到要把决策写进日志,措辞不完全一样,意思都是这个。
这是我最想抄的一步。它不用你搭什么记忆系统,一句话而已:agent 每做一个不那么显然的选择,就往一个文件里追加一条,下次开会话它自己会读到上次踩过的坑。
实现阶段也不顺。workflow.md 里记着,Claude Code 中途停下来 5 次,他靠一段“继续按工单实现”的 prompt 让它接着跑,每张工单做完跑一遍 code-review-expert skill,审完直接提交到 main。
✿ ✿ ✿
一条决策长什么样
每条记录三部分:问题、决定、代价。他在文章里举了 D295:
问题:图片只指定了高度的话,在图片加载完之前,程序以为它的尺寸是 0。决定:所有图片外面套一个尺寸事先写死的框(Flutter 里叫 SizedBox)。代价:这张工单因此出了三个长得一模一样的 bug,New Game 按钮被排成了 316×0,压成一条线,点不到;logo 成了一条零宽的空隙。
写成这样,规则连同理由只说一次。之后每次开会话,agent 读到的是一条现成的事实,不用再踩一遍才想起来。
让这个日志好用的是两条约定:
- 只增不改
。某个决定被推翻时,旧条目加一行 superseded by,新条目写在旁边,走错的路一直留着 - 编号不重来
。第一版 D01 到 D217,第二版 D218 到 D391,现在从 D392 往后。代码注释直接引用编号,比如测试里写着 // D369,任何一行看着奇怪的代码都能顺着编号找到原因
决策条目的三段格式,以及代码注释通过编号指回日志
编号这条我会直接用。很多人给 agent 写 CLAUDE.md,写着写着就变成一份越来越长的规则清单,没人知道哪条是为什么加的。编号加代码注释,等于给每条规则配了一个出处,看到一行怪代码,拿编号一搜就知道当初为什么这么写。
✿ ✿ ✿
测试挂住比测试失败难发现
他讲的第一条教训来自 D178。
有一次会话里,完整的检查脚本前后启动了 10 次,只有 1 次写出了退出那一行,而那一次在跑到引擎测试之前就失败了。连着 5 张工单,没人注意到检查其实从来没跑完过。
原因是一个测试要求电脑从一个还剩 58 个空格的局面开始“精确求解”,也就是把后面所有可能的走法穷举到底。这种算法不设步数上限,也不会中途放弃,等于让程序把整个游戏解出来。它不报错,也永远算不完。
找到它之前,日志里记了 3 个错误诊断。其中一个是怀疑某个函数逐节点分配内存,日志原话评价它“看着合理,没测过,错的”。
失败的测试会自己喊出来,挂住的测试不会,它看上去就和一个跑得慢的测试一样,你每次的直觉都是“再等等”。5 张工单没人发现,就是这么来的。
后来的修法有两条:
-
每条长命令都加硬超时,仓库的 CLAUDE.md 里写的是 timeout 2400 bash tool/check.sh -
搜索代码里加断言,精确求解最多只接受 16 个空格
自己跑 agent 的人可以直接拿走第一条。Claude Code、Codex 这类工具跑测试时如果没有超时,agent 和你都会把“还在跑”当成一种正常状态。
✿ ✿ ✿
agent 的解释再顺也要跑一遍
游戏有 31 个电脑对手,强弱要排成一架梯子。这件事让他吃了不少苦头。
他说 AI agent 特别擅长给出一段听上去很有道理的解释,告诉你为什么对手 A 应该比 B 强。他花了一阵子才学会全都不信,原因是实测结果一次次跟解释对不上:
- 偏好会转圈
。有一级对手是按“喜欢先占哪些格子”来拉开强度的。实际对局里,角优先赢了中心优先,中心优先赢了只占中心,只占中心又赢了角优先,石头剪刀布。偏好只是风格,排不成梯子 - 省时间的测法会把结论翻过来
。为了快,校准时只给电脑十分之一的思考量。最强那一档里,Quartz 在这种条件下比更高一级的 Titan 强 83 分,给满思考量后反而弱 115 分;另一个对手 Flint 从强 95 分变成弱 241 分。它们的本事集中在每局少数几个很费算力的局面上,省掉的恰好是这部分 - 一个坏参照会带偏一片
。对手的评分是一环扣一环比出来的:拿邻居的分数,加上两者实测的差距。一个邻居量错,附近一串都跟着歪。他把多个估计取平均改成取中间值,一个离谱的数就带不偏整体(D341)
最后定下一条规则:任何“谁比谁强”的说法,都必须来自对战工具跑出来的结果,不能来自对代码的论证。整个项目里单次最大的提升也是这么测出来的:让强制搜索除了看自己的威胁,也去看对手的威胁,涨了大约 300 Elo(D169)。
左边是三种开局偏好的循环箭头,右边是两种预算下的分差柱
✿ ✿ ✿
检查全绿的包里带着联网权限
这是我读得最久的一条,D356。
他对玩家的承诺是:和电脑下棋不联网。为了守住这条,检查脚本 check.sh 里有一条规则:Android 的权限清单(manifest)里不许出现联网权限 INTERNET。
后来加了应用内购买。按他的说法,Google 计费库往下三层的依赖里,有一个遥测组件 transport-backend-cct 自带了联网权限。第一个 release 包 app-release.aab 里就带着它,另外还多了网络状态等三项。
检查照样是绿的。它查的是他自己写的那份权限清单,而实际打进包里的,是构建时把所有依赖的清单合并后的另一份。有点像只核对自己写的购物单,没看收银小票。
日志还记下了一个中间的错误结论。第一次排查时,他只看了自己想到的两个依赖,没发现联网权限,就判定检查不用改。其实构建工具会生成一份合并报告,逐行写着每个权限是谁带进来的,一次 grep 就能回答。日志里的说法是:读产物,别读输入。
左边是 check.sh 检查的源 manifest,右边是合并后带 INTERNET 的 manifest
这个坑跟 AI 没什么关系,人写的检查也常这样。它跟 vibe coding 有关系的地方在于:agent 很愿意帮你写检查,也很愿意告诉你检查通过了。检查到底盯的是不是最后交到用户手里的那个东西,得有人问一句。
紧接着的 D357 把权限从合并结果里去掉,检查也改成读合并后的清单。后来这条检查又改过一次形状。加了排行榜之后,Play Games Services 必须联网,权限只能按整个应用申请,“完全不联网”守不住了。现在的 check.sh 改成:联网权限必须在自家清单里明确写出来,而且不能带移除指令。脚本注释里写:出问题的是权限悄悄混进来,权限存在本身现在是一个决定。
✿ ✿ ✿
哪些规则写成 grep,哪些故意不写
只写在文档里的规则,碰上某次会话没读到那份文档,就会被打破。所以重要的规则他都变成了 check.sh 里的检查:
他的原话大意是:agent 打破检查的方式和人一样,忙的时候不小心;区别是检查第二天还在。
也有他故意没写成检查的。美术素材是一个手动跑的 Python 脚本转出来的,产物提交进仓库,但 check.sh 不比对字节。D290 解释了原因:同一张图换个 Pillow 版本重新编码,字节就不一样,这种检查会在库升级时报错,而其实什么都没坏。日志里的原话是,会喊狼来了的门禁,人会学着跳过。
他换了个办法:写一个测试,检查玩家真会碰到的问题,比如当时 20 个对手的头像在安装包里都能找到,素材总大小不超过 3 MB。规则写多了、误报多了,人就会找办法绕过检查,他宁可少写一条。
规则分成三类:写成 grep、写成测试、只留在文档
✿ ✿ ✿
把日志折回需求文档
决策攒到 391 条的时候,他让 agent 把所有决策折回最初的想法文档,把一个个踩坑故事改写成通用规则,得到 idea-v4.md。这份文件约 184 KB。
按他的说法,如果有人想从头重做这个游戏,就该拿这份。问题和答案一起到手,不用等几个月后自己踩出来。他给读者的建议也是:别从代码开始,从 idea-v4.md 开始跑一遍流程,然后对照日志,看你的 agent 做出的决定和他的有什么不同。
文章最后列了哪些事只能他自己来:
-
范围和品味:游戏做成什么样,收费怎么设计 -
说“去测一下”:上面几条教训,大多始于他不信一个听着合理的解释 -
仓库外面的事:商店账号、签名密钥、App Store 审核、Play Console -
美术:设计用 Claude Design 出,再让 Microsoft Copilot 给四种完全不同的视觉方向,挑一种后由 Copilot 写每张图的 prompt,最后用 ChatGPT 出图
✿ ✿ ✿
想抄的话,最少做这四件事
看完这个仓库,我给自己的项目记了四件事,都不需要额外装工具:
- 01
在仓库里建一个 decisions.md,格式照抄 D295 那样的三段,编号从 D01 开始,永不重排 - 02
每段让 agent 干活的 prompt 末尾加一句,让它把这次做的决定追加进这个文件;推翻旧决定时只加 superseded by,不删原文 - 03
选三五条最不能破的规则写进一个 check 脚本,检查读的是构建产物,比如合并后的 manifest、打出来的包,不读源文件 - 04
所有长命令加超时,让挂住变成失败
夹板上的四项勾选清单和一个沙漏
这套做法的成本是文件会很长,600 多 KB,人读不完。好在你不用从头读,agent 每次开会话自己读;你只在某行代码看不懂时,拿着编号去查。Android 版还在等 Google 的 12 人封闭测试,仓库里的 idea-v4-decisions.md 已经记到了 D396。