Lesson 0008 · Engineering · 人和 AI 都能启动(model-invoked)· 流程课

prototype:用一次性代码回答一个设计问题

你在写一个状态机:订单可以从「待支付」走到「已支付」,但「超时取消」和「部分退款」同时发生时, 纸上怎么画都觉得对,心里就是没底。或者:设置页到底用侧边栏还是标签页,脑子里三张草图打了一下午的架。 这种问题在对话里争不出结果——它需要一个能跑起来的答案。prototype 干的就是这件事: 写一段一次性代码(throwaway code:从第一天起就打算扔掉的代码), 它的唯一职责是回答这一个设计问题。问题决定代码的形状:问「这个状态模型感觉对吗」, 就造一个可以在终端里用手推着走的小程序;问「这页面该长什么样」,就在同一条路由上 并排摆几个截然不同的界面版本,用浏览器底部的一条浮动栏来回切换。 学完这节课,你能说清楚:怎么选分支、两条分支各自的骨架长什么样、 收尾时原型本体和答案分别落到哪里、行为不对时该改哪个文件的哪一段。

1. 它在整个系统里站在哪

0001 把 22 个已发布 skill 分成三层:配置层、编排层、纪律层。prototype 不在这三层的任何一条 主流程骨架上——ask-matt 把它放在 Standalone(随时伸手用的独立工具)一节: 「a small, throwaway program that answers one design question」,并且明说 「reach for it any time a design question is hard to settle on paper」 (任何时候,只要一个设计问题在纸上定不下来,都可以伸手用它)。 但它同时又是主流程里的一个正式岔路:主流程第 2 步问 「所有问题都能在对话里定下来吗」,如果某个问题需要一个能跑的答案 (状态、业务逻辑、必须亲眼看到的 UI),就从主流程岔出去做 prototype, 两头都用 handoff 摆渡——出去时 handoff 一次、开新会话做原型, 回来时把学到的东西 handoff 回原线程,并在原来的想法线程里引用它。

它是 model-invoked 的(人和 AI 都能启动):frontmatter 里没有 disable-model-invocationagents/openai.yaml 里也没有 allow_implicit_invocation: false。docs 页的说法是 「Type /prototype, or the agent reaches for it automatically when a task fits」—— 你可以显式打 /prototype,AI 在对话里闻到「这个问题纸上定不下来」的味道时也会自己伸手。

主流程(ask-matt 的 step 2 岔路):

  /grill-with-docs 面试中冒出一个需要「能跑的答案」的问题
        │
        │ /handoff ──► 新会话
        ▼
   ┌─────────────┐   答案 + 原型本体
   │ /prototype  │ ────────────────┐
   └─────────────┘                 │
        ▲                          │ /handoff 回原线程
        └──────────────────────────┘
        然后继续主流程:to-spec / to-tickets / implement

Standalone 用法:不挂任何流程,任何时候单独 /prototype。

地图:0001 · 岔路原文:ask-matt/SKILL.md 的 main flow 第 2 步和 Standalone 一节 · 摆渡船:0006 handoff · 启动方式判定:prototype/agents/openai.yaml(没有禁止隐式调用)

2. 它是什么、什么时候用、什么时候别用

2.1 一句话定性:一次性代码,只回答一个问题

SKILL.md 开篇第一句就是定义:「A prototype is throwaway code that answers a question. The question decides the shape.」(原型是回答问题的一次性代码;问题决定它的形状。) 三个词都不能省:一次性——从第一天起就打算扔,不带测试、不带错误处理、不带抽象; 回答问题——它不是产品的雏形,也不是 MVP(最小可行产品),它的产出是一个答案,不是一套功能; 问题决定形状——先想清楚问的是什么,再决定造什么,顺序反了就白做。

prototype ≠ 早期产品 docs 页有句话值得抄在墙上:「the moment you start hardening it, you've stopped prototyping」 (你一开始加固它——补测试、补错误处理、考虑以后扩展——你就已经不是在做原型了)。 原型的价值在速度:用最低的造价换一个「哦,原来如此」。一旦开始心疼这段代码、想把它养大, 它就从「便宜的探针」变成了「贵得扔不掉的负债」。

