Urazuke — 课程。附实践任务的手册,逐级台阶,直到应用上线生产

作者:Pablo Kuschniroff,由 Claude Code 辅助。第 2 版,2026 年 10 月 18 日。这是 Urazuke 项目的一份工作文档:我会和第一期使用它的学员一起修正它。它用日常的语言写成,每个缩写或陌生的表达都在主文档(ESCALERA_METODOLOGICA.md)的术语表里。当我引用一个真实案例时,我用一个 [Ref. N] 标记,它指向参考资料文档;手册保持干净,细节一点即达。方法的公开仓库是 github.com/pkuschnirof/urazuke。

开始之前

这门课是给谁的

给那些想用人工智能构建软件、并且希望这个软件被真正使用——被真实的人、使用好几个月——的人。这不是一门编程课:你不会学到一门语言或一个框架。也不是一门"提示词"课:你不会学到让模型写得更好的小技巧。这是一门方法课:做什么、按什么顺序、在每个时刻向模型要什么、它必须交给你什么,以及如何——不读代码——知道它交付的东西是完整的。

如果你是下面这些人之一,这门课对你有用:

如果你要的是周五演示用的原型,这门课对你没用。那有更快的工具,而这个方法会让你觉得慢。这个方法是给演示之后还得继续运转的东西准备的。

结束时你会拥有什么

一个属于你的应用,在生产环境中运行,并且带有:在通过之前曾被看到失败的测试;所有流程的端到端测试;可复现的部署和回滚;仓库之外的配置和密钥;写有日期的业务决策;系统的语义树,以及每一样被构建之物的详细规格,都在它的待办项里;对一切收费、发送或删除的东西上的锁;三份一个人无需 AI 就能照着做的手册;一次由与构建者不同的代理完成的审计;以及一份带数字的复盘。这就是我在这里所说的"生产级":不是它能运行,而是它能被维持。

而且你还会得到另一样东西,那才是你真正带走的:不拿到证据就不信模型的反射,以及确切知道在每一步该向它要什么证据。

你需要什么

  1. 一个用来编程的 AI 工具。 任何一个能在你的仓库里读写文件并执行命令的都行:Claude Code、 Cursor、Codex CLI、Gemini CLI、Forge。方法不依赖于用哪一个;主文档有一张表,说明每个产出物在每种工具里放在哪儿。在这本手册里我说"代理",指的就是你用的那个。
  2. 一个 GitHub 账号(或等价物),以及关于什么是仓库、提交和 push 的最起码的概念。如果你没有,开始之前花一个下午;任何入门教程都够。不需要更多。
  3. 一个项目。 最理想的是你自己的:一个你需要的或者客户向你要的系统,有会使用它的用户。如果你没有,就用我在下面描述的引导项目;它被设计成能走完二十四级台阶而不跳过任何一级。如果你手上的是一个已经存在、需要纠正或者让它成长的系统,那么第一部分在一个新项目上教你方法,而 课程第 2 部分教你先把它规范化,用的是第二个继承来的引导项目。
  4. 时间。 课程有二十四节课,每级台阶一节,还有一个顺序告诉你每件事落在哪个阶段(见"课程的顺序")。我刻意不给你期限:方法是一个流程,不是一个速度的承诺,而每个阶段要花多久由你项目的规模来定。我给你的是顺序,而顺序不可协商。
  5. 一个有时间做决定的领域所有者。 如果项目是你的,那就是你。如果是客户的,那就是客户,而且他得知道整个项目期间他都要回答问题。这一点不可协商:没有做决定的所有者,模型就会做决定,方法在那一刻就坏了。

怎么上课

每节课都是同一个结构:

整门课只有一条规则:完整性检验有一项是"否"时,不要前进到下一级台阶。"以后再修"的诱惑恰恰是方法存在的目的所要避免的。如果一级台阶没登完整,下一级就建在沙子上。

课程的顺序:每件事什么时候做

主文档的 B 部分有方法完整的时间线。这里我把它翻译成课程的顺序,针对引导项目。它们是阶段,不是周:每个阶段要花多久由你的项目来定,而不可协商的是顺序,尤其是,课与课之间发生什么。

阶段课除了课之外你要做什么
0无需求摄入:你采访所有者,用散文写下他想要什么、给谁用、今天的情况是什么、什么有后果、哪些数字只有他知道。你说出能说出的用例("前台登记一个预约"),哪怕手画也把主要界面画出来,并列出业务实体及其状态。刻意留着不完整。
10、1、2、3、4启动,一口气做完。 适格性、契约、带语义树的定义、容量估算,以及带着全部基础设施和 CI/CD 链的骨架:仓库、测试环境、持续集成、第一次空部署。结束时,你打开 URL,看到空白的界面。
25、6、8、10第一个功能块(基本日程表)。你把请求转换成有规格的待办项来打开功能块。对每个待办项:决策、先证红的测试、流程的端到端测试、你亲自看的界面截图。功能块结束时:完整的端到端测试套件,而且你在测试环境里按功能块的流程使用系统。
37、9、11、12第二个功能块(患者与搜索)。循环的其余四节课在这个功能块上学。同样的收尾:所有流程的端到端、人工测试、带新界面的用户手册。
413、14上线生产,前半段。 由另一个会话做审计。锁。然后才是第三个功能块(提醒和定金),用完整的循环构建,留在演练模式。
515、16、17、18、19、20上线生产,后半段。 收尾通往生产的链条,回滚经过测试。级别可配置的可观测性、前后都测量过的持久化、作为闸门的漏洞扫描、带断裂点的负载测试。三份手册;一个不编程的人照着恢复手册做。你或所有者对所有流程做验收测试。第一次没有用户的生产部署。
621、22、23运维。 看过演练记录之后启用提醒。两个监控随部署搭起来:资源与性能、错误;第一份周期报告和所有者一起读。驻留代理。带数字的复盘和对梯子的修改提案。

如果你的项目更大,长大的是第 2 和第 3 阶段(更多功能块),不是第 1 阶段,也不是第 4 和第 5 阶段。而如果某个时刻你发现自己在第 3 阶段做基础设施,或者在第 5 阶段第一次跑端到端测试套件,回到顺序:有东西被跳过了。

引导项目:"Turnera"

如果你没有自己的项目,用这个。它很小,但拥有让二十四级台阶里没有一级是假的所需要的一切:真实用户、个人数据、发向外界的消息,还有钱。

业务。 一家诊所(也可以是一家事务所、一家理发店、一家修理铺:任何给人安排预约的地方)。今天预约记在一个本子上,靠手工用 WhatsApp 确认。预约会丢,因为人们忘了;会有重复预约,因为两个人记在了不同的本子上;没人知道上个月取消了多少预约。

他们想要什么。 一个网页日程表,两名前台和三名专业人员都能看到预约;提前 24 小时通过 WhatsApp 或邮件自动发提醒;对某些预约可以通过支付链接收取定金;以及月底的几个数字(多少预约、多少缺席、多少定金)。

所有者。 诊所负责人。她对业务有判断力(知道哪些预约要收定金、提前多久可以取消、对一个爽约三次的患者说什么),而且每天有十五分钟回答问题。她不编程,也不会读代码。

只有她知道的数字。 每天大约四十个预约,一年二百五十天。每个预约大约三条消息(确认、提醒,有时改期)。患者数据保存五年。高峰是周一早上,两名前台同时录入。还有一个她答不上来但很重要的问题:如果这玩意儿好用,他们会卖给其他诊所吗?(答案会让容量估算乘以十。)

有后果的东西。 发送一条提醒(发两次,惹人烦;发到错误的号码,是隐私问题)。收取定金(钱)。取消一个预约(对失去它的患者来说不可逆)。删除一个患者(个人数据)。

负责人说出的用例,照她的原话:"我登记一个预约"、"我找一个患者"、"我取消一个预约"、"患者收到提醒"、"我向他要定金"、"我看这个月有多少预约"。一共六个;它们将是端到端的流程。

有这些就够开始了。其余的一切都由代理来问,而让它来问正是练习的一部分。


启动阶段:第 0 到 4 课

这五节课每个项目做一次,在第 1 阶段,写功能之前。它们是最让人想跳过的("我想看到点能跑的东西"),也是跳过代价最高的。一口气做完,然后再打开循环。

第 0 课 — 这个项目能这样做吗?

你需要理解什么。 方法并不适用于任何项目,而在开始之前就知道这一点是第一个质量决策。有两个条件,没有它们方法就不起作用:有一个标准仓库可以存放证据(X1),以及界面可以程序化操作、从而能做从界面到后端的端到端测试(X2)。只要其中任何一个不满足,代理就无法自己验证自己,于是最终是你去读代码,而那恰恰是方法承诺要避免的。这两条我都是从我拒绝过的项目里得出的 Ref. 46。此外还有加权准则(是否有一个有时间的所有者、错误是否有后果、能否在独立环境里测试,等等),我还在校准。

对引导项目来说两个条件都满足:它是一个新项目(没有遗留),而且我们要用一个可程序化操作的网页界面来构建它。对你自己的项目,这节课可能以"不可行"结束,而那是课程的一个有效结果。

任务。

发送提示词之前,用你自己的手(代理没法替你做这些):

  • docs/INITIAL_SURVEY.md:由你手工写,根据第 0 阶段的需求摄入:客户想要什么、给谁用、今天没有系统时是怎样的、现有或拟用什么技术、是否有旧系统以及它有没有仓库、界面能不能由程序操作、所有者是谁以及他有多少时间、一个错误有什么后果、将有哪些环境。半页。不完整没关系;事实上,不完整正是练习的一部分,而你不知道的就写成"我不知道"。
  • docs/ELIGIBILITY_CRITERIA.md:把主文档里的准则卡片(排他性准则 X1、X2 和候选准则)复制过来,答案、来源和绿或红那几列留空。不要你自己填:填它的是代理。
  • 项目文件夹,除了这两个文件之外还是空的,代理在里面打开。
  • 每个文件的完整内容在第 0 级台阶的契约里,在同一个方框中。
  1. 粘贴这条提示词:

We are going to assess whether this project can be run with the Urazuke method. Read the initial survey in docs/INITIAL_SURVEY.md and the criteria sheet in docs/ELIGIBILITY_CRITERIA.md. First, evaluate whether the survey is sufficient to answer every criterion. If any criterion cannot be answered from the survey, ask me for the missing information before going on; do not assume. Then fill in the sheet criterion by criterion: the answer, where it comes from, and for each exclusionary criterion whether it is green or red. Do not decide: present me the completed sheet with your reading and your doubts, and I will decide. Reply in my language.

你在要求它读这两个文件,评估调研是否足够,并在回答之前问清缺失的东西(这个条款在课程的所有提示词里都会重复;它是方法的核心),填写卡片并说明每个答案来自哪里,以及不要做决定:决定是你的。

  1. 回答它的问题。如果它一个都没问,就该怀疑,问它假设了什么。
  2. 当它把卡片呈给你时,读一读,然后决定。

发送提示词之前(收尾提示词):你的决定,一行话(可行、不可行,或有条件可行,每个条件都可验证,而且不超过三个),以及你在这同一个会话里给它那些问题的回答,因为提示词要求它把这些包含进去。

  1. 粘贴收尾提示词:

Decision: [go / no-go / go under these conditions: …]. Write the final sheet to docs/ELIGIBILITY.md with the date, who decided, the answer to every criterion with its source, and the conditions if any. Include the questions you asked and my answers.

完整性检验。 打开 docs/ELIGIBILITY.md 并问自己:两条排他性准则是否有明确的答案和来源?是否有哪条准则是用"可能"来回答的?(如果有,代理本该去问。)决定是否有日期和名字?条件(如果有)能否验证?它问过的问题和你的回答在里面吗?

交付物。 一页的 docs/ELIGIBILITY.md。

常见错误。 把调研写得"完整"好让代理不提问:你错过了练习,更糟的是,你养成了替模型干活的习惯。带着五个条件回答"可行":那是伪装的"不可行"。还有最常见的:跳过这节课,因为"显然可行"。显然的东西就是不会被写下来的东西,而不被写下来的东西就是之后会被争论的东西。

在引导项目中。 代理至少应该问:界面是网页还是移动端(因为 X2 取决于此),有没有旧系统哪怕只是一个 Excel,以及要用什么消息渠道(WhatsApp 有邮件没有的要求)。卡片以"可行"结束,附一个合理的条件:在第 14 级台阶之前确定消息渠道。

第 1 课 — 协作契约

你需要理解什么。 代理每次会话到来时都不记得上一次。它像每天早上来一个新同事。协作契约就是你第一天对它说的、而它每天都会重读的东西:你怎么工作、你不能容忍什么、你给它多大的自主权。没有契约,你每次会话都要重复同样的话,而且每次都略有不同。我第一次写它是在重复了两周之后 Ref. 1。

六条最低规则背后各有一个故障:"我的时间是神圣的"避免它每个细节都来问你;"零债务"避免"以后再修";"范围精确"避免它"顺便"给你加东西;"文档在同一次提交中"避免过时的手册;"绝不把未验证的报告为已完成"避免那个不是真的"搞定";"说我的语言"避免它因为提示词是英文就用英文回答你。

任务。

发送提示词之前,用你自己的手:

  • 你的工作偏好,花五分钟想好并记在一张短清单上(写在一张纸上,或者写进 docs/MY_PREFERENCES.md):你希望它多问还是少问?用什么语言?你给它多大的自主权(能自己提交,能自己部署到测试环境,生产环境永远不行)?和别人一起工作时什么让你抓狂?它问的时候你要告诉它;这张清单是为了不临场发挥。
  • 项目文件夹,代理在里面打开。没有别的了:这是项目的第一个文件。
  1. 粘贴这条提示词:

Before we write any code, we are going to set the working agreement between you and me. Write docs/WORKING_AGREEMENT.md with at least these rules, each with one line explaining what it prevents: (1) My time is sacred: do not ask me what you can resolve on your own, and when you do ask, ask in prose, with your recommendation. (2) Zero technical debt: whatever breaks is fixed at the root, with its regression test, in the same session. (3) Exact scope: no more and no less than what was asked; if you see something else, log it as a pending item, do not do it. (4) Documentation is updated in the same commit as the change. (5) Never report as done something you have not verified; always separate what you verified from what you believe. (6) Speak my language. Add any rules you think this project needs, marked as proposals. If you need to know something about how I work in order to write this, ask me.

你在要求它写六条规则,每条附上它避免的故障;项目专属的规则标为提案,好让你接受或拒绝;以及让它问你关于你的事。

  1. 回答它的问题。读它的提案:接受对你有用的,拒绝没用的,总是附一行理由。

发送提示词之前(收尾提示词):你对代理每一条提案的裁决,写下来:你接受哪些 [X, Y]、拒绝哪些 [Z]、为什么,每条一行。这些要填进提示词的空里,而填空的人是你。

  1. 用这个收尾:

I accept rules 1 to 6 and your proposals [X, Y]; I reject [Z] because [reason]. Finalize the agreement with today's date and a version number. Then add to the project file the instruction to read this agreement at the start of every session.

如果还没有项目文件(你在第 2 课做它),它会创建一个只带这条指令的最小文件;没问题。

完整性检验。 六条规则都在吗,每条都附有它避免的故障?有没有哪条写成了愿望("尽量……")?项目文件是否命令在每次会话开始时读它?它是否至少问了你一个关于你怎么工作的问题?

交付物。 带日期和版本的 docs/WORKING_AGREEMENT.md,挂接到项目文件。

常见错误。 因为听起来都不错而接受二十条规则:没人遵守二十条;六到八条才会被遵守。留下一条说代理可以修改契约的规则:不行。还有最安静的一个:从不检验它是否在读。第 2 课你会看到怎么检验。

在引导项目中。 代理通常会为 Turnera 提出的一条专属规则,值得接受:"绝不从非生产环境向患者发送真实消息;在测试环境里,消息进入一个假收件箱"。这是第 14 级台阶提前露了头。

第 2 课 — 项目定义、语义树与必读

你需要理解什么。 项目文件(PROJECT.md,或 CLAUDE.md,或你的工具用的名字)是代理每次会话开始时读的地图。它必须短:系统是什么、给谁用、它不是什么、模块、带日期的已做决策,以及读契约的命令。每天发生的事不放在那里:放到活状态(docs/STATUS.md)和待办项登记册里。我的一个系统让这个文件涨到了九千多行;到了那个地步,没人读它,模型也不读 Ref. 41。

但地图还有第二块,而且是干活最多的那块:语义树。它是被分解成一棵树的系统:第一层是领域,每个领域下面是它的参与者、它的流程(你在需求摄入里说出的用例,带参与者、触发条件、步骤、结果和异常)、它的实体及其属性和状态、它的界面和它的外部集成。这就是需求工程里一直有的领域模型,摆成树的形式好让模型读。每个节点下面将挂着触及它的待办项和决策。它的价值是双重的:给代理它要触碰的节点的确切上下文,并给你一种不读代码就能验证代理理解的系统就是业务所要求的系统的方式。如果树里有一个业务从没说过的状态,那是模型编的,而你会在它在上面构建之前看到它。

"它不是什么"一节仍然是最重要的、也是所有人都留空的一节。没写下来的东西,就是模型遇到空洞时会编出来的东西。

任务。

发送提示词之前,用你自己的手:

  • docs/INITIAL_SURVEY.md,用你在第 0 课学到的东西(代理那些问题的答案)和业务调研补全:谁使用系统、它解决什么问题、一节"系统不该做什么"哪怕只有两项、所有者用他自己的话说出的用例、带状态的实体、界面草图哪怕只是纸上的照片、外部集成,以及已经做出的决策,附日期。如果你用了某种需求摄入方法(访谈、用例、用户故事、原型),说出是哪一种,并把结果贴进去。
  • docs/WORKING_AGREEMENT.md,已定稿(第 1 课)。
  • 如果有旧系统,对它的代码或文档的读访问权,记在调研里。
  1. 粘贴:

We are going to define the project in writing. Read the business survey in docs/INITIAL_SURVEY.md and the working agreement in docs/WORKING_AGREEMENT.md. Evaluate whether the survey is sufficient to describe the system, its users, its domain and its boundaries; if it is not, ask me for what is missing before writing anything. Then write two things. First, PROJECT.md, three hundred lines at most, with: what the system is and for whom; what it is NOT; the main modules as a list; the decisions already made, each with its date; and the instruction to read this file and the semantic tree at the start of every session. Second, docs/SEMANTIC_TREE.md: the system as a tree, with the domains at the top level and, under each one, its actors, its processes (the use cases, each with actor, trigger, steps, outcome and exceptions), its entities with their attributes, states and transitions, its screens, and its external integrations. Mark every node you inferred rather than read in the survey, so that I can confirm or correct it. Separately, create docs/STATUS.md for the living state (what is being worked on, what is open): that file changes every day; the other two change only when the system changes. Reply in my language.

你在要求它在起草之前先问,一个带"它不是什么"一节的短项目文件,结构完整、推断节点已标记的语义树,以及放在另一个文件里的活状态。

  1. 通读整个 PROJECT.md。它必须能在五分钟内读完。修正不属实或不是你的东西。
  2. 像客户一样走一遍语义树。看每个标为推断的节点,确认或修正。找缺失的参与者、业务不用的实体状态、你没要求的界面。

发送提示词之前(收尾提示词):PROJECT.md 由你通读过,树由你一个节点一个节点走过、每个推断节点都有一个裁决,以及写好的修正清单,要填进提示词的 [列表] 空里。如果第一次这份清单是空的,该怀疑的是你的阅读。

  1. 用这个收尾:

Apply these corrections: [list]. Finalize PROJECT.md and docs/SEMANTIC_TREE.md with today's date. Add to the working agreement two rules: the change history never goes into PROJECT.md (it goes to docs/STATUS.md or to the pending-items register); and every pending item must hang from a node of the semantic tree, and the tree is updated in the same commit whenever a block adds or changes a node.

  1. 阅读测试。 关闭代理的会话,打开一个新的。什么都不多说,问它:"这个系统不该做什么?日程表界面挂在树的哪个节点下?"。如果它用 PROJECT.md 和树里说的内容回答你,挂接就起作用了。如果它回答了别的什么,它就没在读地图:检查你的工具是怎么加载项目文件的。

完整性检验。 PROJECT.md 能五分钟读完吗?它说了系统不做什么吗?树里有你知道的所有参与者吗,每个实体的状态是否恰好是业务用的那些,不多不少?你是否确认或修正了所有推断节点?每条决策是否有日期和决策者?代理在起草前问了什么吗?活状态在另一个文件里吗?阅读测试通过了吗?

交付物。 PROJECT.md(最多三百行)、节点已确认的 docs/SEMANTIC_TREE.md、 docs/STATUS.md,以及通过的阅读测试。

常见错误。 把你已有的全部规格粘进 PROJECT.md:它是纲领,不是规格。把"它不是什么"留空。不走一遍树就接受它:模型推断状态和界面的自然程度令人害怕,而每个被编出来的节点都是没人要求它就会去构建的功能。还有不做阅读测试:那是唯一能知道第 1 级和第 2 级台阶在起作用而不只是存在的方式。

在引导项目中。 Turnera 的"它不是什么"里至少应该有:它不是一个病历系统(不保存医疗数据),它不是一个支付网关(它用某个供应商的支付链接,不处理银行卡),它不发营销消息(只发确认和提醒)。树有一个"日程"领域,参与者是前台和专业人员,流程是"登记预约"、"取消预约"和"查找患者",实体是预约(状态为待确认、已确认、已取消、缺席)和患者,界面是"当天日程"和"患者档案";一个"消息"领域,流程是"发送提醒",实体是消息,以及与 WhatsApp 供应商的集成;还有一个"支付" 领域,有"索要定金"和与支付供应商的集成。如果代理给预约加了一个"已改期"状态,问它从哪儿来的:负责人没说过。也许需要,也许不需要;由你决定,不是它。

第 3 课 — 在架构之前先做容量估算

你需要理解什么。 有些决策,做错了要付出几周的代价:用什么数据库,对数据库的调用是同步还是异步,多少人能同时使用系统。而它们全都依赖于只有业务才有的数字:每天多少预约、每个预约多少条消息、保存多少年。模型无法推断这些数字;它能推断的是从中派生出来的东西(每年多少行、五年后多大)。所以提示词要求它区分原始数据和派生数据,并且索要缺失的原始数据而不是估计它们。当模型估计一个原始数据时,它的表情和它计算时一样笃定;你没办法从语气上区分。没有及时做这件事让我付出了一整个重构阶段的代价 Ref. 15。

此外还有一类决策总是值得做:非对称决策,多余时便宜、缺少时昂贵得要命。典型的例子是从第一天起就把与数据库的接口做成异步的,哪怕第一个数据库只是一个文件:如果你从不换数据库,多了一个 await;如果你换了,你省下了重写一切。

任务。

发送提示词之前,用你自己的手,和所有者一起:

  • docs/VOLUME_SURVEY.md:和所有者坐下来(如果项目是你的,就和你自己),写下主要实体(语义树里的那些)以及有多少、每月增加多少、每个实体多少事件、高峰时多少人操作以及高峰在什么时候、数据保存多久、有没有特殊负载、他们期望什么增长。对每个数字,写上它来自哪里:"负责人说的"、 "在九月的本子里数的"、"假设"。刻意留几个"假设":你会看到代理怎么处理它们。
  • PROJECT.md 和 docs/SEMANTIC_TREE.md,已定稿(第 2 课)。
  • 如果有带数据库的旧系统,一个只读访问权,让代理取到真实数字,连接串放在仓库之外。
  1. 粘贴:

Before proposing any architecture we are going to size the system. Read PROJECT.md and the volume survey in docs/VOLUME_SURVEY.md. Distinguish primary data (facts only the business knows) from derived data (what can be computed from the primary data). Evaluate whether the primary data is complete enough to compute what follows; if any primary figure is missing, or is marked as an assumption, ask me for all the missing figures before going on. Never estimate a primary figure. If a previous system can be queried, use its real numbers and state where each came from. With the primary data complete, compute, showing the arithmetic: rows per year for the three largest tables, peak concurrency, data size at five years, and what breaks first if everything is multiplied by ten. Then present three options for persistence and concurrency, each with the cost of migrating to the others later. Do not choose: I choose. Do not write code. Reply in my language.

你在要求它:区分原始数据和派生数据;向你索要缺失的或标为"假设"的原始数据,而且绝不估计它们;算式可见地计算派生数据;"乘以十"的场景;三个选项附上互相切换的成本;以及不要做选择。

  1. 如果它向你要"假设"的那些,很好:练习就是这个。弄到数字(问所有者,在本子里数),给它。如果它没向你要而是照样算了,停下来问它拿那些假设怎么办了:这是台阶的一次失败,最好现在就看见。
  2. 读三个选项。手工重算一个算式(比如最大那张表每年的行数):结果必须一样。做出选择。

发送提示词之前(收尾提示词):假设已经变成带来源的数据,有一笔由你亲手重算、而且和代理算的一致的账,以及选定的方案,附一行理由。

  1. 用这个收尾:

I choose option [B]. Record in docs/SIZING.md: the primary data with its source, the derived data with its arithmetic, the three options, the chosen one and why, and the decisions we are taking "just in case" even though they are not needed today (for example: the persistence interface is asynchronous from line one, even if the first engine is synchronous). Include the questions you asked and my answers.

完整性检验。 有没有哪个原始数据的来源是"假设"或没有来源?每个派生数据是否展示了算式,你重算时结果一样吗?是否有三个选项以及一个附理由的选中项?它说了"乘以十"会怎样吗?非对称决策写下来了吗?它问你了吗?

交付物。 docs/SIZING.md。

常见错误。 用你自己编的数字补全调研好让代理不提问(和第 0 课是同一个错误,而在这里更糟,因为这些数字决定架构)。以防万一选"最强"的选项:迁移成本放在表里恰恰是为了让你选能扛住"乘以十"的最小那个。还有不把非对称决策写下来:没写下来,第 4 课代理就不会遵守它们。

在引导项目中。 原始数据:40 预约/天 × 250 天 = 10.000 预约/年;每个预约 3 条消息 = 30.000 消息/年;保存 5 年;高峰是两名前台同时操作。"乘以十"就是是否卖给其他诊所的问题:每年 100.000 个预约和 300.000 条消息。三个典型选项:一个 SQLite 文件(够一家诊所用好几年,多人写入时扛不住乘以十)、从一开始就用 PostgreSQL(什么都扛得住,基础设施成本略高)、或者现在用 SQLite 加上异步接口并预设向 PostgreSQL 的迁移(非对称的那个)。课程里我们选第三个:它教的东西最多。

第 4 课 — 带闸门的骨架

你需要理解什么。 在系统做任何有用的事之前,它必须能空着启动、测试和部署。看起来像浪费时间("部署什么?什么都没有"),其实恰恰相反:通往生产的路是产品的一部分,而知道它能用的唯一方式是在还没有什么可失去的时候走一遍。在我的一个系统里,部署在启动一个月之后才到来,接连付出了五个待办项的代价 Ref. 32;如果骨架在启动阶段就部署了,那五次打击会在空载时发生。

此外,这节课是做项目全部基础设施和 CI/CD 链的地方。到第 15 课之前你不会再碰它,而在这期间你会在它上面构建全部功能。所以它占了第 1 阶段的大部分。

