三国杀武将觉醒马超怎么样 三国杀武将觉醒手游安卓手机版马超角色详解
2026-07-29 3433719
2026-07-29 0
最近关于 SDD(Spec-Driven Development)的讨论很多。OpenSpec、Kiro 等工具采用的核心思路,都是先准备一份 spec,再由 AI 根据 spec 完成实现。
我亲自实践了几个项目,却发现每一次最终都会卡在同一个问题上:
问题并不属于某个工具的 bug,而是来自流程本身:spec 记录的是“准备做什么”,code 体现的是“实际做了什么”。只要 code 持续迭代,spec 就会逐渐落后。AI 根据过期 spec 生成代码甚至比完全不读更危险,因为它会把错误的“事实”作为决策依据。
因此,我尝试打开一条相反的路径:GDD(Geno-Driven Development),对应的开源仓库是 OpenGeno。本文将拆解其设计,比较它与 SDD 的差异,并说明实际应用后才理清的一些取舍。
可以把 SDD 的工作循环抽象为:
1. 人写 spec2. AI 读 spec → 写代码3. (代码合入)4. 下次任务 → AI 再读 spec → 写代码
第 3 步和第 4 步之间,没有任何机制保证 spec 还和代码一致。维护 spec 这件事被默默推给了"AI 应该顺手做"或"reviewer 应该提醒"。这两个都是软约束,软约束扛不住时间。
更棘手的是,spec 通常是一整块文档。一份 200 行的 spec 即使只调整 5 行,diff 看起来变化不大,语义却可能已经完全偏离;而 AI 阅读时并不知道它已经失真。
GDD 建立在一个简单的反向假设上:
具体实现只包含三件事:
feat-tree/├── index.md# L1:项目根索引,列模块├── auth/│ ├── index.md# L2:模块索引,列 feature│ ├── sign-in.md# L3:单个 feature 的详情│ └── sign-out.md└── tasks/├── index.md└── list-view.md
AI 改 sign-in 的时候不需要看 tasks 模块的 50 个 feature。它从 L1 看到目标在 auth/,进 auth/index.md 看到 sign-in.md,再读那一篇。改 50 个 feature 的项目和改 5 个 feature 的项目,单次任务读的 token 量几乎一样。
L3 的 frontmatter 长这样:
type: og-featurekind: uifeature: sign-inmodule: authschema: 1code:- lib/features/auth/sign_in_page.dart- lib/features/auth/sign_in_controller.dart- lib/api/auth_service.dartlast_synced_commit: a1b2c3dlast_reviewed: 2026-05-06
两个关键字段:
code: 记录这个 feature 具体依赖哪些代码文件;last_synced_commit: 这份文档上次被人/AI 真正对照代码核对过的 git SHA。SHA 表示的并非“编辑发生的时间”,而是“完成验证的时间”。这项区别非常重要:只修改文档不能算对账,必须阅读代码并确认两者一致后才可以 bump。
有了 code: 和 last_synced_commit:以后,漂移检测只需要一段简单脚本:
# 伪码for doc in feat-tree/**/*.md:last_sha = doc.frontmatter.last_synced_commitfor code_path in doc.frontmatter.code:if git_log(code_path, since=last_sha) is not empty:mark doc as DRIFT
这个脚本被注册成 Claude Code 的 Stop hook——每次会话结束自动跑。两种模式:
warn(默认):输出漂移摘要,session 仍可正常结束;block:返回退出码 1,在漂移得到处理前不允许结束 session。由此,软约束被提升为硬约束。AI 遗漏文档更新时,不能再留到“下次再说”,而必须“现在就解决”。
| 维度 | SDD(OpenSpec / Kiro 等) | GDD(OpenGeno) |
|---|---|---|
| 起点 | spec 先于 code | code 已经存在,由文档跟随变化 |
| 文档形态 | 单份 spec / 大块 markdown | L1 / L2 / L3 三层树 |
| AI 加载方式 | 一次读取完整 spec | 沿 L1→L2→L3 按需加载 |
| 维护机制 | 依靠人/AI 主动同步 | last_synced_commit + Stop hook 强制对账 |
| 适用阶段 | 更偏向新项目 | 适用于任意阶段,包括遗留代码 |
| 失败模式 | spec 腐烂、AI 读错 | 漂移能够被检测,最差结果也只是收到一次提醒 |
| 介入复杂度 | 编写 spec 属于前置任务 | 执行一次 init,随后规则注入 CLAUDE.md 并自行传递 |
两者并不是相互替代的关系。SDD 处理的是“如何让 AI 从零到一写对”,GDD 关注的是“如何让 AI 从一到正无穷持续写对”。新项目可以先通过 SDD 形成第一版,再交给 GDD 长期维护。
整个系统分成三个阶段,分别查看会更加清晰。
┌─────────────────────────────────────────────────┐│User: /geno-init │└────────────────────┬────────────────────────────┘ │ ▼┌────────────────────────┐│ ① 选语言(中文 / 英文)│└────────────┬───────────┘ ▼ ┌──────────────────────────────┐ │ ② 选漂移模式(warn / block) │ └──────────────┬───────────────┘▼ ┌──────────────────────────────┐ │ ③ 选生成模式(stub / full)│ └──────────────┬───────────────┘▼┌────────────────────────┐│ ④ 扫描代码 → 提议模块│└────────────┬───────────┘ ▼ ┌──────────────────────────┐ │ ⑤ 写 L1 / L2 / L3 文档 │ │ 写 .feat-tree.json │ └────────────┬─────────────┘▼ ┌──────────────────────────────┐ │ ⑥ 把工作流契约注入 CLAUDE.md │ │(此后规则自动传递)│ └──────────────────────────────┘
User: 改一下 sign-in 的逻辑 │ ▼[AI 读 CLAUDE.md] ── 已注入的规则告诉它怎么做 │ ▼[L1 index.md] ──► 看到 auth 模块 │ ▼[L2 auth/index.md] ──► 看到 sign-in feature │ ▼[L3 auth/sign-in.md] ──► 读详情 │ ▼[AI 改代码] │ ▼[AI 同步更新 L3 + bump last_synced_commit] │ ▼[Stop hook 自动跑 drift-check] │ ├─► 无漂移 ──► session 正常结束 └─► 有漂移 ──► warn 提醒 / block 拒绝结束
Stop hook 报漂移 │ ▼User: /geno-sync │ ▼列出五类问题: ├─ 红:明确漂移(code 改了,doc 没跟) ├─ 黄:可疑(提交里有 "refactor" 字样等) ├─ 灰:从未对账过(stub / 待审 full 草稿) ├─ 坏链:code 路径已经不存在 └─ 陈旧 SHA:记录的 SHA 已不在 git 历史里 │ ▼用户选择从哪类开始 │ ▼逐篇:读 diff → 改文档 → bump SHA │ ▼最终报告:哪些已对齐、哪些跳过
整个系统就两个 skill(/geno-init 和 /geno-sync)+ 两个 hook(PostToolUse 提醒、Stop 检漂移)。没有第三个。每多一个命令都是用户要记的事。
/geno-init 进行到 Step 3 时,系统会询问一个问题:生成模式选择 stub 还是 full?
stub 模式(默认)只创建文档骨架,section body 均为 TODO / 待补充。日常修改涉及哪个 feature,再补写对应内容。
type: og-featurekind: uifeature: sign-inlast_synced_commit: ""last_reviewed: 2026-05-06# Sign in## WireframeTODO## Entry pointsTODO## InteractionsTODO
适合:大项目、想增量推进、不希望初始化阶段花太多 token。
full 模式该模式会进一步扫描,AI 尝试一次写完全部 L3。不过其中存在一项关键设计:last_synced_commit: 保持为空。
这表示内容虽然已经生成,却尚未经过任何人验证。
last_synced_commit: ""# 哪怕全文都填了,SHA 也必须是空的
原因在于,SHA 的含义是“已经验证”,而 full 模式只能说明“已经生成”,这两种状态必须明确区分。/geno-sync 看到 gen_mode: "full" + 空 SHA 时,系统会将它识别为“待审稿”而不是“待写”,并给出不同的处理建议。
仓库中的 examples/todo-app-full/ 提供了完整的 full 模式产物 demo,其中 AI 的句式值得注意:
这种 hedged tone 是对 AI 的刻意要求:无法确认的信息就不要伪装成已经确认。AI 不确定时,不应猜测一个看似合理的值,而要直接留下 待补充 ,等待人工补充。
.feat-tree.json:位于项目根的运行时配置init 完成后,会在项目根目录写入一个:
{"version": 1,"tree_path": "feat-tree","drift_mode": "warn","gen_mode": "stub"}
其中四个字段分别承担不同用途:
tree_path:指定树所在的目录,默认 feat-tree/,也可以修改;drift_mode:warn 还是 block,由 Stop hook 负责读取;gen_mode:stub 还是 full,交由 /geno-sync 读取,用于判定空 SHA 表示“待写”还是“待审”;version:记录schema 版本,供以后升级使用。这正是 GDD 真正能够 work 的关键:规则不是放进 README 等用户记住,而是在 init 阶段注入项目的 CLAUDE.md。
/geno-init 最后一步会将一段规则文字追加至 CLAUDE.md,若文件不存在则新建,并使用 / 包裹起来。这段内容会告诉以后每个 session 中的 AI:
Claude Code(包括其他读 CLAUDE.md / AGENTS.md 的工具)每次启动时都会读取该文件。因此规则只需注入一次便会持续生效,用户不用在每个 session 中重复说明。
快速安装
npx skills add web-abin/OpenGeno
手动安装
# 1. 把 skill 装到 Claude Codegit clone [email protected]:web-abin/OpenGeno.gitcp -r OpenGeno/skills/geno-init ~/.claude/skills/cp -r OpenGeno/skills/geno-sync ~/.claude/skills/# 2. 在你的项目下运行cd your-project/geno-init
/geno-init 会跟你交互三个问题(语言 / 漂移模式 / 生成模式),扫描代码、提议模块、确认后生成树。整个过程不会改你的代码,只会创建 feat-tree/、写 .feat-tree.json、追加 CLAUDE.md。
运行结束后,日常使用时无需继续执行命令,AI 会依照 CLAUDE.md 中保存的规则自动完成整个流程。
我承认它仍存在几个明确限制:
完整的设计动机和相关取舍记录在仓库的 docs/motivation.md中;关于为什么只有两个 skill、为何注入 CLAUDE.md 以及为何采用三层结构等具体决策,则写在 docs/decisions/。
把“先写 spec”调整为“先读代码,再让文档跟随代码变化”,虽然反直觉,却会越用越顺手。这有些像从“写注释”转向“写测试”:前者依赖个人自觉,后者则有机器兜底。
如果你也长期受到 spec 腐烂困扰,可以尝试 OpenGeno。
下一篇准备详细讨论“为什么选择 SHA 而非 mtime”,以及“为什么使用三层,而不是两层或四层”。这两个决策当时考虑了很长时间。