2.2 什么时候伸手

frontmatter 的 description 给出触发条件:当你想 sanity-check(快速验证) 一个状态模型或一段逻辑感觉对不对,或者想探索一个 UI 该长什么样。展开成具体场景:

情境 该用 prototype 更该去哪
「这个状态机处理 X 然后 Y 的边界情况,我说不清对不对」 (LOGIC 分支:造一个能用手推着走的终端小程序)
「这个 dashboard 提交之前我想看几种方案」 (UI 分支:同一路由并排几个截然不同的版本)
「已经造好的东西行为不对,我想知道为什么」 不该(那是诊断问题,不是设计问题) diagnosing-bugs——docs 页原话:prototype 探索「该造什么」,不查「造出来的东西为什么坏了」
「想法还很模糊,需求和边界都没聊清」 不该(先磨想法,把问题聊出来) grill-with-docs 主流程的面试
「某个第三方 API 的行为我查不到,需要读一手文档」 不该(那是阅读跑腿活,不需要写代码) research(后台 agent 去读一手来源)
「接口形状怎么设计,模块够不够深」 可以用原型验证手感,但词汇和原则在那边 codebase-design(0005 讲过:语言不是流程)
「不知道该走哪条流程」 不该 ask-matt

权威原文:skills/engineering/prototype/SKILL.md · 给人看的叙事版:docs/engineering/prototype.mdaihero.dev/skills-prototype

3. 第一刀:选对分支

SKILL.md 的「Pick a branch」一节只有两条路,由问题本身决定:

怎么判断当前问题属于哪条?按 SKILL.md 的顺序:从你的 prompt 里读; 读不出来就看周围的代码(一个后端模块 → 多半走 LOGIC;一个页面或组件 → 多半走 UI); 还拿不准而你在场——直接问你。为什么这一步不能糊弄: 原文说「getting this wrong wastes the whole prototype」——选错分支,整个原型白做, 因为两条分支产出的东西完全不同(一个是终端程序,一个是路由上的几个界面)。 如果你真的联系不上、问题又真的模糊,默认规则是:看周围代码更像哪边就走哪边, 并且把这个假设写在原型顶部,让你回来第一眼就能看到。

两条分支互不通用 LOGIC.md 里写着:如果问题是「该长什么样」——wrong branch(走错门了),去 UI.md。 UI.md 里写着:如果问题关于逻辑和状态而不是长相——wrong branch,去 LOGIC.md。 两个文件互相把守对方的门。问自己一句「我想按按钮看状态变,还是想并排看几种长相」, 答案就出来了。

4. 两条分支共用的六条规则

不管走哪条分支,SKILL.md 的「Rules that apply to both」一节都适用。 这六条是 prototype 的骨架,逐条拆开看:

# 规则 具体要求
1 Throwaway from day one, and clearly marked(从第一天起就是一次性的,而且要看得出来) 代码放在它真正服务的位置旁边(给哪个模块或页面做原型,就放在它旁边),上下文一目了然;但命名要让随手翻到的人一眼看出这是原型、不是生产代码。UI 的一次性路由要服从项目已有的路由约定,不许发明新的顶层目录结构。
2 One command to run(一条命令跑起来) 用项目已有的任务运行器——pnpm <name>python <path>bun <path> 之类。你必须能不动脑子就启动它。项目没有任务运行器时,把命令写在原型 README 的最顶上。
3 No persistence by default(默认不持久化) 状态活在内存里。持久化往往正是原型要检验的东西,不该是原型依赖的东西。问题明确涉及数据库时,用一个名字写着「PROTOTYPE — wipe me」(用完就清掉)的临时库或本地文件。
4 Skip the polish(不打磨) 不写测试,错误处理只做到「能跑」为止,不做抽象。要点是快速学到东西。
5 Surface the state(把状态亮出来) LOGIC 分支:每次动作之后打印全部相关状态;UI 分支:每次切换变体时完整渲染。你随时能看见「刚才那一下改变了什么」。
6 Capture it when done(做完要收档) 把验证过的决策折进真实代码;原型本体作为一手材料提交到一次性分支(不并入 main);在实现用的 Issue 上留一个指向该分支的上下文指针;答案(结论 + 它解决的问题)也记进 Issue 或 commit。main 分支只保留验证过的决策。详见第 7 节。
六条规则其实是一条 前四条都在压造价(扔得起、跑得起、不带包袱、不打磨),第五条保证「学到东西」这个动作一定发生 (状态不亮出来,推了半天也学不到),第六条保证学到的东西不随代码一起被扔掉。 一句话:便宜地造,清楚地看,认真地收。