闸门是从一个阶段进入另一个阶段的条件("测试套件是绿的"、"到测试环境的部署成功了")。现在就把它们写下来,哪怕看起来显而易见,是之后能不争论就说"这个过不去"的依据。

任务。

发送提示词之前,用你自己的手:

  • docs/ENVIRONMENTS_SURVEY.md:系统要在哪里运行(你已有的一台服务器、一个云服务),会不会有一个测试环境和一个生产环境,谁有权访问什么,如果已经有东西今天是怎么部署的,以及项目的类型(全新、纠错还是演进)。如果你什么都没有,就这么说:代理会给你提出最简单的选项。提示词把它叫作 "the environments survey",没有给路径:告诉它这个文件叫什么。
  • 要部署的地方的凭证,哪怕是一个免费服务:已创建,由你亲自登进控制台试过,并保存在仓库之外;你告诉代理它们在哪里,不要把它们贴进聊天。没有它,骨架就留在你的机器上,这节课无法完成。
  • PROJECT.md 和 docs/SIZING.md,已定稿(第 2 课和第 3 课)。
  1. 粘贴:

We are going to build the skeleton of the system, with no functionality at all. Read PROJECT.md, docs/SIZING.md and the environments survey. Evaluate whether you have what is needed for the skeleton to run in the test environment (access, URL, deployment method); if anything is missing, ask me before starting. Then build: the folder structure; the server and the UI starting up; the test structure with one smoke test that passes; continuous integration running that test suite on every push; and the deployment to the test environment. Declare the gates of the workflow (what must be green to move from one stage to the next) in docs/GATES.md. When done, take a screenshot of the empty UI running in the test environment and show it to me. Do not add any functionality "while you are at it". Reply in my language.

你在要求它在开始之前核实访问权限,一个骨架到底是什么的确切清单,写下来的闸门,一张来自测试环境的真实截图,以及禁止添加功能。

  1. 这个任务很长(代理一两个小时的工作),而且会有反复:缺凭证、某个服务要求确认邮箱,等等。这很正常。不正常的是它没给你测试环境的截图就说"搞定"。
  2. 当它给你看截图时,你自己在浏览器里打开那个 URL。必须看到同样的东西。这是对系统的第一次人工查看,是一个里程碑。

发送提示词之前(收尾提示词):你亲自访问过测试环境的 URL,由你打开过那条绿色的持续集成运行链接,以及读过 docs/GATES.md,每道闸门都有一个可验证的条件。

  1. 用这个收尾:

Skeleton approved. Record in docs/STATUS.md the test environment URL, the command to run the tests and the command to deploy, and mark this step as done. Then run the deployment a second time and show me that the second run changed nothing.

完整性检验。 你是否亲自打开了测试环境的 URL 并看到了空白界面?有没有一个指向绿色持续集成运行的链接?部署是否运行了两次而第二次没弄坏任何东西?每个闸门是否都有一个可验证的条件?有没有你没要求的功能?

交付物。 带骨架的仓库、绿色的持续集成、能用的测试环境 URL、docs/GATES.md,以及带三个命令的 docs/STATUS.md。

常见错误。 接受"本地能跑,部署以后再看":不行,部署就是骨架。接受一张来自代理机器而不是测试环境的截图。还有让它"顺便"给你加一个登录页、一个菜单、一个 logo:每一样没有待办项就进来的东西都是跑掉的范围。

在引导项目中。 Turnera 的骨架是一个写着"Turnera — 版本 0.0.1"的网页,运行在一个测试环境的公开 URL 上;一个在健康检查路由上回答"ok"的服务器;一个调用那个路由的冒烟测试;每次 push 都运行那个测试的持续集成;以及到测试环境的自动部署。闸门:"测试套件绿色才能合并"、"测试环境绿色才能提升到生产"、"回滚经过测试才能第一次部署到生产"。仅此而已。而看到那个空白页面出现在一个真实 URL 上,竟然出奇地令人满足。


循环阶段:第 5 到 12 课

这八节课是你在整个项目期间每次变更都要用的。它们在第 2 和第 3 阶段、在前两个功能块上学,并一直练到最后。它们按每个待办项的实际做法排序:把请求转换成有规格的待办项来打开一个功能块,取一个,决定缺的东西,测量该测量的,构建,先证红并做端到端测试,通过质量闸门,写人工测试用例,更新另一端,报告时把已验证的和相信的分开。功能块收尾时,自己执行那些人工测试用例。这个反射就是方法。

要练习它们你需要真实的功能。在引导项目里,第一个功能块是基本日程表:看当天的预约,登记一个预约,取消它。第二个是患者与搜索。暂时不做消息和支付;那在锁之后(第 14 课)。

第 5 课 — 计划就是待办项,而详细规格住在每一个待办项里

你需要理解什么。 没有一边的"计划"和另一边的"任务清单":待办项列表就是计划。但还有两个想法,它们把这个方法和任何一张任务清单区分开。

第一个:详细规格住在每个待办项里。在经典方法里有一份长长的需求规格文档,在写完之前就过时了。这里它被分解:每个待办项带着它挂在哪个语义树节点下、引发它的用户故事或用例(用客户的原话)、详细描述、适用的业务规则及其决策和日期、进出的数据、受影响的界面、边界情况、"给定、当、那么"形式的验收标准,以及如何验证。读一个待办项的规格是你离读代码最近的距离,而那恰恰是正确的距离:正是在那里,你在它构建之前发现代理理解成了别的东西。

第二个:已关闭的待办项就是文档。一个待办项关闭时,它保留着规格、引发它的决策、关闭它所凭的证据、提交和日期。已关闭的待办项队列就是系统的功能历史,变更日志和手册的新章节都从那里出来。还有给代理的黄金规则:列表之外出现的东西,记为新待办项,附上规格,不去做。

任务。

发送提示词之前,用你自己的手:

  • 客户对第一个功能块的请求,写在 docs/INITIAL_SURVEY.md 里以功能块命名的标题下,或者写在单独一个文件里(比如 docs/blocks/BLOCK_01_REQUEST.md;提示词说的是 "the client's request for this block",没有给路径,所以告诉它在哪儿),照客户的原话,带着它的模糊之处。对 Turnera:"我想看当天的预约,登记一个新的,还有取消"。如果你在需求摄入时写了用例,也粘进去:它们是功能块的自然单位。如果所有者给了你一个优先级,也写上。
  • docs/SEMANTIC_TREE.md、PROJECT.md 和 docs/STATUS.md,保持最新(第 2 课)。
  1. 粘贴:

We are going to turn everything that must be done in this block into numbered pending items, each one carrying its full specification. Read PROJECT.md, docs/SEMANTIC_TREE.md, the client's request for this block, and whatever is in docs/STATUS.md. If the request is ambiguous on any point such that you cannot write a verifiable pending item for it, ask me; do not resolve it by assuming. Then create or extend docs/PENDING.md with one item per thing to do, each with: number (PND-001 onwards); one-line title; the node of the semantic tree it hangs from; the user story or use case that originates it, in the client's words; the detailed description; the business rules that apply, each with its decision and date, or marked as "rule missing, asked"; the data in and out; the screens affected; the edge cases and exceptions; the acceptance criteria in the form "given, when, then"; how it will be verified as done; and status. If a node the item needs does not exist in the tree, propose it and mark it. Order the items by the priority I gave you; if I gave none, propose an order and tell me why. Reply in my language.

你在要求它把请求转换成可验证的项,在请求不足以写出可验证的项时提问,每一项逐字段的完整规格,它挂在树的哪个节点下,附决策或标为缺失的业务规则,之后会成为测试脚本的验收标准,以及附理由的顺序。

  1. 回答问题(对 Turnera 它会问,比如,"当天"是什么:今天,还是任选的一天?取消是删除还是标记?)。这些问题都是业务决策;第 6 课你会看到它们写在哪里。
  2. 像客户一样读每个待办项的规格。这是这节课最重要的任务,也是最让人想跳过的。它描述的是你要求的东西吗,用的是你的话吗?验收标准是你用来说"行了"的那些吗?有没有你知道而它没写的边界情况?它挂在正确的节点下吗?在构建任何东西之前修正。

发送提示词之前(例行提示词):docs/PENDING.md 里这个功能块已经规格化,而且由你一个待办项一个待办项地读过、修改已经做完(没有哪个待办项的规格只有一行),以及 docs/WORKING_AGREEMENT.md,例行要加进去的地方。

  1. 把每次会话的例行加进协作契约。粘贴:

Add this routine to docs/WORKING_AGREEMENT.md: "Before touching any code, open docs/PENDING.md, tell me which item you are taking and why, and re-read its specification. Every commit cites its pending item. A pending item is closed with written evidence (the test counts red and green, the screenshot, the measured number), never with 'done'. When you close it, keep it in the register with its full specification and add: the evidence, the commit, the date, and any decision that was taken along the way; that record is the documentation of what was built. If along the way you see something that is not on the list, add it as a new pending item with its specification; do not do it."

  1. 现在可以开始构建了。让它取第一个待办项。从这里起,每次会话都以"你取哪个待办项、为什么"开始,每次关闭都留下记录好的待办项。

完整性检验。 随机读一个待办项的规格:它有所有字段吗,描述的是你要求的东西吗?每个待办项是否挂在一个你认得的树节点下?每一个都说了如何验证吗?有没有标为"缺失,已询问"却没问过你的规则?有没有没有证据也没有提交就"关闭"的待办项?已关闭的是否保留了规格和关闭文档?随机取三次提交:它们引用了待办项吗?代码里出现了没有待办项的东西吗?

交付物。 带第一个功能块规格的 docs/PENDING.md、契约里的例行,以及进行中的第一个待办项。

常见错误。 接受一行的规格:代理写它们是为了交差,构建时它会编出缺的东西。不读规格:那是你能在付出代价之前拦住一个误解的唯一时刻。让代理因为"我已经看它跑了"就不带证据地关闭待办项。让它不记录就关闭:六个月后没人知道系统为什么这样做。还有最难纠正的:让它做列表之外的 "一件小事"。你第一次放行,范围精确就丢了。

在引导项目中。 Turnera 的第一个功能块大约是六个待办项,挂在树的"日程"领域下:PND-001 看某一天的预约;PND-002 登记一个带患者、专业人员、日期和时间的预约;PND-003 阻止同一专业人员同一时间的两个预约;PND-004 取消一个预约(标记,不删除:第 6 课的决策);PND-005 按姓名或电话查找患者;PND-006 带最少数据的患者建档。比如 PND-003 的规格写着:节点 日程 → 流程"登记预约" → 实体 预约;故事:"同一个专业人员同一时间不能有两个预约,用本子的时候我碰到过两次"(负责人的原话);规则:"只有专业人员启用了才允许双重预约"(决策待定,已询问);数据:专业人员、日期、开始时间、时长;界面:登记表单;边界情况:部分重叠的预约、时长不同的预约;验收标准: "给定 A 医生 10:00 有一个预约,当我试图再登记一个 A 医生 10:00 的预约,那么界面提示且数据库不保存";验证:表单的端到端测试加数据库查询。这才是一份规格。

第 6 课 — 决策写在代码旁边

你需要理解什么。 模型遇到业务规则里的空洞时,会把它填上。它不会停下来问;它用它觉得最可能的东西补全,自然得就像它写其余东西一样。所以这节课不是"写决策",而是"要求它在缺一条规则时停下来问"。而当你做了决定,决策用你的原话、你的名字和日期写下来,写在提交里、待办项规格里和一个记录里。我从第一个系统起就这么做 Ref. 29;六周之后,当有人问"为什么取消不删除?",答案是写好的,而且是你的。

任务。

发送提示词之前,用你自己的手:

  • docs/WORKING_AGREEMENT.md(第 1 课),例行要加进去的地方。
  • docs/DECISIONS.md,创建好,哪怕是空的,表头里写好格式:日期、谁决定的、原话、需要这个决定的那个待办项。
  • 和领域所有者(如果不是你)约好的渠道和回答时间:这条例行只有在有人用几小时、而不是几周来做决定时才起作用。
  1. 把决策例行加进契约。粘贴:

Add this routine to docs/WORKING_AGREEMENT.md: "Whenever implementing something requires a business rule that is not written down, or you must choose between two approaches that change what the user sees, stop and ask me, in prose, with your recommendation and the alternative. Do not implement the one you prefer. When I decide, write the decision with my name and the date in the commit message, in the pending item's specification and in docs/DECISIONS.md, with the exact words I used. If I do not answer and you must move on, implement the most reversible option and mark it 'decision by omission, pending confirmation', so that I can review it."

你在要求它遇到未写明的规则时停下,带建议和备选来问(好让你能在十秒内决定),把你的原话记录在三个地方,以及当你不回答时选最可逆的并标记出来。

  1. 继续构建功能块的待办项。每次它问你一条业务规则,就做决定。

发送提示词之前(收尾提示词):代理的问题,连同它的建议和它的备选,已经读过;还有你的决定,用你希望永远留下来的原话写成一句话,如果领域所有者不是你就和他商量过,附日期。不在聊天里临场发挥。

  1. 粘贴:

Rule: [exact wording]. Decided by me, [date]. Write it down and continue.

  1. 刻意做一次反向测试:在一次会话里留一个问题不回答,看它怎么做。它必须选最可逆的并标为"缺省"。如果它选了自己觉得对的而没有标记,这是台阶的一次失败:在契约里修正。

完整性检验。 打开三次涉及业务规则的提交:它们引用了谁决定、何时吗?规则也在待办项的规格里吗?有没有未确认的"缺省"决策?系统里有没有你不认为是你的规则?原话是逐字的,还是被"改进"了?

交付物。 增长中的 docs/DECISIONS.md,至少有第一个功能块的五条决策,以及引用它们的提交和待办项。

