mattpocock/course-video-manager · AI 原生开发实践逆向复盘
这不是一份「用 AI 写代码」的心得,而是一份把软件开发流程改造成机器可执行状态机的工程档案。 15 个月、3534 次提交、483 个 PR,作者把「领域词汇、工程纪律、架构决策、验收标准」全部写成了 Agent 能直接读懂并自我约束的文本, 再用 GitHub label 当状态机驱动一支无人值守的智能体流水线。
RALPH: 前缀,但 2026 年 6 月之后归零。那个「无人值守批处理循环」被更可控的 label 状态机取代了。作者自己淘汰了自己的第一代自动化。Course Video Manager(CVM)是 Matt Pocock(TypeScript 教育者,Total TypeScript 作者)为自己的课程生意写的单人内部工具:管理课程结构、分镜脚本、录制、剪辑、导出、发布到 YouTube / TikTok,一路到交付日历。它不是一个玩具项目——
最后一条是整个实践的分水岭。这不是一个「Agent 搞砸了就 git reset」的项目——写错一行,可能毁掉的是正在录制的素材或生产数据。后面你会看到,作者对「隔离」的偏执程度,直接来自这个前提。
把每个月的提交数(柱)和 AI 参与率(折线)叠在一张图上,能清楚看到这条曲线的形状。注意纵轴已经剔除合并提交,否则 2026-10 的 1354 次提交里有一大半是 merge,会把参与率压到 40% 的假象上。
git log --no-merges --grep='Co-Authored-By: Claude' 按月聚合。2025 年下半年是纯手工期,2026 年 1 月突然跃升到 87%,此后稳定在 70—91%。| 阶段 | 时间 | AI 参与 | 在发生什么 |
|---|---|---|---|
| 手工期 | 2025-07 → 12 | 0% | 188 次提交,全部人写。脚手架(create-react-router)、Drizzle、db 层、课程视图。唯一的例外是 10 月的 4 次尝试。 |
| 跃迁期 | 2026-01 → 03 | 87→91% | 提交量从 326 冲到 582。RALPH: 前缀提交暴增(2 月 153、3 月 474)——无人值守批处理循环在跑。 |
| 平台化期 | 2026-04 → 07 | 70→78% | AFK 平台成型:8 个 GitHub Actions 工作流、label 状态机、Sandcastle 运行器契约。RALPH 循环退场(6 月 0、7 月 18)。 |
| 质量期 | 2026-08 → 10 | 36→89% | 8—9 月谷底是 ADR 决策 + Animatic 攻坚;10 月回到 89%,同时 .claude/skills/verify-cvm/ 长到 417 行——重点从「让 AI 写代码」转向「让 AI 自证正确」。 |
这个项目最贵的资产不是代码,是那 4809 行写给 Agent 读的文本。它们按「加载时机」分成四层,Agent 在不同阶段读不同的层:
CLAUDE.md 明确只做引用与指路,把细节下沉到 docs/——这样纪律文本只有一份,不会漂移。作者对这个分层的态度写在 CLAUDE.md 里,值得原样引用:
“Read it while writing code, not only while reviewing it: "every
anyis a leak" binds the hand that writes the cast. It sits at the repo root rather than in.sandcastle/for that reason.”在写代码的时候读它,而不是只在评审的时候读。「每个
any都是一处泄漏」这句话约束的是写下那句 cast 的那只手。所以它放在仓库根目录,而不是藏在自动化目录里。
多数团队给 Agent 的上下文是「代码库 + 任务描述」。这个项目的第一层给的是一张命名表——578 行的 GLOSSARY.md,每个词条长这样:
**Course**:
The primary domain entity: a structured collection of versions, sections,
lessons, and videos. It is not backed by any repository on disk (ADR 0018).
_Avoid_: Repo, Project
**Draft Version**:
The one CourseVersion that can still change, and the only one that accepts
writes to sections, lessons, videos and clips. There is exactly one per course.
_Avoid_: Current version, Working version
注意三件事:
ADR 0018)——词汇和决策是交叉索引的,不是两套文档。_Avoid_ 反义词表。这不是修辞洁癖:一个 Agent 如果把「Course」叫成「Repo」,它会在代码里引入一个不存在的磁盘仓库概念,然后写一堆错误代码去 sync。Draft Version → Pending Version → Published Version)。Agent 最擅长「看起来合理」的推断,而领域状态恰恰不能推断。docs/agents/domain.md 把这条纪律变成了对 Agent 的硬要求,并给出了唯一的正确反应:
“If your output contradicts an existing ADR, surface it explicitly rather than silently overriding:
_Contradicts ADR-0007 (event-sourced orders) — but worth reopening because…_”如果你的输出和已有 ADR 冲突,显式说出来,不要静默覆盖。
术语表的作用不是「统一叫法」,是给 Agent 一个不允许自由发挥的命名空间。成本极低(写一个词条 5 分钟),收益极高(避免整类概念性 bug)。如果你的 Agent 老是在造同义词,缺的就是这张表。
56 份 ADR 覆盖从「课程版本快照」到「录制服务何时启动」的全部结构性决定。但真正有价值的是它们的写法——每份都有 Rejected alternatives 和 Consequences 两节。看 ADR 0008 的片段:
## Why
A PRD's sub-issues are not independent — earlier sub-issues frequently
subsume later ones. By the time the agent reaches sub-issue 4, the rename /
refactor / cascade work it describes may already have landed in the commit
that closed sub-issue 2.
## Rejected alternatives
- **Check "branch is ahead of `main`" instead of "this run produced commits".**
Closer to the right invariant, but still wrong: it would fail a legitimate
first-sub-issue scenario...
- **Reorder the workflow so PR creation precedes sub-issue close**...
Not adopted yet — current ordering is fine once the no-commit case is
accepted as success.
这份 ADR 记录的是一次系统性的自我纠错:原来的守卫规则是「Agent 一个提交都没产出就算失败」——但这个规则恰好惩罚了正确的行为(工作已经在前面的切片里做完了)。作者把它改成 ADR 而不是悄悄改代码,因为:
“If the agent silently does nothing (genuine bug, not "work already done"), the workflow still chains forward. The PR review step at the end of the PRD is the catch-all for "did this PRD actually deliver its acceptance criteria" — not the per-sub-issue commit count.”
如果 Agent 真的什么都没干(不是「活已干完」而是真 bug),流水线照样往前走。兜底的是 PRD 末尾的评审环节,而不是每个切片的提交数。
ADR 对人类的价值是「可追溯」,对 Agent 的价值是「不许重新发明」。写 ADR 时最值钱的一节是 Rejected alternatives——它拦住的不是错误,而是「看起来更好的错误」。
CODING_STANDARDS.md 有 409 行散文。光靠散文,Agent 会读、会点头、然后照样违反。作者的答案是 19 个 check-*.sh / .ts 脚本(共 2623 行),把纪律编译成退出码:
check = check:static && test && check:client-build
check:static = check-effect-tsc && typecheck && lint && lint:boundaries
&& check-file-tokens && check-no-dirname && check-no-gitlinks
&& check-script-db-url && check-db-clients && check-clip-inserts
&& check-env-example && check-routes-folder && check-entity-links
&& check-duration-format && check-response-body && check-fs-writes
&& check-toast-import && check-effect-guards && check-frontend-state
&& check-background-jobs && check-complexity
这些守卫不是泛泛的 lint。它们编码的是这个项目的具体教训。看 check-toast-import.sh 开头的注释:
# Every toast goes through apps/local/app/components/ui/toast.tsx, which
# clamps its text, adds "Show more", and gives error toasts a "Copy" action.
# A toast imported straight from "sonner" skips all of that, so only the
# files that wrap sonner (and the wrapper's own test) may import it.
它甚至知道该放过哪些文件(三个白名单),并且报错信息里给了正确的替代方案:
ERROR: sonner imported directly — import toast from "@/components/ui/toast" instead:
"@/components/ui/toast bounds every toast (four-line clamp, Show more, Copy on errors).
For a caught error, use toastError(error, fallback) from the same module.
--all。默认是扫描 staged 文件(跑在 pre-commit 里,快);CI 里用 --all 扫全部。提交循环要短,CI 要全。check:response-body,「为了让提交循环保持短」。correctness 警告是一份手工清理的存量待办,「你碰到的文件里有一条警告是邀请,不是拦路石」——只有那些编码了已记录标准的规则才是 error。这条区分极其重要:如果所有 lint 都是硬错误,Agent(和人)会学会绕过它。还有一个更小的例子,说明这条路走得多远——一个 5 行的 hook,专门拦 npx tsc:
# .claude/hooks/block-npx-tsc.sh
if [ "$FIRST_WORD" = "npx" ] && echo "$COMMAND" | head -1 | grep -qE 'npx\s+tsc(\s|$)'; then
echo 'Use `pnpm run typecheck` instead of `npx tsc`' >&2
exit 2
fi
注释里还写着:只在 npx 真的是被执行的第一个词时才拦,不拦「提交信息里提到 npx tsc」这种文本参数。
把「我们团队的规矩」翻译成 exit 1 的那一刻,它才对 Agent 生效。而且守卫的错误信息必须给出正确答案——Agent 读到「不许这样」会困惑,读到「改用这个」会照做。优先级:先守卫那些「错了会造成真实损害」的规则(写生产库、绕过 hook、直接 import 底层库)。
这是整个实践里最激进的一步:作者没有自建任务数据库。议题的状态完全由 label 表达,8 个 GitHub Actions 工作流监听 label 变化来推进。
docs/agents/afk-agent-platform-spec.md(65 KB)是这份设计的完整规格,开篇就声明了它的野心:
“This document is the source of truth. You should be able to re-implement the whole system in a fresh repository from this spec alone, without reading the original workflow YAML.”
这份文档是唯一事实来源。你应该能只靠它,在一个全新的仓库里把整套系统重建出来,而不需要去读原始的 workflow YAML。
| Label | 打在 | 含义 |
|---|---|---|
agent:to-issues | PRD 议题 | 把 PRD 拆成扁平的子议题列表 |
agent:implement | 议题 | 同一个 label 触发两条不同流程——有子议题就走 PRD 模式,没有就走单议题模式 |
agent:queued | 议题 | 可以干了,但被原生 GitHub 依赖阻塞着;阻塞者一关闭就自动提升 |
agent:in-progress | 议题 | 一把锁:加上它属于「接受任务」的一部分,移除它写在 always() 里 |
agent:review | PR | 评审并主动改进这个 PR |
agent:blocked | 议题 / PR | 运行失败或被拒绝,需要人来看 |
注意 agent:implement 的歧义消解方式——按议题的「形状」判断,而不是靠人选择:
# 有子议题 → 这是 PRD,交给 agent-implement-prd.yml,这里静默跳过
# 有父议题 → 这是别人的子议题,拒绝运行(子议题从不直接打标签)
# 都没有 → 独立议题,正常实现
“One issue → one branch → one PR. Branch names are deterministic (
agent/issue-<n>-<slug>)…
Nothing auto-merges to the base branch. Review marks a PR ready for review and posts a review withevent: "COMMENT"— neverAPPROVE. A human approves and merges.”
第三条尤其值得注意:评审 Agent 发的永远是 COMMENT,从不 APPROVE。而且它不能关闭 review thread——「解决线程是评审者(人)的活,Agent 只能回复」。
用 label 当状态机的最大好处不是「省了一个数据库」,而是状态变更天然可审计、天然有 UI、天然能触发事件。你不需要写一套任务系统,GitHub 已经是了。代价是:并发、竞态、失败分类都得自己写进 workflow——这份规格里有专门的三节讲这个(§3.5 并发模型、§3.6 推送安全、§3.7 失败处理)。
整份规格里最重要的一句话,被作者称为「central design rule」:
“the orchestrator owns every tracker/VCS mutation (labels, comments, pushes, PR creation, issue closing). The agent runner only emits files. This seam is what makes the runner swappable and the system testable — the agent never holds a GitHub token and never calls the GitHub API to mutate state.”
编排器拥有所有 tracker / VCS 变更,Agent 运行器只输出文件。这条接缝让运行器可替换、让系统可测试——Agent 从不持有 GitHub token,从不调用 GitHub API 去改状态。
契约具体长这样:
ISSUE_NUMBER、BRANCH、OUTPUT_DIR…OUTPUT_DIR 里的文件failure_reason.txt — 失败原因has_commits.txt — true/falsepr_title.txt、pr_description.txtreview_payload.json(经 schema 校验)这个设计的收益是具体的:Architecture Review 工作流(每工作日定时跑一次,扫代码库找深化机会)也是只吐一份 PRD 的结构化输出,由编排器去建议题。作者明确写了:
“This holds universally — even Architecture Review only emits a PRD as structured output; the orchestrator creates the issue.”
给 Agent 的 token 越少,它闯的祸越小;它和外部系统的接触面越小,你替换它时越轻松。把「做事」和「改状态」拆成两个进程,是你今天就能做的一个架构决定,与用不用 Claude Code 无关。
这是全篇最「懂 LLM」的一段。问题很具体:让一次 LLM 调用既干活又吐严格 JSON,很脆——一个格式错误的 blob 会让整次运行失败,而活已经干了(副作用已发生)。作者的两种解法:
更见功力的是作者对「模型输出不稳定」的处理方式——不是加更多约束,而是在 schema 里接受别名:
path 或 file;接受 body 或 commentline,或字符串 lineRange(从中解析出前导整数)side 默认 RIGHTpath+line 不在真实 diff hunks 里的行内评论(幻觉锚点),以及任何 commentId 不在已抓取线程里的回复最后一条是关键:不指望模型不犯错,而是在落库前用真实 diff 做一遍交叉验证。规格里那句小标题叫 Input tolerance (hard-won robustness)——「hard-won」两个字,说明这是踩出来的。
两件事今天就能做:(1)凡是「有副作用 + 要结构化输出」的 Agent 任务,拆成两遍;(2)凡是让模型产出「指向真实代码的位置」的输出(文件名、行号、issue 编号),解析后一律用真实数据做一次存在性校验再落地。这两条能把 Agent 流水线的失败率砍掉一大半。
.claude/skills/to-issues-project/SKILL.md 负责把 PRD 拆成子议题。它的核心规则叫 tracer-bullet(曳光弹):
“Each slice delivers a narrow but COMPLETE path through every layer (schema → API → UI → tests), NOT a horizontal slice of one layer…
Sub-issues are flat — a sub-issue must not itself need sub-issues. If a slice is too big to leaf, split it into multiple peer slices instead of nesting.”
配套的几条约束都很实在:
模板里还有一处微妙的安排:子议题正文故意不包含 Closes 指令。关闭子议题是 PRD 模式工作流的职责,关闭 PRD 本身靠 PR 描述里的 Closes #<PRD>。谁关什么,是设计好的,不能让 Agent 顺手关错。
「垂直切片」是老概念,但对 Agent 有新增量:切片必须能独立验收,因为 Agent 没有跨会话的隐性记忆——它只看到分支上有什么。另外「正文不写文件路径」这条反直觉但正确:路径会过期,而决定不会。
这是我认为整个仓库最值得抄的一份文件:.claude/skills/verify-cvm/SKILL.md,417 行。它解决的是一个所有人都有的痛点——Agent 说「我做完了,测试通过了」,但 UI 根本没生效。
作者把它写成了硬性流程,塞在 CLAUDE.md 的最显眼处:
“Any change with a visible effect in the UI is checked in the browser with verify-cvm before you open the PR. Passing tests and a green
checkdo not replace it… The PR body states what you clicked and what you saw, with screenshots or the evidence path. A change with no UI effect says so in one line —No UI change— instead.”任何对 UI 有可见影响的改动,在开 PR 之前都要用 verify-cvm 在浏览器里跑一遍。测试通过和
check变绿不能替代它。PR 正文要写清楚你点了什么、看到了什么,附截图或证据路径。没有 UI 影响的改动就写一行「No UI change」。
最后那半句是点睛之笔:「没有 UI 改动」也必须显式声明。不给 Agent 留「我也不知道算不算」的模糊地带。
pg_stat_user_tables 计数器(有延迟),把一次真实的 UI 保存判成了「干净」。is-filming.sh,在录制就打印 PAUSED、每 60 秒复查、自己继续。「别绕过暂停」——因为录制时抢 GPU 会毁掉素材。db:verify-snapshot(那是唯一读生产的一步)。两个脚本都会拒绝 Agent 的 shell。features/README.md 末尾老实写着哪些页面「还没画进来」(Diagrams、归档课程、API Token…),以及 6 个已映射功能的多个入口。原文:「在你宣布某功能已验证之前,先查查这个索引有没有列出同一个行为的另一个入口。」证据的定义也被写死了四条:走真实用户路径(路由 + 按钮,不是自己搓一个 API 调用)、拍动作前和动作后两个状态、验证副作用(回读那一行,而不只是看页面)、看控制台(水合失败的截图是看不出来的)。
不需要照抄这套 417 行的机器,值得抄的是三条原则:(1)「测试绿了」不等于「功能生效」,必须有独立的、看得见 UI 的验收环节;(2)给 Agent 的数据永远是克隆,且物理上断掉它通往生产的一切路径(不是靠提示词请求,是靠代码拒绝);(3)验收要留下人能不重跑就能读懂的证据。
| 指标 | 数值 | 说明 |
|---|---|---|
| 提交总数 | 3534 | 其中 940 个是合并提交,2594 个是实质提交 |
| 时间跨度 | 15.4 个月 | 2025-07-23 → 2026-10-30 |
| AI 署名提交 | 1961 | 占实质提交 75.6%;含合并提交口径则是 56.5% |
RALPH: 前缀提交 | 766 | 无人值守批处理循环产物,2026-06 后归零 |
| 合并的 PR | 483 | 平均每个 PR 约 5.4 个实质提交 |
| 提交粒度 | 中位 9 文件 | 均值 14.6;59% 的提交 ≤10 个文件;最大 184 |
| 峰值日 | 325 提交 | 2026-10-09 单日,其中大量是并行 worktree 的合并 |
| 代码 / 测试行 | 347k / 126k | 653 个测试文件,测试密度约 36% |
git show --stat 逐条解析。署名 trailer 里留下了完整的模型演化痕迹,说明作者一直在换模型,而不是绑定某一个:
| 模型 | 提交数 | 说明 |
|---|---|---|
| Claude Opus 4.6 | 862 | 主力,横跨 2026 年上半年 |
| Claude Opus 5 | 611 | 2026 下半年主力 |
| Claude Opus 5.5 | 544 | 当前默认(merging.md 指定每个提交都带它的署名) |
| Claude Opus 4.5 | 238 | 2026 年初 |
| Claude Sonnet 4.5 | 95 | 轻量任务 |
| Claude Opus 4.8 (1M) | 68 | 1M 上下文版本 |
| Claude Sonnet 4.6 | 60 | 轻量任务 |
| Claude Opus 4.7 (1M) | 44 | 1M 上下文版本 |
| Claude Opus 5 (1M) | 37 | 1M 上下文版本 |
| Claude Opus 4.6 (1M) | 22 | 1M 上下文版本 |
口径说明:同一提交可能匹配多个模型名(如「Opus 5」是「Opus 5 (1M」的前缀),所以上表之和大于 1961。趋势可放心引用,绝对数请按「主要型号」理解。
DB-TDD.md 里那条「不要测试类型系统已经证明的东西」是对的,但它无法解决根本问题:当一个 Agent 同时写了实现和测试,绿灯只能证明这两份代码彼此一致,不能证明它们都符合业务意图。
这个项目的缓解手段是 verify-cvm(真实浏览器)+ 人评审 PR,两者都在 Agent 之外。如果你的项目没有这两层,TDD 的绿灯基本是安慰剂。
4809 行契约 + 65 KB 平台规格 + 417 行验证技能 + 19 个守卫脚本——这些是单人维护的基础设施。规格里那些「hard-won robustness」的注释说明:每一条都是踩坑换来的,而踩坑的人只有作者一个。
对团队而言这是真实的复制壁垒:不是技术门槛,是投入门槛。合理的做法是挑 ROI 最高的三条先做(见下节),而不是一次性照抄。
作者明说 correctness 警告是一份手工清理的存量待办。这个政策对 Agent 友好(不会因为碰了一个文件就被迫清理整个文件),但代价是债务不清零。在 347k 行代码上,这会变成一个长期负担。
对策:给存量债务设一个硬上限(如「警告数不得增长」),把它变成一条机械规则,而不是靠自觉。
2026-08/09 的 36% 不是偶然:那两个月在写 8 份 ADR 并攻坚 Animatic 视觉分镜。凡是「还没决定怎么做」和「需要肉眼审美判断」的领域,Agent 参与率会塌。这不是缺陷,是当前的真实边界。
对策:把这类工作显式标记为「人工期」,别指望自动化;同时注意——正因为 ADR 在这两个月定型了,10 月的 AI 参与率才能回到 89%。决策投入不是自动化的对立面,是它的前置条件。
本次分析没有作者的 Claude Code 会话日志(~/.claude/projects/ 下没有本项目目录),所以无法统计 token 消耗、会话时长、人类指令条数。所有结论来自仓库内的 AI 契约文件 + git 元数据 + 代码三方交叉取证。这比会话日志更可靠地反映了「沉淀下来的实践」,但无法反映「实际执行中的偏差」——例如 Agent 有多少次违反了守卫又被拦下。
| # | 做什么 | 投入 | 为什么值 / 怎么起步 |
|---|---|---|---|
| 1 | 写一个带 _Avoid_ 的术语表 | 半天 | 最便宜、见效最快。20 个核心概念就够。 Agent 立刻停止造同义词。 |
| 2 | 把「不许做」写成守卫脚本 | 1—2 天 | 先守三条最痛的(写生产库、绕过 hook、直接依赖底层库)。错误信息必须给出正确替代。 |
| 3 | 「测试绿了」之外加一道 UI 自证 | 2—3 天 | 不必做 417 行。最低配:一条命令起隔离环境 + 强制 PR 正文写「我点了什么、看到了什么」。 |
| 4 | ADR 必须写 Rejected alternatives | 边际成本 | 改模板即可。它拦住的是「看起来更好的错误」——最贵的一类返工。 |
| 5 | 有副作用的 Agent 任务拆成 produce / extract | 1 天 | 今天就能改。凡是「先干活再吐 JSON」,都拆成两遍。失败率立刻下降。 |
| 6 | 模型产出的「位置」一律用真实数据校验 | 半天 | 文件名 + 行号 + issue 编号,落库前拿真实 diff 过一遍,不存在就丢。 |
| 7 | Agent 不持有写权限,只吐文件 | 架构决定 | 编排器独占所有远端变更。换模型 / 换 runner 时零改动。 |
| 8 | 垂直切片 + 扁平子任务 | 边际成本 | 切片要能独立验收、一次会话能做完。禁止嵌套。 |
| 9 | 永不自动合并;Agent 永不 APPROVE | 零成本 | 一条规则,守住最后一道闸。也别让它关闭 review thread。 |
| 10 | 统计 AI 参与度时先剔除合并提交 | 5 分钟 | 否则你的数字会低 20 个百分点,然后得出错误结论。 |
本报告的每一个数字都有一条命令可以复跑。分析过程全程只读,未对被分析仓库做任何修改。
# 克隆
git clone https://github.com/mattpocock/course-video-manager.git
cd course-video-manager
# 总提交 / 合并提交 / 实质提交
git log --oneline | wc -l # 3534
git log --merges --format=%H | wc -l # 940
git log --no-merges --format=%H | wc -l # 2594
# AI 参与度(两个口径,注意差 20 个百分点)
git log --grep='Co-Authored-By: Claude' --format=%H | wc -l # 1995 (56.5%)
git log --no-merges --grep='Co-Authored-By: Claude' --format=%H | wc -l # 1961 (75.6%)
# RALPH 无人值守循环
git log --grep='^RALPH:' --extended-regexp --format=%H | wc -l # 766
# 按月聚合(本文图二的数据来源)
git log --no-merges --date=format:'%Y-%m' --format='%H %ad' # 分母
git log --no-merges --grep='Co-Authored-By: Claude' \
--date=format:'%Y-%m' --format='%H %ad' # 分子
# 模型谱系
for m in "Opus 4.6" "Opus 5" "Opus 5.5" "Opus 4.5" "Sonnet 4.5"; do
echo "$m $(git log --grep="Co-Authored-By: Claude $m" --format=%H | wc -l)"
done
# 规模
git ls-files | grep -E '\.(ts|tsx)$' | xargs wc -l | tail -1 # 347284
git ls-files | grep -E '\.(test|spec)\.(ts|tsx)$' | wc -l # 653 个测试文件
ls docs/adr/ | wc -l # 56 份 ADR
ls scripts/ | grep -c '^check-' # 19 个守卫脚本
# PR 数
git log --format='%s' | grep -c '^Merge pull request' # 483
# 契约体量
cat CLAUDE.md CODING_STANDARDS.md GLOSSARY.md docs/agents/*.md \
$(find .claude/skills -name '*.md') $(find .sandcastle -name '*.md') | wc -l # 4809
| 路径 | 行数 | 它证明了什么 |
|---|---|---|
CLAUDE.md | 81 | 入口导航;UI 改动必须浏览器自证的硬要求 |
GLOSSARY.md | 578 | 词汇即接口;每个概念的 _Avoid_ 反义词表 |
CODING_STANDARDS.md | 409 | 工程纪律散文层(再由脚本编译成 exit code) |
docs/agents/afk-agent-platform-spec.md | 65 KB | AFK 平台完整规格;可据它在新仓库重建整套系统 |
docs/agents/merging.md | ~60 | 落地一个改动的 5 步;「永不跳过 hook」 |
docs/agents/domain.md | ~50 | ADR 冲突必须显式声明,不许静默覆盖 |
docs/agents/triage-labels.md | ~40 | 5 个 triage 角色 → 本仓库实际 label 的映射表 |
docs/agents/queued-promotion.md | ~60 | 竞态处理;label 触发下游需要 PAT 而非 GITHUB_TOKEN |
.claude/skills/verify-cvm/SKILL.md | 417 | 真实浏览器自证;隔离边界;Write Ledger |
.claude/skills/verify-cvm/features/*.md | 6 个 | 功能地图;老实列出「还没画进来」的页面 |
.claude/skills/do-work/{DB,FRONTEND}-TDD.md | 56 | 一次只写一个失败测试;不测类型系统已证明的东西 |
.claude/skills/to-issues-project/SKILL.md | 136 | tracer-bullet 垂直切片;子议题必须扁平 |
.claude/skills/to-prd-project/SKILL.md | 86 | PRD 是 spec 不是 sketch;四类读者 |
.claude/skills/deploy-migration/SKILL.md | 71 | 生产迁移只由人执行;破坏性变更走两段式 |
.claude/skills/optimize-loader/SKILL.md | 79 | 性能反模式(重复取数 / 过度取数 / 串行) |
.claude/hooks/block-npx-tsc.sh | 5 | 最小可行守卫:拦 npx tsc,且只拦真执行 |
.sandcastle/*/prompt.md | 9 个 | 提示词即流程操作系统;红-绿-重构写进提示词 |
.github/workflows/agent-*.yml | 7 个 | label 状态机的实际实现;形状探测 / 前置检查 |
scripts/check-*.{sh,ts} | 19 个 / 2623 行 | 把纪律编译成退出码 |
plans/ralph.sh | ~25 | 第一代自动化:无人值守批处理循环(已被取代) |
docs/adr/0008-agent-implement-zero-commit-runs.md | ~30 | 系统性自我纠错;Rejected alternatives 的范本 |
这个项目的 AI 原生程度,不是由「用了多少 AI」决定的,而是由「有多少工程判断被写成了机器可读、机器可执行的文本」决定的。 75.6% 的提交由 AI 产出只是结果;4809 行契约、56 份 ADR、19 个守卫脚本、417 行验收技能才是原因。 而 2026 年 8—9 月那个 36% 的谷底提醒我们:在决定还没做出来之前,什么都自动化不了。