5. LOGIC 分支:能用手推着走的状态模型

LOGIC.md 的适用信号是:问题关于业务逻辑、状态迁移、数据形状—— 「在纸上看挺合理,但只有真的推几个 case 才知道感觉对不对」的那种东西。 它的原文例子:「我不确定这个状态机能不能处理 X 然后 Y 的边界情况」、 「这个数据模型到底能不能表达……这种情况」、「我想在动手写之前先摸摸这个 API 的手感」。 共同点:你想按按钮、看状态变

5.1 七步流程

做什么 要点
1. State the question 写代码之前,先用一段话写下:原型要验的状态模型是什么、要回答的问题是什么 写在原型的 README 或文件顶部注释里。答错问题的逻辑原型是纯浪费;把问题写明,日后(你看着的时候,或者 AFK 之后回来)才能核对它答的是不是原来那道题
2. Pick the language 用宿主项目已经在用的语言和工具链 项目没有明显运行时(比如纯文档仓库)就问你。不许为了原型引进新的包管理器或运行时
3. Isolate the logic 把真正回答问题的那段逻辑收进一个可搬走的独立模块:小的、纯的接口,日后能整个拎起来放进真实代码库 TUI(终端界面)那层壳是一次性的,逻辑模块不是。这条决定了原型的价值能不能活过原型本身——见 §5.2
4. Build the smallest TUI 造一个轻量终端界面:每一拍清屏重绘整个画面,而不是不停往下滚 你永远看到一个稳定的画面,不是越来越长的滚动记录。结构见 §5.3
5. One command 在项目已有的任务运行器里加一条脚本(package.json scripts、Makefile、justfile、pyproject.toml) 你跑 pnpm run <prototype-name> 之类就能启动,永远不用记路径
6. Hand it over 把运行命令交给你,由你自己开 最值钱的时刻是你说「等等,这不应该可能发生」或「咦,我原以为 X 会不一样」——那些是想法里的 bug,正是整个原型的目的。想加动作就加,原型是会演化的
7. Capture 问题答完,先收答案,再按 SKILL.md 的方式收原型本体 LOGIC 分支的具体映射:验证过的逻辑模块拎进真实代码(决策被吸收),TUI 壳跟着原型一起去一次性分支当一手材料

5.2 逻辑模块的四种形状,按问题选

第 3 步「把逻辑隔离进可搬走的模块」是整个 LOGIC 分支最值钱的设计决策。 LOGIC.md 给了四种候选形状,选最贴合问题的那种,而不是最好接 TUI 的那种

形状 长什么样 什么时候合用
Pure reducer(纯归约器) (state, action) => state:吃当前状态和一个动作,吐新状态 动作是一个个离散事件,状态是单一值
State machine(状态机) 显式列出有哪些状态、状态之间允许哪些迁移 「当前这个时刻哪些动作根本是合法的」本身就是问题的一部分时
一组纯函数 作用在一个普通数据类型上的若干纯函数 没有隐含的「当前状态」概念,只是数据变换
有清晰方法面的类或模块 逻辑自己持有持续的内部状态,对外只露一组方法 逻辑确实拥有进行中的内部状态时