常见错误。 在聊天里回答问题却不要求把它们写下来:到第三次会话就没人记得了。让代理"改进"你的话:一条被改写的规则是另一条规则。还有最严重的:在生产里发现一条没人决定过的规则。发生时(会发生的),它不是一个 bug:它是第 6 级台阶的失败,要在契约里修,不只是在代码里。

在引导项目中。 Turnera 第一个功能块的典型决策:"取消把预约标为已取消并保存谁和何时;什么都不删除";"一个专业人员同一时间不能有两个预约,但如果专业人员启用了双重预约,一个预约里可以有两个患者";"患者的电话是必填的;邮件不是"。每条都附日期和负责人的名字,每条都贴在需要它的待办项的规格里。

第 7 课 — 构建之前先测量,用一把也被验证的量具

你需要理解什么。 模型产出带数字的断言("有大约 120 个那种查询"),而它们在测量过和估计过时听起来一模一样。你没法从语气上区分。唯一的方式是要求仪器:"你用什么测的?给我看命令"。这级台阶的规则是:任何决定某事的数字都不来自印象:它来自一把住在仓库里的量具,它有一个对已知案例验证它的测试,而且你能运行它。

还有一件事,是我吃一堑长一智学到的,也是我迟迟没有把这条我视为显然的规则形式化的原因:量具也会撒谎。在我的一个系统里,一把量具给出零,因为它看不到代码的一部分,另一把说 27,而实际是 44 Ref. 35、Ref. 36。所以要双向审计:从代码到测量(该计数的都计了吗?)和从测量到代码(计数的都真实存在吗?)。

在小项目里,"测量"是朴素的:数一数一个界面加载时做多少查询,月报要多久,昨天发了多少条消息。不需要棘轮,也不需要每条路径的预算。需要的是数字背后有一条命令。

任务。

发送提示词之前,用你自己的手:

  • 假设,写成一句话。在 Turnera 里,一个好的假设是:"当天日程对它显示的每个预约都做一次数据库查询"(经典的 N+1 循环)。如果它不成立,更好:你会看到一个错误的假设是怎么被验证的。它和要测量的量一起填进提示词的空里。
  • 一个已知案例,用来测试量具,你事先就知道答案("零个预约时,它必须恰好数出那些基础查询")。
  • 对所测之物的访问权(如果测的是真实数据,数据库的只读权限;日志),以及仓库里已经存在的量具,把它们的名字说出来。
  1. 粘贴:

Before building anything, we are going to measure. The hypothesis is: [the day view runs one database query per appointment shown]. Write a meter (a script that lives in the repository) that measures [the number of queries the day view runs for a day with N appointments], and a test for the meter that checks it against a known case where the answer is known in advance. If measuring requires access or data you lack, ask me; do not approximate silently. If some part can only be approximated, mark it as approximate and tell me the margin. Run the meter, show me the number together with the command that produces it, and audit it in both directions: from the code to the measurement (is everything that should be counted, counted?) and from the measurement to the code (does everything counted actually exist?). Only then propose what to build. Reply in my language.

你在要求它把量具放在仓库里,量具的测试,缺什么就索要而不是近似,标出近似的部分,数字连同命令,双向审计,以及顺序:先测量,再提议。

  1. 你自己运行它给你的命令。结果必须是同一个数字。你不需要理解脚本;但需要执行它。
  2. 如果假设成立且代理提议修复,向它要一个预算。

发送提示词之前(预算提示词):测出来的那个数字,由你用它给的命令亲自跑过(结果必须一样),以及你愿意给的余量,由你决定:比测出来的多一点,不是"以防万一"翻一倍。

  1. 粘贴:

Set the budget for [queries] on this path at the measured value plus a small margin, and protect it with a test that fails if it is exceeded. Tell me the number and the margin.

这样,如果三个月后有人把循环放回去,测试套件会检测到。

发送提示词之前(规则五提示词):docs/WORKING_AGREEMENT.md,以及对计划可能断言的每个量都已经有一把量具;如果某个数字没有量具,先发这节课的初始提示词。

  1. 把第五条规则加进契约,用于计划带着数字的情况:

Add this routine to docs/WORKING_AGREEMENT.md: "Before executing a plan that asserts numbers, verify every number with the meter. If any does not match, stop and tell me; do not execute on a wrong number."

完整性检验。 你运行了量具并得到同样的结果吗?它有测试吗,对什么已知案例?报告里有没有背后没有量具的数字?近似的部分标出来了吗?有带测试的预算吗?

交付物。 仓库里一把带测试的量具,docs/STATUS.md 或待办项里一个带命令和日期的数字,以及(如果适用)一个由测试保护的预算。

常见错误。 不运行命令就接受数字:这节课一半的价值在于你亲自运行它。一把给出零的量具:几乎从来不是零;几乎总是它没看到什么。还有让代理"边测边修":顺序就是方法。

在引导项目中。 Turnera 的量具是一个脚本,在测试数据库里加载有 12、50 和 200 个预约的一天,打开那天的日程并计数查询,写下三个数字。量具的测试验证 0 个预约时它恰好数到基础查询数(比方说,2)。如果有循环,数字是 12、52 和 202;修好后变成 3、3 和 3,预算定在 4,附一个测试:对任何数量的预约,如果日程做了超过 4 次查询就失败。

第 8 课 — 作为锁的测试,先证红,以及端到端测试

你需要理解什么。 这是课程的核心一课。一个你从没见它失败过的测试,你不知道它是否证明了什么。模型在同一次会话里、用同一种理解写代码和测试:如果它误解了请求,测试证明的是它理解的东西,并且通过。知道测试在看重要之处的唯一廉价方式是看它对旧代码变红:如果变更之前的代码也能通过它,测试就区分不了前后,那它就没用。所以要求的不是"写测试",而是"给我看红色,然后绿色,然后回归"。最能说明这一点的案例是对不上的计数器 Ref. 26。

还有第二个想法,和第一个一样重要:端到端测试。一个从界面开始验证(点击、输入、读取)并检查后端效果(行留下了、状态变了)的测试,才是替代你读代码的那个。待办项的验收标准("给定、当、那么")就是它的脚本。而且它们要跑三次,在三个尺度上:每个待办项,跑它触及的流程;每个功能块结束时,跑所有已有的流程;每次部署到生产之前,用确切的版本跑所有流程。如果你只做第三次,到最后你拿到的是一个几周里没人端到端测过的系统。

任务。

发送提示词之前,用你自己的手:

  • docs/WORKING_AGREEMENT.md,例行要加进去的地方。
  • 要关闭的那个待办项,在 docs/PENDING.md 里带着它"给定、当、那么"的验收标准和它的"如何验证":那是测试的脚本。如果它没有这些,在要测试之前回到第 5 课。
  • docs/STATUS.md 里的测试套件和运行它的命令(第 4 课),以及测试环境在运行、有足够的数据覆盖被触及的流程;如果缺数据,由你加载,或者让它作为测试的一部分去创建,但不许模拟。
  1. 把证据例行加进契约。粘贴:

Add this routine to docs/WORKING_AGREEMENT.md: "To close a pending item I need evidence, not your word. (1) Write the test that proves what the item asks for, following its acceptance criteria. (2) Demonstrate that this test fails against the previous code: you may temporarily revert the change, or run the test against the old version, but show me the red output as a receipt, not a summary: the exact command, the identifier of the version it ran against, the count of failing tests and the exit code, pasted as they came out, so that I can rerun it myself with one command. (3) Show me the green output with the change. (4) Run the regression suite for the neighbouring items and show me it is still green. (5) Run the end-to-end test of the flow this item touches, from the screen to the backend, and show me the backend effect. (6) Do not replace with mocks the very part you are trying to test. If testing end to end requires something you lack (data, access, an environment), ask me for it; do not simulate what you do not have. Finally, the commit message says 'proved red against [what]' and the test counts."

你在要求它写由验收标准引导的测试,作为凭据展示出来的红色(命令、版本、数字和退出码,好让你能自己重新跑一遍),绿色,相邻项的回归,带后端效果的流程端到端测试,不模拟恰好要测的那部分,以及缺什么就索要而不是模拟。

  1. 取重复预约的待办项(Turnera 里的 PND-003,或你项目里的等价物:一条阻止某事的规则)。让它用这个例行关闭它。读报告:必须有带数字的红色输出("1 个中 1 个对没有这条规则的版本失败"),然后是绿色,还有端到端测试:一个程序控制的浏览器打开界面,试图登记重复预约,读到提示,并在数据库里核实它没有被保存。
  2. 如果它告诉你无法做端到端测试,因为界面不能程序化操作,停下来:那是 X2 在失败,必须现在就修(给界面元素加标识符),然后再继续。

发送提示词之前:分清它缺的是你手上有的输入(数据、一个访问权、一个环境:给它,别发这条提示词),还是系统在那里真的不可测试(这条提示词)。

  1. 粘贴:

Explain why it cannot be tested, what would have to be added so that it can, and open a pending item for that. The change is not closed until it can be tested.

  1. 功能块结束时,当六个待办项都关闭了,跑完整的测试套件。

发送提示词之前(测试套件提示词):六个待办项都用例行关闭了,要提升的那个确切版本已经标识并部署在测试环境上,以及你自己那份已有流程的清单(从语义树的用例里来),用来对照代理说它覆盖了什么。

  1. 粘贴:

Run the complete end-to-end suite of every flow that exists so far, in the test environment, with the exact version that will be promoted. Show me the counts, and for each flow the backend check that proves it. If any flow has no end-to-end test, say so and open a pending item for it; do not pass it as covered.

  1. 然后,你自己使用系统。打开测试环境,做前台会做的事:登记一个预约,试一下重复预约,取消,查找。让你烦的、你不懂的、放错地方的:记为待办项。这一会儿功夫找到的是任何测试都找不到的东西。

完整性检验。 报告是否展示了带数字的红色?回归是否在同一次会话里带数字运行了?端到端测试是否也验证了后端?变更所在之处是否恰好有被模拟的部分?提交写了"先证红"吗?功能块结束时:所有流程都在端到端测试套件里吗?你亲自用了系统并记下了你发现的东西吗?

交付物。 至少三个用完整例行(红、绿、回归、端到端)关闭的待办项,功能块的完整端到端测试套件绿色,以及从你的人工测试里产生的待办项。

常见错误。 接受没有数字也没有红色的"测试通过"。接受用模拟替换端到端测试来让测试套件"提速"。放过一个"无法测试"而不开待办项。还有不亲自使用就关闭功能块:对界面的人工查看不能委托。

在引导项目中。 重复预约的红色是这样的:代理临时去掉规则,运行测试,输出说测试失败了,因为第二个预约被保存了。把规则放回去,运行,绿色。然后回归:PND-001 和 PND-002 的测试仍然绿色。然后端到端:一个程序控制的浏览器填写表单,按"保存",读到"该时段已被占用"的消息,一条数据库查询确认那个时间只有一个预约。功能块结束时,端到端测试套件走一遍已有的三个流程(看当天、登记、取消),而负责人登记了十个测试预约,发现选时间用的下拉菜单很不顺手。新待办项。当你第一次看到那份报告时,你会明白整个方法是为了什么。

2026 年 10 月 19 日的变化(D-037)。 新项目的第一个块没有先前代码:新测试失败只是因为模块不存在,这种红色什么也证明不了。从那天起,当上一版本没有这个模块时,红色要通过变异来证明:代理在刚写好的函数里故意植入一个缺陷,展示测试针对该缺陷变红(凭据附带 diff),再还原并展示绿色。在 Forge 中,只说"文件无法加载"的红色不再能关闭待办项。每个待办项一个变异体;对整个代码库的变异测试仍然是第 9 课的可选加固。

第 9 课 — 由机器验证的代码质量

你需要理解什么。 你不读代码,这没问题;但代码是存在的,而且总得有人维护它。如果没人读它,那就得让一台机器来看:一个 *linter*(像文字处理软件的拼写检查,只不过是给代码用的)、一个 *格式化工具*(所有代码只用一种风格)、文件和函数的大小限制、*复杂度*限制(一个函数有多少条路径)、重复代码和死代码的检测,以及带*棘轮*的测试*覆盖率*(今天的就是明天的底线)。全部放在持续集成里,违反就变红。我在我所有项目里都做过;一次审计给我退回了一个两千多行的组件,因为没有任何限制拦住它 Ref. 41。

发送提示词之前,用你自己的手:

  • 持续集成在跑(第 4 课),而且功能块里至少有一个待办项用第 8 课关闭了,这样才有真实的代码可测。
  • 你的两条限制,写成一行:对 Turnera 来说,每个文件三百行、每个函数四十行,而且覆盖率只能往上走。
  1. 把第 9 级台阶契约(在 docs/peldanos/ 里)的初始提示词贴给它,把你的限制填进空位。
  2. 读 docs/CODE_QUALITY.md。里面会有一张今天没通过的清单;决定哪些现在就修(每条一个待办项)、哪些带日期容忍,并写在同一个文件里。

发送提示词之前(例行):docs/CODE_QUALITY.md 你读过并写上了你的决定,以及 docs/WORKING_AGREEMENT.md。

  1. 把第 9 级台阶的例行加进契约,像其他例行一样用 "Add this routine to docs/WORKING_AGREEMENT.md:" 包起来。
  2. 让它在一个分支上故意塞进一个一百行的函数和一个没用到的变量:链条必须因为这两件事都变红。

完整性检验。 链条最后一次运行的质量是绿的吗?分支上的那次试验变红了吗?有没有对 linter 的例外,每一条都有写下来的理由吗?今天的覆盖率大于或等于昨天的吗?

交付物。 链条里的质量闸门、带限制和数值的 docs/CODE_QUALITY.md,以及写进契约的例行。

常见错误。 让代理"把这条规则关掉,因为它太严格了"而不经过你。为了让它通过而调高一条限制。还有把覆盖率和质量搞混:覆盖率数的是测试,而第 8 课的红说的是它们是否证明了什么。

