用单文件可共享 HTML 原型验证状态模型:skills 仓库 prototype/LOGIC 实战指南
【免费下载链接】skillsSkills for Real Engineers. Straight from my .agents directory.项目地址: https://gitcode.com/GitHub_Trending/skills13/skills
在业务逻辑、状态迁移与数据结构这一类"纸面上看起来合理、推演真实用例时才觉得不对劲"的问题上,与其反复口头推演,不如把状态模型做成一个可点击驱动的单文件 HTML 演示,交给非开发者(设计师、产品经理、领域专家)亲手"玩"一遍。本指南以 skills 仓库prototype技能的 LOGIC.md 为核心,完整讲解如何构建这种逻辑原型——从问题声明、纯逻辑模块隔离、页面布局到交付与归档——并补充 SKILL.md、prototype 文档 与 CHANGELOG.md 中的实现依据。读完你将掌握一套可复用的逻辑原型方法论,能用一次双击就能运行的 HTML 文件验证任何状态机、reducer 或数据变换逻辑。
逻辑原型是什么:一个可分享的演示
逻辑原型(Logic Prototype)的产物是一个自包含的 HTML 文件(一份"可分享的演示"),任何人都能通过点击按钮来驱动一个状态模型。它针对的问题是业务逻辑、状态迁移或数据形状:这类问题往往"在纸上看起来合理,只有把它推到真实用例里走一遍才感觉不对"。
这份文件的三个关键特性决定了它的价值:
- 单文件、零安装:纯 HTML/CSS/JS,没有框架、没有打包器、没有服务器,所有内容内联,双击即可打开,甚至可以放进邮件转发;
- 说领域语言而非代码语言:按钮和状态展示都使用业务术语,让设计师、PM、领域专家"亲自感受模型",而不是读 reducer 代码;
- 可被非开发者驱动:交付对象不需要克隆仓库、不需要安装运行时,这正是 prototype 文档 强调的取舍——终端应用只能被"克隆了仓库并装有运行时"的人驱动,这恰好排除了原型最需要征求意见的那批人;而一个自包含文件可以被任何人双击驱动。
从 CHANGELOG.md 的 1.2.0 版本记录可以看到这一演进脉络(PR #763):逻辑分支从"终端应用"改为"单个可共享 HTML 文件",核心动机正是"终端应用把设计师、PM、领域专家排除在外";而底层的纯逻辑模块保持不变,仍然是最终可以搬进真实代码的那部分。
何时该用逻辑原型:识别正确的形状
以下场景适合选择逻辑原型:
- "我不确定这个状态机能不能处理 X 然后 Y 这种边界情况";
- "这个数据模型真的能让我表达……这种情况吗";
- "在写 API 之前,我想先感受一下 API 应该长什么样";
- 任何"想按按钮、看状态变化"的诉求。
关键的分支判定在 SKILL.md 中有明确定义:原型由问题决定形状——"这个逻辑/状态模型感觉对吗?"走 LOGIC 分支,产出一个带自由操作按钮与标签化引导走查的单文件 HTML;而"这看起来应该是什么样?"走 UI.md 分支,产出同一路由下可切换的多套差异化 UI 变体。如果问题关乎"外观",那是 UI 分支而不是逻辑分支。两个分支产出截然不同的工件,选错分支会浪费整个原型;如果问题确实模棱两可且用户不在场,则按周围代码匹配的分支默认处理(后端模块→逻辑,页面或组件→UI),并在原型顶部写明假设。
五步流程构建逻辑原型
第一步:写出问题声明
写代码之前,先把"要验证什么状态模型、回答什么问题"写下来——一段话,放在演示页顶部(可见的引言区域,而不是只放在注释里)。
这段文字是原型的"锚点":一个回答了错误问题的逻辑原型是纯粹的浪费,所以要让问题显式化,以便之后随时核对——无论用户此刻在旁观看,还是之后才回来(AFK)查阅。这与 prototype 文档 的验收标准一致:"你能用一句话说出这个原型要回答的问题,而且它写在演示顶部,而不只是在你脑子里。"
第二步:把逻辑隔离进可移植的纯模块
把真正的逻辑(回答问题的核心那部分)放进一个<script>块,写成一个小而纯的模块——它必须能够被"拎出来"直接放进真实代码库。页面外壳是一次性的;这个模块不是。
模块的形态取决于问题:
| 形态 | 签名/特征 | 适用场景 |
|---|---|---|
| 纯 reducer | (state, action) => state | 动作是离散事件、状态是单一值 |
| 状态机 | 显式状态与迁移 | 问题的核心包含"现在到底哪些动作是合法的" |
| 一组纯函数 | 作用在普通数据类型上 | 没有隐式的当前状态,只有变换 |
| 类/带清晰方法面的模块 | 明确的 method surface | 逻辑确实拥有持续的内部状态 |
选择最适合问题的形态,而不是最容易接到页面的形态。保持它的纯粹性:不碰 DOM、不碰document、按钮处理器不得深入其内部。页面单向调用它,绝不允许反向流动——这正是原型能超越自身生命周期的原因:问题一旦被回答,经过验证的 reducer / 机器 / 函数集可以原样升格进真实模块。这对应 codebase-design 所倡导的"深模块"理念:大量行为封装在小型接口背后,测试/驱动都通过接口进行。
第三步:构建可分享的 HTML 文件
一个文件、纯 HTML/CSS/JS,没有框架、没有打包器、没有服务器,全部内联,双击可开、可被邮件转发。任何人打开即可运行。
为非开发者而写:每个标签都用领域语言而非代码语言,按钮和状态读起来像业务本身,而不是 reducer;用平实的语言解释正在发生什么。
页面自上而下采用清晰的层级:
- 标题与一句话说明:解释这个演示让你探索什么(即第一步的问题声明);
- 当前状态面板:完整呈现相关状态,渲染成可读的面板(带标签的字段,而不是原始 JSON dump),每次点击后重新渲染,让变化可见;在有助于非开发者理解时,明确指出"刚刚改变了什么";
- 自由操作按钮:每个动作一个按钮、始终可用,任何人都能以任意顺序"戳"模型;每次点击分发对应 action 并重新渲染状态;
- 引导走查(Scenarios):一组场景,每个场景一个标签页。每个标签页包含一段简短的平实语言场景描述(它构建了什么情境、要注意观察什么),下方是该场景有序的按钮序列。每一步都是真实按钮:点击执行该动作并进入下一步。启动一个走查时重置到已知初始状态,保证场景每次都按相同方式运行。
场景的选择要有策略:优先演示那些在纸上难以推演的不自然情况——happy path、一个棘手的边界用例、一次对"应该非法"操作的尝试。
外观保持"美观但克制":干净的排版、充裕的留白、一种强调色。不要动画、不要花哨——没有任何东西应该与状态和按钮争夺注意力。
第四步:交付
把文件发给对方,或当面打开。他们会在方便时点击走查和自由操作;最有价值的时刻是当他们说出**"等等,这不应该是可能的"或"咦,我原本以为 X 会不一样"**——这些正是"点子本身"的 bug,而这正是原型的全部意义所在。如果对方想要新的动作或新场景,就加上——原型是不断演化的。
第五步:捕获答案与原型
一旦原型回答了问题,先捕获答案,再按 SKILL 描述的方式捕获原型本体。逻辑分支的映射关系:
- 经过验证的 reducer / 机器 / 函数集升格进真实模块——这是被吸收的"决定";
- HTML 外壳随原型归档到一次性分支,保留原型作为一手资料(primary source);由于它是单一自包含文件,在那里始终可以轻易地重新运行。
这一归档机制在 prototype 文档 中有更完整的展开:答案(结论 + 它所解决的问题)被持久化捕获——提交信息、ADR 或实现 issue 中;原型本身作为"答案的可运行证据"提交到 main 之外的prototype/<name>一次性分支,永不合并,并在实现 issue 上留下指向该分支的上下文指针(context pointer)。main 保持干净,探索成果却仍然可查找、可重跑。这也是对"原型不是用来删的"这一观念的澄清:废弃(throwaway)是写作方式的约束,而不是销毁的承诺——它永远不合并进 main,这一点没有变,变的只是代码存放的位置。
反模式清单:六条红线
逻辑原型有六条明确的反模式,LOGIC.md 原样列出:
- 不要加测试。需要测试的原型就不再是原型了。这对应 prototype 文档 的通用规则"跳过打磨":没有测试、没有超出'可运行'所需的错误处理、没有抽象——目标是快速学到那一件事。
- 不要接真实数据库。除非问题特指持久化,否则使用内存状态。SKILL.md 的通用规则同样强调:默认无持久化,状态驻留内存;持久化是被原型"检查"的对象,而不是它应该依赖的东西;若问题确实涉及数据库,才使用带明确"PROTOTYPE,用完擦掉"命名的临时库或本地文件。
- 不要泛化。不要"如果我们以后想支持 X 呢"。原型只回答一个问题。
- 不要把逻辑和页面糊在一起。如果纯模块引用了 DOM、
document或按钮处理器,它就不再可升格。页面只是纯模块之上的薄壳。 - 不要引入框架、打包器或服务器。收件人双击就能运行的一个文件;React 应用或 dev server 会毁掉"可分享"。
- 不要把 HTML 外壳发布到生产环境。页面是为人工点击优化的一次性外壳,其背后的逻辑模块才是值得保留的部分。
从源码看逻辑原型的定位
在 skills 仓库中,prototype是一个model-invoked技能(见 skills/engineering/README.md 的 "Model-invoked" 列表),这意味着 Agent 可以在任务匹配时自动使用它。其 agents/openai.yaml 给出了 Codex 界面的元数据:display_name: "Prototype"、short_description: "Prototype to answer a design question",与 SKILL.md 的 frontmatter 描述("Build a throwaway prototype to answer a design question")完全对应。
从 CHANGELOG.md 可以看到逻辑分支的演化证据:
- 1.2.0(PR #763):逻辑分支的产物重塑为"单个可共享 HTML 文件"——带标签的状态面板、始终可用的自由操作按钮、一组标签化的引导走查;可移植的纯逻辑模块仍然升格进真实代码,HTML 外壳才是被丢弃的部分。原型不再等于删除,而是作为可运行证据归档到
prototype/<name>分支。 - 1.0.0:
prototype技能改为 model-invoked,描述围绕"prototype = 回答设计问题的一次性代码"改写,每个分支各有一个触发场景(状态/逻辑 sanity-check,或 UI 探索)。
它最大的消费方是 wayfinder:一张 wayfinder 地图由决策 ticket 组成,prototype是四类 ticket 之一——用于"应该长什么样 / 应该怎么表现"这种任何讨论都解决不了的阻塞性问题。原型 ticket 由答案解决,原型本身作为资产链接在地图上。
判定逻辑原型是否有效的检查清单
综合 prototype 文档 的 "It's working if" 标准,一个成功的逻辑原型应当满足:
- 你能用一句话说出它存在的目的,且这句话写在演示顶部,而非只在脑中;
- 不读代码的人也能驱动它——打开文件、在走查标签页里按按钮,用自己的话描述所见;
- 有人说出"等等,这不应该是可能的"或"咦,我原本以为 X 是这样"——这是点子的 bug,正是全部意义所在;
- 一次坐下就完成回答。如果一天后还在构建它,说明问题太大了,需要拆分;
- 结束时 main 只包含决定、不包含原型,实现 issue 指向仍保留原型的分支。
小结
逻辑原型把"难以在纸面上推演的状态模型"变成"任何人双击即可驱动的一个 HTML 文件":顶部写清问题声明,逻辑隔离进可移植的纯模块,页面用领域语言渲染状态面板、自由操作按钮和标签化引导走查,交付给非开发者获取真实反馈,最后把验证过的逻辑升格进真实代码、把演示归档到一次性分支。它的每一步都在向一个目标收敛:用最低的成本,在写真实代码之前发现"点子"里的 bug。
相关资源:核心方法论见 LOGIC.md;分支选择与通用规则见 SKILL.md 与 UI.md;完整背景与常见问题见 prototype 文档;技能在技能体系中的定位见 skills/engineering/README.md 与 CHANGELOG.md。
【免费下载链接】skillsSkills for Real Engineers. Straight from my .agents directory.项目地址: https://gitcode.com/GitHub_Trending/skills13/skills
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考