不管选哪种,都保持:不做 I/O、不碰终端、不用 console.log 当控制流。 依赖方向是单向的:TUI import 逻辑模块并调用它,反方向什么都不流。 回报在第 7 步兑现:问题答完,验证过的 reducer / 状态机 / 函数集可以原样拎进真实模块—— 原型死了,器官捐献给了产品。

和 0005 的接缝对上了 「逻辑收进小的纯接口,壳是一次性的」就是 0005 codebase-design 里 「深模块立在干净接缝上」的一次性版本:接口小到日后能整体搬走, 壳(TUI)和核(逻辑)之间只走这一个接口。如果你在原型阶段就把接缝切对, 收尾时的「拎进真实代码」就是一次移植,而不是一次重写。

5.3 TUI 的画面结构

每一拍(tick)清屏后重绘的整帧画面,按顺序只有两部分:

┌──────────────────────────────────────┐
│ 1. 当前状态                            │
│    友好打印、方便比对(一行一个字段,     │
│    或格式化 JSON)                      │
│    字段名/小节标题用粗体,次要信息用暗色   │
│    (时间戳、ID、派生值)                │
├──────────────────────────────────────┤
│ 2. 键盘快捷键(列在底部)               │
│    [a] add user  [d] delete user      │
│    [t] tick clock  [q] quit           │
│    键名加粗、说明暗色(或反过来,读着顺就行)│
└──────────────────────────────────────┘