可选的加固。 功能块收尾之后,第 9 级台阶的契约里有第三条提示词,*变异测试*那条:一个工具故意把代码弄坏,一次一个小改动,然后数一数这些错误里有多少被测试抓住了。这是覆盖率给不出的度量,也是回答"代理可以伪造红色"这条反对意见的度量。在 Turnera 里我们在第二个功能块收尾时做,只对改动过的行。

在引导项目中。 Turnera 的第一次运行在三件事上变红:预约表单有一个 70 行的函数(拆成两个:待办项)、日期格式有两份副本(只留一份,在服务器上:待办项),而覆盖率在 61%。棘轮定在 61,随每个待办项往上走。功能块收尾时,74%。

第 10 课 — 人工测试用例和功能一起写

你需要理解什么。 到这里为止我们看到的所有测试都是模型写的、模型跑的,而它们检验的是模型理解到的东西。还缺一个人用自己的眼睛和手、照着*脚本*做的那种测试:一个人工测试用例,带前置条件、步骤、预期结果,以及一张记录每次执行的表。脚本由代理和每个待办项一起写,根据验收标准;由你(或负责人)在每个功能块收尾时、第一次部署之前和每次部署之后执行;而失败的东西作为纠正型待办项、用你的原话回到循环。这是带脚本的人工查看,也是过程图那个回路的闭合:手工执行把需求重新注入。

发送提示词之前,用你自己的手:

  • docs/MANUAL_TESTS.md 已创建,哪怕是空的,在开头写好格式(从第 10 级台阶的契约里抄过来)。
  • 谁执行、什么时候执行的决定,写进契约:对课程来说,就是你在每个功能块收尾时,负责人在第一次部署之前。
  • 功能块里有一个用第 8 和第 9 课关闭的待办项。
  1. 把第 10 级台阶的例行(契约里的"每个待办项的例行")加进契约,像其他例行一样包起来。从现在起,每个触及界面的待办项都要带着它的用例关闭。
  2. 让它为功能块里已经关闭的待办项写用例。随便挑一个,你自己在测试环境里走一遍,什么都不要问它:如果你不得不开口问,那这个用例就不是写给人看的;让它重写。
  3. 功能块收尾时,你亲手执行该块的全部用例,并在每个用例里记下日期、你的名字、通过还是失败,以及备注。让你别扭又落不进任何用例的东西,记在文件末尾。

发送提示词之前(执行):你的那些执行逐个用例记在了文件里,零散观察记在末尾。

  1. 把第 10 级台阶契约的执行提示词贴给它。每一处失败都必须作为带着你原话的纠正型待办项回来。

完整性检验。 你能不开口问就走完一个用例吗?每个触及界面的已关闭待办项都有它的用例吗?每个用例都说了要在屏幕的另一边验证什么吗?执行在每个用例里都有日期和名字吗?每一处失败都有带着你原话的待办项吗?

交付物。 docs/MANUAL_TESTS.md,功能块的每条流程一个用例,第一次执行有记录,每处失败一个待办项。

常见错误。 让代理把用例标成已执行。用例是把自动测试用散文抄了一遍。还有当场修掉失败、不开待办项:你的原话和什么在重复出现的痕迹都丢了。

在引导项目中。 Turnera 第一个功能块的用例有六个:看当天的日程、登记一个预约、尝试一个重复预约、取消、搜索一个患者、新建一个患者。第一次执行里失败了两个:取消那个(日程不重新加载页面就不更新;负责人的观察:"我以为没取消成功")和搜索那个(有两个同姓的患者时,只显示一个)。两个纠正型待办项,带着她的原话。那个没有任何测试看见的、别扭的小时下拉菜单,作为一条零散观察出现。

第 11 课 — 生产者/消费者对称性与反模式记录

你需要理解什么。 当你改了系统的一端(服务器、数据库、一个机器人),另一端(界面、一个计数器、一份报告)必须在同一次提交中改变。否则它们会分叉,用户在一边看到一个数字,在另一边看到另一个 Ref. 26。避免这一点的原则说起来简单、坚持起来难:每张列表只有一个所有者。一个预约的可能状态、一份报告的列、某物等待的原因:由一个部分(生产者)定义,其余部分从那里读 Ref. 27。语义树是这些列表第一个被声明的地方。一旦有两份副本,其中一份就是过时的;只差知道是哪一份。

这节课的后半部分是反模式记录(GOTCHAS):你已经吃过一次亏的错误,记下它们的气味、根因、修复和如何提前检测 Ref. 2。重要的不是记下来:是加上能检测它们的自动检查,因为一个记下来却没自动化的反模式,会在换了会话时重演。

任务。

发送提示词之前,用你自己的手:

  • 至少已经构建了一个功能块:各部分之间没有契约,就没有地图可做。
  • docs/SEMANTIC_TREE.md 保持最新:那些列表(状态、类型、原因)是在那里声明的,而地图必须和树一致。
  • 一份由你写的清单,列出住在仓库之外、却生产或消费系统某样东西的部分(一个机器人、一张读数据库的表格、另一个团队的流程),或者确认一个都没有。
  1. 向它要契约地图:

Build docs/PRODUCER_CONSUMER_MAP.md: for every contract in the system (a service, a table, a file format, a list of values), who produces it and who consumes it. If there are parts of the system you cannot access or do not understand, tell me instead of leaving them out of the map. Reply in my language.

你在要求它列出带两端的契约清单,以及把看不到的东西声明出来而不是省略。

发送提示词之前(例行提示词):地图由你审阅过(挑一个契约,验证两边就是你知道的那两边),以及 docs/WORKING_AGREEMENT.md。

  1. 把例行加进契约:

Add this routine to docs/WORKING_AGREEMENT.md: "Before closing any change that touches a contract, look up every consumer in the map and update them in the same commit. Always prefer that the producer defines the list (columns, causes, states) and the consumer reads it, rather than each keeping its own copy. If the map does not have that relationship, add it."

  1. 做列表测试:选一张出现在界面上的列表(Turnera 里是预约的状态:待确认、已确认、已取消、缺席),让代理给你看它在哪里定义、在哪里被读取。如果有两处定义,让它只留一处,并与语义树一致。
  2. 第一次有东西第二次坏掉时(会发生的),把它登记下来。

发送提示词之前:第一次的那个待办项或那次提交,由你在 docs/PENDING.md 已关闭的待办项里找出来,好和提示词一起贴给它(没有这个引用,反模式会写得很泛泛,什么也检测不到);还有 docs/GOTCHAS.md,创建好哪怕是空的,表头里写好格式:气味、根因、修复、如何提前检测。

  1. 粘贴:

This has happened to us before. Record it in docs/GOTCHAS.md with: how it smells (the early sign), the root cause, the fix, and how to detect it next time before it hurts. And add, if possible, a test or an automatic check that detects it.

完整性检验。 从地图里选一个契约:每个消费者是从生产者读它,还是有自己的副本?有没有一个数字在两个界面上显示,而且一致?实体的状态在代码和语义树之间一致吗?GOTCHAS 的每条记录都有四个部分吗?有没有已经记下的东西重演了?

交付物。 docs/PRODUCER_CONSUMER_MAP.md、契约里的例行,以及 docs/GOTCHAS.md(哪怕只以一条记录开头)。

常见错误。 做一次地图就再也不看:它必须随每个新契约增长。把反模式记成轶事("有一次发生了……"),没有"如何提前检测"的部分。还有经典的一个:同一个状态的文本手写在服务器和界面两处;哪天一处改了,另一处就留着。

在引导项目中。 Turnera 的地图一开始有四五个契约:预约状态列表(服务器定义,界面和月报读取);预约表(登记写入,当天日程和报表读取);患者表;以及之后的消息队列(日程写入,发送器读取)。第一个 GOTCHA 通常是日期的:日程表把预约显示在了错误的一天,因为服务器用一个时区保存,界面用另一个时区显示。气味:"23 点的预约出现在明天"。根因:两个时区。修复:只用一个,在服务器上。检测:一个 23:30 的预约的测试。

第 12 课 — 诚实的验证与相称的报告

你需要理解什么。 如果你不打算读代码,代理的报告就必须可信,而这不会自动发生。模型被训练成有用且完整,而一份说"这个我没能验证"的报告,感觉上不如一份说"搞定"的有用。所以必须明确向它要坏消息、盲点和它没做的事。你不要,它就不给;不是因为它隐瞒,而是因为它不把那当作回答的一部分。我曾收到一个带着"success"字样报告的认证错误 Ref. 43。

让报告变得诚实的结构是三个块:已验证(证据在旁边)、相信(它认为能用而没测的,以及为什么没测)和盲点(它从所在之处看不到的东西)。还有一条规则:坏消息先说。

任务。

发送提示词之前,用你自己的手:

  • docs/WORKING_AGREEMENT.md,例行要加进去的地方。
  • 每次会话都把你要求的事情记成一张清单的习惯:提示词要求它逐项覆盖,而你得知道一共有几项。
  1. 把报告例行加进契约:

Add this routine to docs/WORKING_AGREEMENT.md: "When you report, separate three blocks: VERIFIED (what you tested, with the command and its output), BELIEVED (what you think works but did not test, and why not), and BLIND SPOTS (what you cannot see from where you are: another environment, real data, the user's browser). Cover every item I asked for, one by one, even when the answer is 'I did not do it'. No claim without its proof next to it. Do not say 'done': say what is done and how you know it. If something went wrong, say it first."

  1. 让它关闭下一个待办项,用下面的检验清单读报告。第一次,几乎肯定,"盲点"块会是空的。问它: "你从所在之处看不到什么?"。必须有点什么(你的浏览器、生产环境、真实数据)。如果它坚持说没有,你也坚持。
  2. 练习"还是坏的"。向它要一个小东西,在测试环境里验证,如果出于任何原因你没看到变化(很常见:部署要时间,或者没部署),对它说"还是坏的"。

发送提示词之前:你记下来的那次验证(你在哪个 URL 看的、系统显示哪个版本、你做了什么、你期待什么、结果怎样),以及代理说包含那个修复的提交或版本,从它的报告里抄下来。没有这些,"还是坏的"只是一种印象。

  1. 粘贴:

Before diagnosing again, compare the version running where I see the problem with the version that contains your fix. Tell me both. If they differ, the problem is publication, not code.

一半的"还是坏的"都是修复在仓库里却不在你看的地方 Ref. 45。这个条款帮你省下好几个小时。

完整性检验。 三个块都在吗?你随机选了一条"已验证"的断言,它旁边有证据,而且你能复现吗?它覆盖了你要求的每一项吗?坏消息在开头吗?它有没有哪次光秃秃地说了"搞定"?

交付物。 契约里的例行,以及连续三份带三个块的报告,其中一份带一个真实的盲点。

常见错误。 只读"已验证"块而跳过另外两个:另外两个才是保护你的。接受"已验证"里的"应该能用":那是伪装的"相信"。还有习惯了越来越长的报告:一份注水的报告是在用篇幅掩盖什么。

在引导项目中。 Turnera 里 PND-004(取消一个预约)的一份好的关闭报告:"已验证:取消的测试通过(1/1,先证红,针对会删除的版本);回归 6/6;'取消'流程端到端绿色,数据库里的行已标记;在测试环境里,11 月 3 日的 17 号预约显示由 'recepcion1' 于 10:42 取消(截图)。相信:月报会正确计数取消,但我没测,因为报表是 PND-011,还不存在。盲点:我看不到'已取消'状态在前台的手机上是什么样子;你来试。"


上线生产阶段:第 13 到 20 课

这八节课把"能运行"变成"能被维持",而且只做一次,按顺序,那时前两个功能块已经用循环构建完并由你在测试环境里测过,而系统还没有作用于外部世界(发消息、收钱)或拥有真实用户。在引导项目里,这是加提醒和定金的时候,所以第 14 课(锁)在构建那些功能之前,而不是之后。运维质量的那四节(16 到 19)是我做过却没有给它命名的;如果你是初学者,它们的每一份契约都有一段"如果你是初学者"的段落解释它那些词;上课之前先读它。

第 13 课 — 作为计划输入的定期审计

你需要理解什么。 每隔一段时间就得用新鲜的眼光看整个系统,而看到的东西不进一份归档的报告:进待办项列表。重要的部分是"新鲜的眼光":审计的不该是构建的那个,因为它共享自己的假设。一个构建了某模块的模型"知道"写在代码里的密码是临时的;一个第一次看到它的模型会如实报告它是什么。两扇构建者在一天之内留下的管理员后门就是这样被发现的 Ref. 40。不需要另一家公司:另一个会话,带另一份指令文件,没有参与过构建就够了。

任务。

发送提示词之前,用你自己的手:

  • 一个新的代理会话,开在仓库的一份干净副本里(克隆到另一个文件夹)。如果你的工具允许,给它一份不同的指令文件,只说"你是审计者;你没有构建这个系统"。
  • 给那个会话的测试环境只读访问权(数据库、日志),只读凭据放在仓库之外。
  • 由你决定的范围,填进提示词的空里,以及上一次的审计,如果有的话。
  1. 粘贴:

Audit this system for [integrity, security, performance and code quality]. Read-only: change nothing except writing your report to docs/AUDIT_[date].md. If auditing some part requires access you lack (the database, the log, an environment), tell me and mark that part as not audited; do not pass it as fine. Go through all of the code, not a sample. For every finding: where it is (file and line), what the risk is, what evidence proves it (the command, the query, the number), and its priority: P0 must be fixed now, P1 soon, P2 when possible. Give a grade to every dimension you audited. End with the list of P0 findings turned into proposed pending items. Reply in my language.