样式直接用终端原生的 ANSI 转义码(\x1b[1m 粗体、\x1b[2m 暗色、 \x1b[0m 复位),项目里没有现成的样式库就不要为原型引一个。 行为循环四步:初始化一个内存里的状态对象并渲染第一帧 → 每次读一个按键(或一行输入)、 派发给处理函数改状态 → 每个动作之后整帧重绘(替换,不是追加)→ 循环直到退出。 整帧必须一屏放得下。

5.4 LOGIC 分支的反面模式

6. UI 分支:同一路由上的几个激进版本

UI.md 的适用信号是:问题是「该长什么样」。做法:在同一条路由上生成 几个截然不同(radically different)的 UI 变体,用一条浮在屏幕底部的工具栏切换, 你在浏览器里来回翻,挑一个(或者从每个里偷一点),剩下的扔掉。 原文对时机的判断很到位:任何时候,你如果不这么做、就得花一整天在脑子里比较三个模糊的假想稿—— 那就该做这个原型。

6.1 两种子形状:强烈优先 A

子形状 做法 什么时候用
A — 挂在已有页面上(默认) 路由已经存在;变体在同一条路由上渲染,用 ?variant= 这个 search param 开关。已有的数据获取、路由参数、登录态全部保留,只换渲染的那棵子树 只要有一个说得过去的已有页面能容纳变体,就用它。被原型的东西还没有页面、但天然会长在某个页面里(dashboard 的新板块、设置页的新卡片、现有流程的新步骤)——也算 A:把变体挂载进宿主页面
B — 新开一条一次性路由(最后手段) 按项目已有的路由约定造一条一次性路由,命名里带上 prototype 字样,同样用 ?variant= 切换 只在被原型的东西真的没有家时用:全新的顶层界面、嵌不进任何地方的流程。决定用 B 之前先自问:真的没有任何已有页面能装它吗?

为什么强烈优先 A?UI.md 的理由:UI 原型要贴着真实的应用才好判断——真实的页头、侧边栏、 真实的数据、真实的密度。孤零零的一次性路由是一个真空环境:每个变体在真空里看着都挺好, 空路由会把设计问题藏起来,而有内容的路由会把它们暴露出来。

6.2 六步流程

做什么 要点
1. State the question, pick N 默认 3 个变体;超过 5 个就不再「截然不同」,开始变成噪音——封顶 5 个 用一句话写下计划,放在原型的位置或文件顶部注释里,例如「Three variants of the settings page, switchable via ?variant=, on the existing /settings route.」你在不在场都能被核对
2. Generate radically different variants 逐个起草,每个都要服从三样约束:页面的用途和它能拿到的数据;项目已有的组件库 / 样式体系(TailwindCSS、shadcn、MUI、纯 CSS 都行);清晰的导出组件名(VariantAVariantBVariantC 变体必须结构上不同:布局不同、信息层级不同、主要操作入口不同——不是换颜色。三个微调过的卡片网格不是 UI 原型,是墙纸(原文:wallpaper)。两个草稿太像,就重做其中一个,明确指示「不许用卡片网格」之类
3. Wire them together 在路由上写一个切换组件:读 search param,按值渲染对应变体,最后挂上 PrototypeSwitcher 子形状 A:保留切换器之上全部已有的数据获取,只有被渲染的子树随变体换。子形状 B:一次性路由挂同一个切换器
4. Build the floating switcher 底部居中的固定位置小条,三件东西:左箭头(循环到上一个,绕回)、变体标签(当前键 + 名字,如「B — Sidebar layout」)、右箭头(循环到下一个,绕回) 行为细则见 §6.3。做成一个共享组件,两种子形状复用,放在项目里公共 UI 该在的地方
5. Hand it over 把 URL 和 ?variant= 的键告诉你,你有空就翻 最值钱的反馈往往是「我要 B 的页头配 C 的侧边栏」——那才是你真正想要的设计
6. Capture and clean up 变体决出胜负后:收答案(哪个变体、为什么),再按 SKILL.md 收原型 子形状 A:赢家折进已有页面,输家和切换器从 main 里删掉。子形状 B:赢家提升为正式路由,一次性路由和切换器从 main 里删掉。全套变体是一手材料,去一次性分支,不进回收站——留在 main 里的变体组件和切换器会快速腐烂、迷惑后来的读者

6.3 浮动切换器的行为细则

6.4 UI 分支的反面模式

7. 收尾纪律:原型是一手材料,不进 main

六条共用规则里的第 6 条(Capture it when done)值得单独一节,因为它是这个 skill 和其它「随便写写试试」之间最大的差别:原型做完不是删掉,是收档。 docs 页把收档的产出拆成两样东西:

收什么 收到哪 为什么
答案——结论(verdict)加上它解决的那个问题 耐久的地方:commit message、ADR(架构决策记录)、或 Issue 答案是原型真正产出的东西,要能被以后的人(和 AI)查到
原型本体——那份能跑的代码 一条一次性分支:提交进去、离开 main、永不合并;然后在实现用的 Issue 上留一个指向这条分支的上下文指针 原型是答案的一手材料(primary source:答案所依据的原始证据)。它没有测试、没有错误处理、不值得维护,所以不属于 main;但这不是销毁它的理由——想复跑证据的人,点一下就能拿到
收档全景:

   原型答完了问题
        │
        ├─► 验证过的决策 ──折进──► 真实代码(main 只留这个)
        │     · LOGIC:逻辑模块拎进真实模块(正经重写/移植)
        │     · UI:赢家变体折进已有页面或提升为正式路由
        │
        ├─► 答案(结论 + 问题)──写进──► Issue / commit / ADR
        │
        └─► 原型本体 ──提交到──► 一次性分支(off main,never merged)
                                   实现用的 Issue 上留指针
烂在 main 里的原型 = 过期的负债 docs 页的对仗句:「A prototype left rotting in the main branch has outlived its purpose — a prototype captured as a primary source on a side branch hasn't.」 (留在 main 里腐烂的原型已经活过了它的意义;收在侧边分支上当一手材料的原型没有。) 判断标准很简单:main 里只允许出现「验证过的决策」,不允许出现「探索的过程」; 探索的过程去分支,指针留在 Issue 上。

出处:SKILL.md 规则 6 · docs/engineering/prototype.md 的「Keep the prototype as a primary source」一节 · 分支各自的映射:LOGIC.md 第 7 步、 UI.md 第 6 步

8. 谁在上游、谁在下游

                 想法线程(主流程)
              /grill-with-docs 面试中
              冒出一个「纸上定不下来」的问题
                        │
              /handoff ─┼─► 新会话 ─► /prototype
                        │                 │
                        │            答案 + 一次性分支
                        │                 │
                        ◄── /handoff ─────┘
                        │
                        ▼
        ┌───────────────┼────────────────────┐
        ▼               ▼                    ▼
    /to-spec      /domain-modeling      直接回主流程
  把验证过的状态    若决策不可逆,         (甚至 /implement)
  模型或 UI 方向    记一条 ADR
  当已确定的输入写进 spec
Skill 启动 和 prototype 的关系
handoff User 双向摆渡船。ask-matt 主流程第 2 步的原话:岔出去做原型时 handoff 一次、开新会话对着那份文件干活;做完把学到的东西 handoff 回原线程,并在原想法线程里引用它。为什么不就地做?原型是个自成一体的探索,配一个干净的上下文窗口。
to-spec User 最常见的下游。docs 页原话:验证过的状态模型或 UI 方向,成为 to-spec 落笔时「已确定的输入」。spec 里写的是结论,原型分支的指针是证据。
domain-modeling Model 原型验出来的决策如果难以逆转(比如选定了某个状态机语义),值得按 0004 的规矩记成一条 ADR。
codebase-design Model 提供词汇:LOGIC 分支「逻辑收进小的纯接口、壳可扔」就是深模块 / 接缝那套语言的一次性应用。想先想清楚接口形状再造原型,可以先踩这块词汇地板。
diagnosing-bugs Model 分工对偶:prototype 探索「该造什么」,diagnosing-bugs 查「造好的东西为什么坏了」。docs 页互相点名,别混。
research User 另一条 standalone 岔路,但产出不同:research 交出一份带引用的 Markdown(读出来的一手资料),prototype 交出一份能跑的代码(推出来的手感)。一个问题靠「读」解决就走 research,靠「跑」解决就走 prototype。
ask-matt User 路由:在它的地图里,prototype 同时是主流程 step 2 的岔路和 Standalone 名单的一员——「reach for it any time」。

9. 会留下什么、用完接什么、想微调改哪里

9.1 副作用清单

动作 会写/改什么 说明
造原型(任一分支) 仓库里新增一次性代码:放在被原型对象的旁边,命名标明是原型;UI 分支可能改已有页面(子形状 A 的切换器挂载) 规则 1:贴着使用位置放;规则 4:不带测试和错误处理
让它一条命令能跑 改项目已有的任务运行器配置(package.json scripts、Makefile、justfile、pyproject.toml),加一条脚本 项目没有任务运行器就只写 README
问题涉及数据库时 新建一个名字写着「PROTOTYPE — wipe me」的临时库或本地文件 规则 3:默认内存;持久化是被检验对象,不是依赖
收尾(规则 6) ① 真实代码:折入验证过的决策(移植或正经重写)② 一次性分支:提交原型本体,离开 main、永不合并 ③ Issue / commit / ADR:写答案(结论 + 问题)和指向分支的指针 ④ 从 main 删掉原型残留(UI 分支:输掉的变体和切换器) main 只留验证过的决策;探索过程留在分支上,指针留在 Issue 上
不会动的 Issue tracker 的结构(不改标签、不开新 Issue 类型)、CONTEXT.md(除非走 domain-modeling)、main 分支的产品行为(决策折入之前) 它是 standalone:不写 CONTEXT.md、不动 triage 那套标签体系

9.2 用完之后,下一步去哪

  1. 从主流程岔出来的/handoff 把答案带回原线程 → 继续主流程(多半进 /to-spec,把验证过的结论当已确定的输入)。
  2. 决策不可逆 → 走 domain-modeling 记一条 ADR,把「为什么是这个状态机 / 这个方向」钉下来。
  3. standalone 单独用的 → 答案收进 Issue 或 commit,原型收进一次性分支,然后回到你手头的工作。
  4. 答完问题发现想法本身有裂缝 → 回 /grill-with-docs 重新磨——原型暴露了想法里的 bug,这是胜利,不是返工。

9.3 行为不对时,去改哪个文件的哪一段

症状 先查 不要误改
AI 造出来的东西带着测试、抽象、错误处理全家桶 SKILL.md 规则 4「Skip the polish」;docs 页「the moment you start hardening it…」 LOGIC.md 的 TUI 画面细则(那是形状,不是造价)
选错分支——给状态问题造了 UI,或给长相问题造了终端程序 SKILL.md 的「Pick a branch」一节(含模糊时的默认规则和「把假设写在顶部」) 两个分支文件的正文(分支选错是上游问题)
逻辑原型做完了,逻辑搬不走——和终端代码糊成一团 LOGIC.md 第 3 步(四种形状、保持纯、单向依赖)和反面模式「Don't blur the logic and the TUI together」 TUI 的重绘细则
UI 变体彼此雷同,只是换皮 UI.md 第 2 步「structurally different」和「wallpaper」警告 切换器的键盘行为
原型孤零零开在真空路由里,看不出好坏 UI.md 的「Two sub-shapes」一节:优先 A,B 是最后手段 变体数量默认值(3)
浮动栏跟着生产构建发出去了 UI.md 第 4 步的 NODE_ENV 开关要求 search param 的命名
原型做完直接进了 main,或者做完随手删了没留档 SKILL.md 规则 6;docs 页「primary source」一节 分支各自的流程步骤
AI 从不主动伸手用它 SKILL.md frontmatter 的 description(「Use when…」触发条件) openai.yaml(那里只有展示名和简介,不管触发)

10. 检索练习

先别往回翻,凭记忆答。选项长度刻意对齐,不会从版式泄题。规则编号和措辞以本课和 SKILL.md / LOGIC.md / UI.md 原文为准。

自测(立即反馈)

1. SKILL.md 对 prototype 的一句话定义是?
2. 用户在 grill-with-docs 面试中冒出一个需要「能跑的答案」的问题,ask-matt 规定的走法是?
3. 「我不确定这个状态机处理 X 然后 Y 的边界情况」——该走哪条分支、造什么?
4. LOGIC 分支要求把逻辑收进「可搬走的模块」,其直接回报是?
5. UI 分支为什么强烈优先「挂在已有页面上」(子形状 A)?
6. 关于收尾(规则 6),下列哪种做法符合本 skill?
7. 下列哪条是 UI.md 点名的反面模式?
8. 「已经造好的功能行为不对,想查原因」——该去哪里?
额外提取练习(无选项) 合上本页,默写六条共用规则(throwaway 且标明、一条命令、默认不持久化、不打磨、亮出状态、做完收档), 再说出每条规则防的是什么翻车。然后想一个你当前项目里「纸上定不下来」的问题, 说出你会选哪条分支、问题一句话怎么写、原型放在哪个位置、跑它的那条命令会挂在哪里—— 不用真的做,把决策链走通就算过关。

11. 下一课与一手材料

本课主一手材料(请打开原文读,不要只背本页摘要):

速查页(本课同步): reference/prototype.html

导航: 上一课 0007 research (另一条 standalone 岔路:一个交 Markdown,一个交能跑的代码)。 总览仍回 0001 系统地图; 摆渡船见 0006 handoff; 下游接 0009 to-spec

建议下一课(0009 to-spec): 原型把「感觉对吗」变成了「验证过」——下一课看 to-spec 怎么把这种已确定的结论 (连同面试记录、研究文件)写成一份能拆成 Issue 的 spec。 交接点:spec 的 Implementation Decisions 一节写的就是这类已敲定的决策, 而原型分支的指针就是它们的证据附件。

老师就在会话里。 对本课任何一处有疑问——比如「问题涉及持久化时到底开不开数据库」、 「子形状 A 和 B 的边界案例」、「逻辑模块搬走后 TUI 壳还值不值得留在分支上」——直接在对话里问。 回答会回到 SKILL.md / LOGIC.md / UI.md 的原文,不会临场编造。 做完检索练习后,回复「练习结果 / 哪里卡住 / 开 0009 或先补 0007」,我们安排下一课。