你在要求它只读,把无法审计的声明出来而不是默认放行,全部代码而不是抽样,每个发现带位置、风险、证据和优先级,按维度打分,以及 P0 已经转成拟议待办项。

  1. 读报告。选两个发现,让审计者给你看证据(命令、行号)。它必须能做到。

发送提示词之前(收尾提示词):报告由你读过,两个发现和审计者核实过,以及构建者的会话已经打开(这条提示词是发给构建者的,不是发给审计者的),文件的日期填进空里。

  1. 回到构建者的会话,粘贴:

Read docs/AUDIT_[date].md. Open one pending item for every P0 and P1, citing the finding. If you disagree with any of them, tell me with the evidence against; do not ignore it.

  1. 在第一次部署到生产之前(第 15 课)重复这节课,之后每一两个月一次。

完整性检验。 报告说了它没能审计什么吗?随机两个发现是否有文件、行号和可复现的证据?每个 P0 都有开着的待办项吗?打分能和下一次审计比较吗?审计的是和构建者不同的会话吗?

交付物。 docs/AUDIT_[日期].md 和开好的 P0/P1 待办项。

常见错误。 用构建的那个会话做审计:它会确认自己的假设。在一个超过一个月的系统上接受一份没有任何 P0 的报告:几乎从来不是真的。还有归档报告却不开待办项:那审计就毫无用处。

在引导项目中。 Turnera 的第一次审计通常会发现:日志里有患者数据(发消息时打印了电话: P0);按姓名查找患者没有索引(P1,到 5.000 个患者时会显现);预约表单没有校验日期在未来(P1);以及某个带已知漏洞的依赖(P1)。四个新待办项,全都带文件和行号。

第 14 课 — 失败即关闭、锁与零静默损害

你需要理解什么。 软件默认在有疑问时继续前进:如果校验失败,代码继续;如果服务没响应,就假定一切正常。这是"失败即打开",像一扇停电时打开的门。方法反转了默认值:有疑问,就不。每个有后果的动作(收费、发送、删除、批准)都带一把锁,有写下来的回退路径,而且每个新的自动化都从演练模式开始,记录它本会做什么,直到你启用它。我在第一个系统里放的锁是样板 Ref. 28。

这节课在构建作用于外部世界的功能之前。在 Turnera 里,在提醒和定金之前,它们是第三个功能块。

任务。

发送提示词之前,用你自己的手,和所有者一起:

  • docs/CONSEQUENTIAL_ACTIONS_SURVEY.md:系统做什么或将要做什么是收费、发送、批准、删除或通知外部某人的;并且对每个动作,做两次会怎样、做错了会怎样、怎么撤销、谁应该有权做。语义树的流程给你起始清单。提示词把它叫作 "the survey of consequential actions",没有给路径:告诉它在哪儿。
  • docs/SEMANTIC_TREE.md,保持最新。
  1. 粘贴:

We are going to decide which actions of the system need a lock. Read the survey of consequential actions, and go through the code looking for everything that charges, sends, approves, deletes or notifies outwards. Compare the two lists: if the code has actions the survey does not mention, or the other way round, ask me before going on. For every action propose: whether it is reversible, which lock it gets (explicit confirmation, double safety, dry-run mode, daily limit, a human in the loop), and what the way back is if it goes wrong. General rule: on any doubt or failure along the way, the action is NOT executed and the reason is logged. Present me the table; I confirm lock by lock. Reply in my language.

你在要求它把业务说的和代码做的做比较(没人记得的动作就在那里出现),差异要问而不是自作主张,以及对每个动作的可逆性、锁和回退路径,并写明失败即关闭的规则。

  1. 和所有者一起,逐把锁确认。

发送提示词之前(收尾提示词):表格的每一行,你的确认或你的修改,都写好准备填进提示词的空里;没有哪一行缺裁决,因为一把没有确认的锁就是一把不会被实现的锁。

  1. 用这个收尾:

I confirm locks [list]; change [X] to [Y]. Implement them with one test per lock that proves that, under the doubt condition, the action does NOT happen (proved red against the version without the lock). Record the final table in docs/LOCKS.md. And for any new automation: dry-run mode first, logging what it would have done, until I enable it.

  1. 现在用第 5 课打开第三个功能块(提醒、定金),用第 5 到 12 课的完整循环构建它,并让它留在演练模式。在启用真实发送之前,花几天看"本会发送"的记录。

完整性检验。 每个收费、发送、批准或删除的动作都在表里吗?每把锁都有它的红色测试吗?每个不可逆的动作都有写下来的回退路径吗?新的自动化是从演练模式开始的吗,启用之前你看过记录吗?

交付物。 docs/LOCKS.md、每把锁一个先证红的测试,以及处于演练模式并带记录的自动化。

常见错误。 "为了用一条发到我自己手机的消息测试"而启用真实发送:演练模式恰恰就是为此存在的。一把可以用"测试用"参数绕过的锁:那个参数会留在生产里。还有不看"本会做"的记录:那是在错误到达患者之前看到它的唯一窗口。

在引导项目中。 Turnera 的锁表:发送提醒(不可逆;锁:按预约幂等,每个预约和渠道只发一次,每天上限 200,先演练模式;回退:无,所以才有上限);收取定金(可通过退款撤销;锁:前台的显式确认,金额由专业人员设定;回退:通过支付供应商退款);取消预约(对患者不可逆;锁:确认并记录谁操作的;回退:重新创建预约);删除患者(不可逆;锁:双重确认,只有负责人可操作,且仅当没有未来预约时;回退:无,所以才双重)。每把锁都有一个测试表明,没有锁时,动作会发生。

第 15 课 — 持续集成、环境、密钥与作为产品一部分的部署

你需要理解什么。 到达生产必须是一个可复现的包:测试跑一次,部署只打包并上传,配置住在服务器上而不在仓库里,有一个从测试提升到生产的、可以运行两次的程序,而且回滚经过测试 (从没测过的回滚不存在)。每次提交跑三次测试把我的持续集成配额烧光了 Ref. 39。关于密钥,一条没有例外的规则:在聊天、日志或提交里出现过的密钥就不再是密钥;轮换它,没有商量 Ref. 42。

从骨架(第 4 课)起你已经有了其中的大部分:持续集成和到测试环境的部署。这节课把它向生产补全。

任务。

发送提示词之前,用你自己的手:

  • docs/ENVIRONMENTS_SURVEY.md(第 4 课),用生产所缺的东西补全:域名和谁管理它、密钥要住在哪里以及有哪些(名字,永远不写值)、谁可以动生产、今天要回滚会怎么做,以及你有多少持续集成配额(如果用 GitHub Actions,看看套餐的上限)。提示词把它叫作 "the environments and access survey":告诉它在哪儿。
  • 给代理的生产访问权,由你决定,凭据放在仓库之外、聊天之外。
  • 带持续集成和测试环境部署、已经跑通的骨架(第 4 课)。
  1. 粘贴:

We are going to close the path to production. Read the environments and access survey. Evaluate whether you have what is needed to deploy to production safely (access, domain, where the secrets live, how to roll back); if anything is missing, ask me. Then set up: (1) continuous integration runs the full test suite exactly once per commit; (2) the deployment waits for that run's result and only packages and uploads, without running tests again; (3) each environment's configuration lives on the server (environment file or vault), never in the repository; (4) a written procedure to promote from the test environment to production, which can be run twice without breaking anything; (5) the rollback, tested; (6) an automatic check that prevents committing a secret. Document all of it in docs/DEPLOYMENT.md so that a person can do it by hand without you. Reply in my language.

你在要求它在开始之前核实访问权限,每次提交只跑一次测试,只打包的部署,配置放在外面,可重复的提升,经过测试的回滚,防密钥的锁,以及给人看的文档。

  1. 在第一次部署到生产之前,如果还没做第 13 课的审计就做了,并用将要提升的确切版本跑所有流程的完整端到端测试套件(第 8 课的功能块收尾提示词)。

发送提示词之前(例行提示词):docs/DEPLOYMENT.md 已写好并由你读过(你一个人能照着做下来吗?),以及 docs/WORKING_AGREEMENT.md。

  1. 把部署例行加进契约:

Add this routine to docs/WORKING_AGREEMENT.md: "Before touching production: the test environment is green with the same version; the rollback is ready; and tell me what changes in the configuration. After deploying: show me the version running in production and an end-to-end check on production, read-only."

  1. 做第一次生产部署,此时系统还没有真实用户。然后测试回滚:让它回到上一个版本并给你看它在运行,再回到新版本。
  2. 如果课程期间你在聊天里粘贴过某个密钥(一个 API 密钥、一个密码),轮换它。

发送提示词之前:已经确认的那个密钥(它的名字,永远不写值)、它在哪里出现、什么时候,以及你对生成它的那个控制台的访问权,以防代理没法自己轮换。这条提示词不存在"晚点再发"。

  1. 粘贴:

That secret is compromised: rotate it now, tell me everywhere else it was used, and add the check that would have caught it.

完整性检验。 每次提交测试套件跑几次?(一次;索要链接)。仓库里有没有任何真实的配置值?(让它给你看空的搜索结果)。你能独自照着 docs/DEPLOYMENT.md 做吗?回滚测过了吗,什么时候,凭什么证据?聊天里出现过的每个密钥都轮换了吗?你知道还剩多少持续集成配额吗?

交付物。 每次提交一次的持续集成、只打包的部署、docs/DEPLOYMENT.md、经过测试的回滚、启用的防密钥锁,以及在生产中运行的系统(暂时没有用户)。

常见错误。 "手工、快速地部署,因为是个小改动":小改动会弄坏生产,因为没人测它们。配置示例文件里有看起来像真实的值(它们就是)。还有直到需要的那天才测回滚。

在引导项目中。 对 Turnera,持续集成在每次 push 时运行测试套件(到这时大约 30 个测试,包括六个端到端流程);合并到 main 时自动部署到测试环境;提升到生产是一条命令,把测试环境里的版本打包上传,配置(消息供应商的密钥、支付供应商的密钥、数据库的 URL)放在服务器上一个不在仓库里的环境文件中。回滚是同一条命令带上一个版本。防密钥的锁是每次提交时的一个检查,拒绝任何看起来像密钥的东西。

第 16 课 — 可观测性与可追溯性,级别可配置

你需要理解什么。 当生产里有什么东西坏掉时,你会想在不打开代码、也不让代理"查一下"的情况下知道发生了什么:搜一个编号,然后那个动作从界面到数据库的完整故事就出现。那就是带*关联标识符*的 *结构化日志*。而且你会想在调查的时候,花一分钟把系统写下来的详细程度调高,不用重新部署:那就是 *可配置的级别*。*可追溯性*是另一半:每个有后果的动作(收了钱、发了消息、删了数据)都留在一份应用改不了的清单里,带着谁、何时和做了什么。没有这些,第 23 课的驻留代理就是瞎着运维,而第 20 和 21 课的监控没有东西可看 Ref. 16。

发送提示词之前,用你自己的手,和所有者一起:

  • docs/DEPLOYMENT.md(第 15 课),写着按环境的配置是怎么读的:级别将是多出来的一个变量。
  • docs/LOCKS.md(第 14 课):那些必须留在审计里的动作的清单。
  • 在 docs/INITIAL_SURVEY.md 里,哪些数据是个人数据的清单(在 Turnera 里:患者的姓名、电话、邮箱和证件),和负责人一起写。日志必须给它们脱敏;这是法律义务。
  • 你关于日志住在哪里、保存多久的决定。课程里用:服务器上一个轮转的文件,九十天。
  1. 把第 16 级台阶契约(在 docs/peldanos/ 里)的初始提示词原样贴给它。
  2. 当它给你看一个真实请求的标识符时,你自己去测试环境的日志里搜它:它必须出现在界面、在后端、在数据库,以及在对消息供应商的调用里。
  3. 你自己把级别调到调试,做一个动作,看一看,再调回去。如果你必须部署,那就是没登上去。
  4. 在日志里搜"Juan"(或者任何一个测试用的名字):不应该有。

发送提示词之前(例行提示词):docs/OBSERVABILITY.md 由你读过,而且级别由你自己调高又调低过一次。

  1. 把第 16 级台阶的例行(在契约里,"例行"那一段)加进契约,像其他几条一样,用 "Add this routine to docs/WORKING_AGREEMENT.md:" 包起来。

完整性检验。 你自己把一个标识符跟过所有层了吗?你不部署就改了级别吗?日志里有任何个人数据吗?在测试环境里取消一个预约,带着谁和何时留在审计里了吗?docs/OBSERVABILITY.md 够不够让你一个人找到一条日志?

交付物。 级别可配置的结构化日志、被传递的标识符、有后果动作的审计、 docs/OBSERVABILITY.md,以及写进契约的例行。

常见错误。 接受每个模块写法都不一样的"日志"。把生产"暂时"留在调试级别。还有让标识符到不了夜间的提醒任务,而那恰恰是事故走失的地方。

在引导项目中。 "取消预约 17"的标识符出现在那次点击里、在服务器里、在数据库被标记的那一行里,以及在发给专业人员的邮件里。审计写着"recepcion1 在 10:42 从当天的日程里取消了预约 17;结果:ok"。负责人把级别调到追踪,去看一条提醒为什么迟了,找到了,然后调回去。

第 17 课 — 持久化层的优化,前后都要测量

你需要理解什么。 数据库是系统慢慢死掉的地方:一百个预约时好好的,十万个时很糟。一个*索引* 就像一本书的目录:没有它,数据库为了找到一个预约要读整张表。一个 *N+1 循环*是为了显示一百个预约而做一百条查询,而不是一条。这节课把第 7 课应用到数据库上:用一把带测试的量具测量每条查询,按数字说的去改,在同样的数据上重新测量,并留下一个带测试的预算让它别回来 Ref. 18、Ref. 37。它不是重写数据库,也不是换引擎:那是第 3 课。

发送提示词之前,用你自己的手:

  • docs/SIZING.md 和 docs/VOLUME_SURVEY.md(第 3 课):要装载的量级。
  • 装载了预期量级数据的测试环境:对 Turnera 来说,一年一万个预约和三万条消息,由代理留在仓库里的一个脚本生成(把它作为一个在先的待办项向它要,按语义树的形状来)。
  • docs/PRODUCER_CONSUMER_MAP.md(第 11 课),保持最新。
  • 给量具的测试用的一个已知案例:专业人员表有三行。
  1. 把第 17 级台阶契约的初始提示词贴给它。
  2. 像负责人读一份诊所修缮清单那样读那份被提议的改动清单:你明白每一项赚到什么、有什么风险吗?逐项批准或拒绝。手工重算一笔(预约表每月的行数)。

发送提示词之前(收尾提示词):带着你逐项裁决的清单,以及对得上的那笔手工重算。

  1. 把第 17 级台阶契约的收尾提示词贴给它,带上批准的清单。
  2. 你自己用它给你的两条命令跑一把量具的前和后。

完整性检验。 你跑出来的和 docs/PERSISTENCE.md 的表对得上吗?关键流程里还留着读整张表的查询吗?每张表都有写下来的保留期吗?预算有测试吗(让它超出一个,给你看那个红)?

交付物。 带测试的量具、带前后对比的 docs/PERSISTENCE.md、带测试的预算,以及绿色的测试套件。

常见错误。 用骨架里那十条记录来测量。接受"我们还是迁移到另一个数据库吧"。还有改好了一个数字却弄坏了读那张表的月度报表:漏了地图。

在引导项目中。 当天的日程每个预约做一条查询(第 7 课那个循环):50 个预约的 52 条查询变成 3 条。按姓名搜患者原本读整张表:一个索引在一万名患者下把它从 800 毫秒降到 4 毫秒。保留期:消息满一年归档;预约,永不。预算:日程 4 条查询,带测试。

第 18 课 — 漏洞的扫描与解决

你需要理解什么。 一个*漏洞*是一个别人可以用来进来、偷数据或者让系统做它不该做的事的缺陷。审计(第 13 课)带着判断力、每隔一段时间去找它们;这节课由机器去找,一直找,分四层:*依赖*(你的系统用的别人的组件,它们的已知缺陷是公开的)、代码(*静态分析*)、仓库历史里的密钥,以及服务器的配置。还有一条规则:更新一个依赖是一次变更,它要走循环;"升了个版本把发现关掉"而没有回归,是在星期五弄坏生产最常见的方式 Ref. 41、 Ref. 23。

发送提示词之前,用你自己的手:

  • 持续集成在跑(第 4 和 13 课),以及你还剩多少配额:扫描会多花几分钟。
  • docs/ENVIRONMENTS_SURVEY.md,带服务器、端口和谁管理域名。
  • docs/AUDIT_[日期].md(第 13 课),好让它不重复已知的东西。
  • 你那两行的 P0 判断准则:对 Turnera 来说,能不带凭证就进来的、能暴露患者数据的、能不经控制就发消息或收钱的。
  1. 把第 18 级台阶契约的初始提示词贴给它,把你的准则填进空位。
  2. 读 docs/SECURITY_[日期].md。挑两项发现,向它要证据。它说了没有扫描什么吗?

发送提示词之前(收尾提示词):已批准的修复清单;对每次依赖更新,清楚它会跑回归和端到端。

  1. 把第 18 级台阶契约的收尾提示词贴给它。
  2. 让它在一个分支里故意放进一个有已知漏洞的依赖:链条必须变红。

完整性检验。 最近一次扫描运行是绿的吗,是哪一天的?每个 P0 都有带证据关闭的待办项吗?扫描是 docs/GATES.md 里的闸门吗?你知道下一次计划的扫描什么时候跑吗?

交付物。 链条里带日程的四层扫描、docs/SECURITY_[日期].md、docs/SECURITY.md、修好的 P0 和 P1,以及那道闸门。

常见错误。 一个存在超过一个月的系统,扫描"第一次就干净":少了一层。在一次提交里更新十个依赖。一个"被接受"的 P0,没有待办项也没有日期。

在引导项目中。 Turnera 的扫描找到:PDF 生成器的一个依赖带着一个已知漏洞(P1,带回归和端到端地更新);那个阻止把日程嵌进别的站点的响应头缺失(P1,一行配置,带测试);仓库历史里一把支付供应商的测试密钥(P0:轮换掉,哪怕它是测试用的);以及测试服务器上一个对全世界开着的数据库端口(P0:关掉)。扫描留作闸门,每周一跑。

第 19 课 — 量级下的性能测试与断裂点

你需要理解什么。 第 3 课用算式做了容量估算;这节课用跑着的系统核对它们。一次*负载测试*模拟很多用户同时在用。要看*吞吐量*(每秒多少个动作)、*延迟*(每个动作要多久;中位数和 *95 百分位 *,也就是那些来抱怨的人看到的),以及错误率,在预期量级下和在"乘以十"下。然后把负载加上去直到有什么坏掉:*断裂点*不是"变慢了",而是"数据库连接在 180 个用户时耗尽了"。那个数字是这个阶段最有用的,因为第 21 课的告警*阈值*就是从它来的 Ref. 12、 Ref. 44。

发送提示词之前,用你自己的手,和所有者一起:

  • docs/SIZING.md,带预期量级和"乘以十":对 Turnera 来说,峰值两名前台和每天四十个预约;乘以十,二十名前台和四百个预约。
  • 填进提示词空位的关键流程:登记一个预约、看当天的日程、搜一个患者、提醒的夜间发送。
  • 负责人对最常用界面(当天的日程)的容忍度:"如果超过两秒,姑娘们就会抱怨"。
  • 装载了量级数据的测试环境(第 17 课),以及,如果它在资源上和生产不一样,在 docs/ENVIRONMENTS_SURVEY.md 里记下怎么不一样。
  • 第 16 课的可观测性已打开。
  1. 把第 19 级台阶契约的初始提示词贴给它,把流程和容忍度填进空位。
  2. 把容忍度放在旁边读 docs/PERFORMANCE_[日期].md:当天的日程在预期量级下进得了两秒吗?乘以十呢?最先坏的是什么,在什么负载下?
  3. 和负责人一起决定现在修到哪个量级的瓶颈:如果他们想卖给别的诊所,"乘以十"就是现在;如果不想,带上日期。

发送提示词之前(收尾提示词):报告已读,关于"乘以十"的决定已做。

  1. 把第 19 级台阶契约的收尾提示词贴给它。
  2. 你自己用它给你的命令在预期量级上跑一遍脚本:数字必须相像。

完整性检验。 最常用的界面进了容忍范围,还是有一个带那个数字的待办项?断裂点说了是什么坏了、在什么负载下吗?docs/PERFORMANCE.md 的阈值在断裂点之下、预期量级之上吗?这项测试是闸门吗,第一次运行归档了吗?

交付物。 带测试的负载脚本、docs/PERFORMANCE_[日期].md、带阈值和闸门的 docs/PERFORMANCE.md,以及每个瓶颈一个待办项。

常见错误。 "乘以十毫无压力扛得住"却没有断裂点:负载加得不够。一个打 API 而不打界面的脚本。在测试过程中"顺手"修瓶颈。

在引导项目中。 在预期量级下,日程 300 毫秒响应;乘以十,1.4 秒;400 条提醒的夜间发送要 50 秒。断裂点:180 名并发用户时连接池(12)耗尽,动作开始失败。阈值:日程超过 1.5 秒,或者在用连接数超过 7,就告警。待办项:夜间发送改成每批 50 条,因为负责人确实想卖。

第 20 课 — 活的文档与无需 AI 即可遵循的手册

你需要理解什么。 系统必须能在没有代理、没有你的情况下被使用、维护和恢复。恢复手册的标准很具体:一个不编程的可信的人,在凌晨三点,系统宕机、代理离线的情况下,必须能照着它做。如果某一步写着"咨询开发者",它就是不完整的。而既然用模型重新生成文档是免费的,规则就变成"在同一次提交中":每个触及用户所见、操作者所做或部署方式的变更,都在同一次提交中更新手册。我按这个标准为我的第一个系统写的手册是样板 Ref. 24。

还有一个你已经有的来源:已关闭的待办项。如果你按第 5 课的要求记录了每次关闭,用户手册不是从零写的:它是从关闭的待办项里一块一块搭起来的,变更日志直接从它们产生。

任务。

发送提示词之前,用你自己的手:

  • docs/WORKING_AGREEMENT.md,例行要加进去的地方。
  • 系统会有哪些手册的清单,每本都写上它的读者(最终用户、维护者、运维基础设施的人),写在 PROJECT.md 里或在聊天里说出来。
  1. 把例行加进契约:

Add this routine to docs/WORKING_AGREEMENT.md: "Every change that modifies what the user sees, what the operator does, or how the system is deployed, updates the corresponding manual in the same commit. At the close of every functional block, add to the user manual the screens and flows of the pending items closed in that block, with real screenshots from the test environment, and add their entries to the changelog. If you do not know which manual, ask."

发送提示词之前(手册提示词):系统在测试环境里运行着、带着真实感的数据(截图从那里出来),已关闭的待办项带着它们的文档(第 5 课),docs/DEPLOYMENT.md 和 docs/LOCKS.md 保持最新,以及只有业务才知道的东西,准备好等它来问:谁对什么负责、凌晨三点打哪个电话、谁做备份。

  1. 粘贴:

Generate three manuals from the system as it is today in the test environment: the user manual (screen by screen, with real screenshots, in the user's language, no jargon); the maintenance manual (how it is built, where everything is, how to run the test suite, how to add a module); and the infrastructure and recovery manual (how to deploy, how to roll back, how to restore a backup, what to do if it goes down; written for a person who has to do it by hand, without AI, at three in the morning). If for any section you need something only the business knows (who is responsible for what, which phone number to call), ask me; do not leave a gap and do not invent. Each manual carries a date and the system version. Reply in my language.

你在要求它三份带真实截图的手册,恢复手册的凌晨三点标准,只有业务才知道的东西要问而不是留空洞,以及日期和版本。

  1. 真正的测试:把恢复手册交给一个不编程的人(所有者、一个家人),让他在测试环境里照着"宕机了"一节做。卡在哪里,手册就得修哪里。
  2. 下一次界面变更之后,核实提交动了用户手册。
  3. 现在做验收测试:所有者在测试环境里用真实感的数据、按六个流程使用整个系统,在第一个真实用户进来之前。他发现的东西是待办项;它们关闭后,系统就被接受了。

完整性检验。 随机一张截图和今天的测试环境一致吗?一个不编程的人能照着"宕机了"一节做吗?最近一次改了界面的提交动了手册吗?变更日志对每个已关闭的待办项都有一条吗?有没有"[待补]"之类的空洞?每份手册都说了它对哪个版本有效吗?所有者对所有流程做了验收测试吗?

交付物。 带日期和版本的三份手册、已完成的真人测试,以及已关闭的验收测试。

常见错误。 生成一次手册却不把例行放进契约:两个月后它们的截图是已经不存在的界面。一份以 "打开一个代理会话"开头的恢复手册。还有不做真人测试:那是唯一算数的验证。

在引导项目中。 Turnera 的用户手册有六个带截图的界面,从三个功能块关闭的待办项搭起来;维护手册解释每个模块在哪里、怎么运行测试套件;恢复手册按顺序、带命令写明:怎么知道系统宕了、怎么重启、怎么回到上一个版本、怎么从最近一次备份恢复数据库(每天夜里做一次:这也出自这节课),以及给谁打电话。负责人在测试环境里照着做,卡在了"恢复数据库",因为命令假定了一个她没有的文件夹;手册修好了。

运维阶段:第 21 到 23 课

这三节课从第一次部署到生产开始,而且不会结束:两个监控随它搭起来,从那以后自己跑;复盘则收尾每个阶段,并回到第 0 级,为下一个阶段做准备。

第 21 课 — 资源与性能的监控,带周期报告

你需要理解什么。 第 19 课说的是系统在某一天表现如何;生产是每一天,而且数据在长大。*监控* 就是由机器一直看着*资源*使用(处理器、内存、磁盘、连接、消息供应商的配额)和每个函数每条查询的响应时间,并在有东西出了范围时报警:高于一个*阈值*(第 19 课那些)或者和*基线*(它一直给出的值)差得很远。一条*告警*当场通知;*周期报告*每周或每月平心静气地读。两条规则:一条没人读的告警是那条告警的缺陷;而且不能为了让一条告警闭嘴就在没有一条测出来的书面理由的情况下调高阈值 Ref. 37、Ref. 39。

发送提示词之前,用你自己的手,和所有者一起:

  • docs/OBSERVABILITY.md(16)、docs/PERSISTENCE.md(17)和 docs/PERFORMANCE.md(19)已收尾。
  • 和负责人一起决定的告警渠道:发给谁、走哪里。课程里用:你的邮箱和你的即时通讯。
  • 周期:Turnera 用每月;如果每天都有钱在赌桌上,就用每周。
  • 你认为算告警的偏离:比最近十四天的平均值高 30%。
  • 监控对生产的只读访问权,凭证在聊天之外。
  1. 把第 21 级台阶契约的初始提示词贴给它,把渠道、周期和偏离填进空位。
  2. 让它在测试环境里故意超过一个阈值:告警必须按那个渠道到你手上。
  3. 读第一份报告:每个超出范围的事件都必须有原因或提议。

发送提示词之前(例行提示词):第一份报告由你读过。

  1. 把第 21 级台阶的例行加进契约,用 "Add this routine to docs/WORKING_AGREEMENT.md:" 包起来。

完整性检验。 那条被挑起来的告警到你手上了吗?报告里每个偏离都有原因或提议吗?你决定要处理的每个偏离都有一个以数字为准则的待办项吗?从第 19 课以来有哪个阈值被调高过吗,理由是什么?报告看消息供应商的配额吗?

交付物。 带历史的采集、告警、周期报告、docs/MONITORING.md,以及写进契约的例行。

常见错误。 每天都响却没人读的告警。连着三份说"一切正常"的报告。一个"好让它别再报了"而被调高的阈值。"为了能诊断"而保存的个人数据。

在引导项目中。 Turnera 的第一份月报说:磁盘 40%,内存稳定,当天的日程中位数 320 毫秒(基线),患者搜索从 4 毫秒升到 9 毫秒(表在长大;在范围内),消息配额 60%(提议:到 80% 时通知)。一个偏离:负责人的月度报表在 1 号用了 11 秒(提议:夜里预先算好;待办项)。

2026 年 10 月 19 日的变化(D-039)。 复核容量规划的测量必须自带可验证的窗口:从数据库读取的最早和最晚记录,而不是手工输入的数字。在练习中,三年的 30,000 行被当作一年传入,引发了误报。手工输入的窗口要在报告中标明,且只发出提醒。

第 22 课 — 错误监控与周期报告

你需要理解什么。 一个生产中的系统会产生没人报告的错误:那个看到白屏又重试了一次的前台,那个中断了的夜间任务。这节课在它们藏身的三个地方(后端、任务和浏览器)*捕获*它们,把它们*分组* (同一个错误的一千次是一条带计数器的条目),对新出现的和在长大的发出告警,并产出一份带*按流程错误率*的报告(每登记一百个预约,有多少个失败)。两条规则:没有任何错误没有待办项,而且不存在" 已知错误",除非有一个待办项说明为什么容忍它、容忍到什么时候 Ref. 43、 Ref. 22。

发送提示词之前,用你自己的手:

  • docs/OBSERVABILITY.md(16):错误已经带着标识符、版本和执行者出来。
  • docs/LOCKS.md(14):有后果动作里的错误是立即告警。
  • 第 21 课的渠道和周期,以及一个已知错误每小时出现多少次你就想要告警:对 Turnera 来说,十次。
  • 错误保存在哪里、保存多久,不含个人数据。
  1. 把第 22 级台阶契约的初始提示词贴给它,把渠道、周期和那个数字填进空位。
  2. 你自己在测试环境的浏览器里引发一个错误(比如在预约表单里填一个不可能的日期):它必须带着你的标识符出现在捕获里,而且如果是新错误,告警要到你手上。

发送提示词之前(例行提示词):第一份错误报告由你读过,以及 docs/PENDING.md 放在手边。

  1. 把第 22 级台阶的例行加进契约,像其他几条一样包起来。
  2. 拿到第一份报告时:为新错误开纠正型待办项,每个带上它的复现方式,并用第 8 课的例行(复现它的红色测试)关闭它们。

完整性检验。 你引发的那个错误到了捕获、到了告警、到了报告吗?有没有哪个复发错误既没有待办项也没有带日期的理由?报告里一个"已修好"的错误,它的待办项里有那个红色测试吗?按流程的错误率在不在,有没有和上一周期比较?捕获里有个人数据吗?

交付物。 集中且分组的捕获、告警、带按流程错误率的周期报告、完整的 docs/MONITORING.md、写进契约的例行,以及每个错误一个待办项。

常见错误。 有真实用户却"一个月零错误":捕获没有看到浏览器或者任务。一个有一千次出现、附着" 已知,不影响"的错误。靠重启"修好"的错误。还有读错误报告却不和资源报告交叉看:一个的慢查询就是另一个的等待超时。

在引导项目中。 Turnera 的第一份报告有三个错误:一个在浏览器里(如果专业人员的名字里有一个撇号,日程就失败;14 次;待办项,红色测试),一个在夜间任务里(有一晚消息供应商返回了一个错误;1 次;待办项,带记录地重试),还有一个侄子留下的"已知"错误,在月度报表里(带日期的待办项:用第 2 部分 R2 课的报表替换它)。错误率:"登记预约"0.3%,其余 0%。

2026 年 10 月 19 日的变化(D-038)。 错误捕获按来源区分:系统的错误和观测它的工具(面板、常驻代理、监控本身)的错误放在报告的不同节里,只有系统的错误才会打开待办项。在练习中,第一份周期报告打开了六个"bug",其实是工具在跟一个已关闭的服务器通信。

第 23 课 — 系统里的代理,以及带数字的复盘

你需要理解什么。 两件事收尾这个循环。第一件是可选的:把代理留在生产里驻留,范围受限,用来诊断和提供支持,不需要你在场 Ref. 13。统领它的规则是它以某人的名义行动:它的凭证映射到一个负责的人,它做的一切都以那个人的名义记录;它不是一个匿名账号,是一个有上司的员工。第二件是必做的:带数字的复盘。花了多少,多少待办项,哪些反复折腾,有哪些事故以及各落在哪级台阶,以及至少一条对梯子的修改提案。一次什么都不改变的复盘不是复盘;让我砍掉了整个代理团队的那一次是样板 Ref. 6。

任务(驻留代理,如果适用)。

发送提示词之前,用你自己的手,和所有者一起:

  • 系统是否需要驻留代理的决定,以及为了什么。Turnera 需要:前台会有问题("为什么这个患者没收到提醒?"),代理读日志就能回答,不需要任何人打电话给开发者。
  • 代理在生产里专属的凭证,由你创建,映射到一个有名字的负责人,一开始只读,值在聊天之外、仓库之外。
  • docs/LOCKS.md 和 docs/DEPLOYMENT.md 保持最新,以及它解决不了的事情通过哪个渠道(工单、邮件)上报。
  1. 粘贴:

We are going to leave you resident in production. First, build the map of the system you will need in order to operate it: modules, environments, where each configuration lives, which actions are consequential (read docs/LOCKS.md), whom to notify. If there are parts of the system you cannot see from where you are, tell me; the map must not have hidden gaps. Then propose your scope in three levels: read-only and diagnosis; configuration changes with my approval; consequential actions behind locks. I choose the level. Your credential acts as the person who created it: everything you do is logged under that person's name. Reply in my language.

你在要求它在操作之前先有地图,地图的空洞要声明出来,三个范围级别让你选一个,以及以某人名义的凭证。

  1. 选级别(起步时,永远是第一个:只读与诊断)。

发送提示词之前(收尾提示词):地图和三个级别已经读过、级别已经选定,以及由你想好的一个在那个范围之外的具体动作,好让代理去尝试、而你在日志里看到那次拒绝。

  1. 用这个收尾:

Level [1: read-only and diagnosis]. Write in docs/RESIDENT_AGENT.md the map, the scope, the credential (never its value), and the procedure to request my approval. Then prove, with a real attempt recorded in the log, that something outside your scope is rejected.

任务(复盘,永远要做)。

发送提示词之前,用你自己的手:

  • 只有你才有的数字,写在 docs/RETROSPECTIVE_INPUTS.md 里或准备好在聊天里给出:你投入了多少小时(从你的日历里来,不是从记忆里来)、在 AI 工具上花了多少钱(账单)、用了多少持续集成配额,以及开始和结束的日期。
  • 事故清单,按你记得的样子,附大致日期,用来和代理从待办项和提交里挖出来的东西对照。
  • docs/PENDING.md 里带文档的已关闭待办项,以及 docs/DECISIONS.md:它们是那些数字的来源。
  1. 粘贴:

[The project] is over. Do the retrospective with numbers, not impressions: what it cost (tokens, quota, my time), how many pending items, which ones took the most rounds and why, which incidents happened and which step of the ladder each falls into, and which step failed or was missing. If you lack the data for any figure (my time, for instance), ask me for it or mark it as an estimate; do not invent it. End with a concrete proposal to change the ladder: which step is modified, how, and which incident justifies it. Write it to docs/RETROSPECTIVE_[date].md. Reply in my language.

你在要求它带来源的数字,每个事故映射到一级台阶(落不进任何一级的事故就是缺失的台阶),它没有的数字向你索要,以及一条对梯子的具体修改提案。

  1. 读修改提案。如果你觉得它成立,把它作为改进提案发到 Urazuke 仓库(见 CONTRIBUTING.md):这就是方法从每个项目学习的方式。

完整性检验。 地图说了代理看不到什么吗?你在日志里看到范围之外的动作被拒绝了吗?凭证以某个人的名义行动吗?复盘有带来源的数字,还是形容词?每个事故都落在一级台阶上吗?至少有一条具体的修改提案吗?

交付物。 docs/RESIDENT_AGENT.md(如果适用)和 docs/RETROSPECTIVE_[日期].md。

常见错误。 给驻留代理一个高级别"好让它什么都能解决":从只读开始,有证据了再升。一份说一切顺利的复盘:从来没有一切顺利;它是写给你的,不是写给方法的。还有不发修改提案:第 23 级台阶在梯子改变时才关闭,不是在文档写完时。

在引导项目中。 典型课程里 Turnera 的复盘:学员大约四十小时,二十八个待办项,折腾最多的是消息相关的(时区和 WhatsApp 供应商);两起事故,一起在第 11 级台阶("缺席"状态存在于界面而不存在于服务器,也不在语义树里),另一起在第 15 级(一次带着测试配置部署到生产,被部署后的端到端检查发现)。修改提案:让第 15 级台阶的部署例行在提升之前要求展示测试与生产的配置差异,而不只是"有什么变化"。


上线生产清单

当二十四节课都完成,系统满足这张清单。这是 Urazuke 里"生产级"的操作性定义,可以作为最终检验清单使用,和所有者一起,不打开代码:

  1. 有一份带决定、日期和名字的适格性卡片(P0)。
  2. 有一份代理每次会话都读的协作契约,并且验证过它在读(P1、P2)。
  3. 项目文件五分钟能读完,并说了系统不是什么;有一棵由所有者确认的语义树,每个待办项都挂在一个节点下(P2)。
  4. 有一份容量估算,原始数据带来源,派生数据带算式,还有"乘以十"的场景(P3)。
  5. 骨架在启动阶段空着部署了,带 CI/CD 链,而且部署运行了两次(P4)。
  6. 每个变更都有带详细规格的待办项,每个已关闭的待办项都保留着规格和证据:这是所做之事的文档(P5)。
  7. 每条业务规则都有所有者、日期和原话,在提交里、待办项里和记录里(P6)。
  8. 每个决定过某事的数字都有一把你能运行的、带测试的量具(P7)。
  9. 每个重要的测试都在变绿之前被看到变红;每个流程都有从界面验证后端的端到端测试;完整测试套件在每个功能块结束时和生产之前都跑过(P8)。
  10. 代码质量在每次提交时由机器验证,它是闸门,而且覆盖率从未下降(P9)。
  11. 每个功能都有它的人工测试用例,写给一个人看;它们在每个功能块收尾时和验收时被执行过,带日期和名字;每一处失败都作为待办项回来了(P10)。
  12. 每张列表只有一个所有者;有一张生产者/消费者地图;有一份带自动检查的反模式记录(P11)。
  13. 每份报告都把已验证、相信和盲点分开(P12)。
  14. 至少有一次由不同会话做的审计,而且它的 P0 是待办项(P13)。
  15. 每个有后果的动作都有带红色测试的锁和回退路径;自动化都经过了演练模式(P14)。
  16. 测试每次提交跑一次;配置在仓库之外;回滚测过了;没有密钥住在仓库里或未经轮换地留在聊天里(P15)。
  17. 日志是结构化的,级别不部署就可配置,带一个把每个动作从界面跟到数据库的标识符;有后果的动作留在一份不可变的审计里;日志里没有任何个人数据(P16)。
  18. 真实查询在预期量级下被测量过,带前后对比地纠正过,而且每条关键路径都有带测试的预算(P17)。
  19. 漏洞扫描在每次 push 和按日程运行,它是闸门,而且没有 P0 开着(P18)。
  20. 有一项带测试的负载测试,在预期量级和"乘以十"下跑过,断裂点写了下来,阈值从它推导出来(P19)。
  21. 有三份带日期和版本的手册,从已关闭的待办项搭起来;一个不编程的人照着恢复手册做了;所有者对所有流程做了验收测试(P20)。
  22. 资源使用和响应时间被告警和周期报告看守着,而且每个偏离都有原因或待办项(P21)。
  23. 错误在后端、任务和浏览器里被捕获、被分组、按周期连同按流程的错误率被报告,而且没有一个留着没有待办项(P22)。
  24. 有一份带数字的复盘和一条对梯子的修改提案(P23)。

如果二十四条都是"是",系统就可以投入使用了。如果有哪条是"否",你已经知道该回去重登哪级台阶。


之后:第 2 部分,继承来的项目

第一部分在一个空文件夹上教方法,因为那是学会它最清楚的方式。但在现实生活里几乎没人会给你一个空文件夹:给你的是一个已经在跑的系统,然后让你"把这个修一下"或者"给它加上那个"。课程第 2 部分把同一家诊所放到另一条时间线上,那里有一个负责人的侄子在一个夏天做出来、而没人完整理解的应用,并且教演进-纠正之路的规范化阶段:八节课,把方法所预设的那些输入物恢复出来、造好安全带、把债务排好序,再加两节课,纠正第一个 bug 并用完整的方法构建第一个新功能。

附录 A — 如何评估(给授课者或自评者)

一份简单的评分表,按课,三个级别:

不评估的东西:代码的质量。没人读它。评估的是证据的质量。

附录 B — 课程对各工具的适配

提示词在任何工具里都一样。变的是课程里加到 docs/WORKING_AGREEMENT.md 的那些例行放在哪里:

第 2 课的阅读测试对所有工具都有效:如果一个新会话不知道系统不做什么,工具就没有加载你放契约的那个文件。

附录 C — 这本手册还没有的东西