@namewta/speculo 0.2.9 → 0.2.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -79,12 +79,15 @@ Every workflow ships an `INDEX.md` as its auto-generated work catalog. Work entr
|
|
|
79
79
|
|
|
80
80
|
## Acknowledgments — Honoring Open Source Heritage
|
|
81
81
|
|
|
82
|
-
Speculo stands on the shoulders of pioneers. With deep gratitude, we honor:
|
|
82
|
+
Speculo stands on the shoulders of pioneers — including our own failures. With deep gratitude, we honor:
|
|
83
83
|
|
|
84
|
+
- **[SpecForge](https://github.com/NAMEWTA/specforge)** — the author's own previous project. A CLI-driven SDD tool whose failure taught us the most important lesson: in the AI era, documents are the interface, not CLI commands. Making humans learn commands to manage AI documents gets the relationship backwards.
|
|
84
85
|
- **[Matt Pocock Skills](https://github.com/mattpocock/skills)** — the groundbreaking work that defined AI-assisted development workflows and inspired the very concept of packageable agent skills.
|
|
85
86
|
- **[Khazix Skills](https://github.com/KKKKhazix/khazix-skills)** — a rich ecosystem of practical agent skills that demonstrated the power of community-driven workflow sharing.
|
|
87
|
+
- **[OpenSpec](https://github.com/Fission-AI/OpenSpec)** — a lightweight spec-driven development framework whose changes/ directory structure and archive mechanism deeply influenced Speculo's persistence contract design.
|
|
88
|
+
- **[Superpowers](https://github.com/obra/superpowers)** — a complete agentic development methodology whose skill orchestration and subagent dispatch provided key reference for workflow package design.
|
|
86
89
|
|
|
87
|
-
Speculo
|
|
90
|
+
Speculo synthesizes lessons from all: from failure we learned "documents are the interface"; from Matt we inherited skill methodology; from OpenSpec we adopted engineering management; from Superpowers we studied orchestration. Together they form package-based workflow management, persistence contracts, and a unified install/migrate lifecycle. We carry their spirit forward.
|
|
88
91
|
|
|
89
92
|
## License
|
|
90
93
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# 逻辑原型
|
|
2
|
+
|
|
3
|
+
构建一个微小的交互式终端应用,让用户手动驱动状态模型。当问题涉及**业务逻辑、状态转换或数据形态**时使用——这类问题在纸面上看起来合理,但只有推进真实用例后才会暴露出不对劲的地方。
|
|
4
|
+
|
|
5
|
+
## 适用场景
|
|
6
|
+
|
|
7
|
+
- "我不确定这个状态机能否处理先 X 后 Y 的边界情况。"
|
|
8
|
+
- "这个数据模型真的能表示那种情况吗……"
|
|
9
|
+
- "我想在写之前先感受一下 API 应该长什么样。"
|
|
10
|
+
- 任何用户想要**按按钮、观察状态变化**的场景。
|
|
11
|
+
|
|
12
|
+
如果问题是"这个应该长什么样"——选错了分支。用 [UI.md](UI.md)。
|
|
13
|
+
|
|
14
|
+
## 流程
|
|
15
|
+
|
|
16
|
+
### 1. 陈述问题
|
|
17
|
+
|
|
18
|
+
在写代码之前,写下你正在为哪个状态模型和哪个问题做原型。一段话即可,放在原型的 README 或文件顶部的注释中。回答了错误问题的逻辑原型是纯粹浪费——让问题显式化,这样之后可以核查,无论用户是现在看着还是稍后 AFK 回来再看。
|
|
19
|
+
|
|
20
|
+
### 2. 选择语言
|
|
21
|
+
|
|
22
|
+
使用宿主项目所用的语言。如果项目没有明显的运行时(如文档仓库),则询问。
|
|
23
|
+
|
|
24
|
+
遵循项目已有的工具链约定——不要仅为原型引入新的包管理器或运行时。
|
|
25
|
+
|
|
26
|
+
### 3. 将逻辑隔离到一个可移植模块中
|
|
27
|
+
|
|
28
|
+
将实际逻辑——回答问题的部分——放在一个小巧、纯净的接口后面,使其之后可以被提取并放入正式代码库。围绕它的 TUI 是一次性的;逻辑模块不应该是一次性的。
|
|
29
|
+
|
|
30
|
+
正确的形态取决于问题:
|
|
31
|
+
|
|
32
|
+
- **纯 reducer**——`(state, action) => state`。适用于动作为离散事件且状态为单一值的场景。
|
|
33
|
+
- **状态机**——显式的状态和转换。适用于"当前哪些操作是合法的"本身就是问题的一部分。
|
|
34
|
+
- **一组纯函数**操作一个纯数据类型。适用于没有隐式当前状态、只有转换的场景。
|
|
35
|
+
- **类或模块**——具有清晰方法接口,当逻辑确实拥有持续性内部状态时使用。
|
|
36
|
+
|
|
37
|
+
选择最适合所问问题的形态,而*不是*最容易接入 TUI 的形态。保持纯净:无 I/O、无终端代码、无用于控制流的 `console.log`。TUI 导入它并调用它;反向不传递任何内容。
|
|
38
|
+
|
|
39
|
+
这就是让原型在自身生命周期之后仍有价值的关键:当问题得到回答后,验证通过的 reducer / 状态机 / 函数集可以被单独提升到正式模块中。
|
|
40
|
+
|
|
41
|
+
### 4. 构建最小的 TUI 来暴露状态
|
|
42
|
+
|
|
43
|
+
将其构建为**轻量 TUI**——每次 tick 清屏(`console.clear()` / `print("\033[2J\033[H")` / 等价方式)并重新渲染整个帧。用户应始终看到一个稳定视图,而非不断增长的滚动回溯。
|
|
44
|
+
|
|
45
|
+
每帧包含两部分,顺序如下:
|
|
46
|
+
|
|
47
|
+
1. **当前状态**,pretty-print 且 diff 友好(每行一个字段,或格式化 JSON)。使用**粗体**标注字段名或节标题,**暗色**标注次要上下文(时间戳、ID、派生值)。原生 ANSI 转义码即可——`\x1b[1m` 粗体、`\x1b[2m` 暗色、`\x1b[0m` 重置。无需引入样式库,除非项目中已经存在。
|
|
48
|
+
2. **键盘快捷键**,列在底部:`[a] 添加用户 [d] 删除用户 [t] 推动时钟 [q] 退出`。粗体标键、暗色标描述,或反过来——怎么读起来清晰怎么来。
|
|
49
|
+
|
|
50
|
+
行为:
|
|
51
|
+
|
|
52
|
+
1. **初始化状态**——单个内存中的对象/结构体。启动时渲染第一帧。
|
|
53
|
+
2. **每次读取一次按键(或一行)**,分发到修改状态的处理器。
|
|
54
|
+
3. **每次操作后重新渲染**完整帧——不追加,而是替换。
|
|
55
|
+
4. **循环直到退出。**
|
|
56
|
+
|
|
57
|
+
整个帧应适配一屏。
|
|
58
|
+
|
|
59
|
+
### 5. 一条命令即可运行
|
|
60
|
+
|
|
61
|
+
向项目已有任务运行器添加一条脚本(`package.json` scripts、`Makefile`、`justfile`、`pyproject.toml`)。用户应运行 `pnpm run <原型名称>` 或等价命令——永远不需要记住路径。
|
|
62
|
+
|
|
63
|
+
如果宿主项目没有任务运行器,直接把命令写在原型 README 的顶部。
|
|
64
|
+
|
|
65
|
+
### 6. 交付
|
|
66
|
+
|
|
67
|
+
给用户运行命令。他们会自己驱动它;有趣的时刻是他们说"等等,那不应该可能"或"嗯,我以为 X 会不一样"——那些是_想法_中的 bug,这正是整个原型的目的。如果他们想添加新操作,就添加。原型会演化。
|
|
68
|
+
|
|
69
|
+
### 7. 捕获答案并持久化
|
|
70
|
+
|
|
71
|
+
原型回答问题后,按 [SKILL](SKILL.md) 中持久化约定的方式捕获答案:
|
|
72
|
+
|
|
73
|
+
1. **提升验证过的逻辑**:将验证通过的 reducer / 状态机 / 函数集提升到正式模块中(决策已被吸收)。
|
|
74
|
+
2. **持久化答案记录**:在 `<Path>{roots.state}/<workflow>/changes/{change}/prototype/logic-<topic>.md</Path>` 创建答案文件,记录:
|
|
75
|
+
- 所回答的问题
|
|
76
|
+
- 结论——什么可行、什么不可行
|
|
77
|
+
- 被验证的逻辑模块的描述
|
|
78
|
+
- throwaway 分支指针(TUI 外壳代码所在位置)
|
|
79
|
+
3. **更新索引**:将新答案追加到 `prototype/index.md` 表格中。
|
|
80
|
+
|
|
81
|
+
TUI 外壳代码仍提交到 throwaway 分支——它是一次性的交互壳,真正有价值的部分(逻辑模块)已经提升到正式代码中。
|
|
82
|
+
|
|
83
|
+
## 反模式
|
|
84
|
+
|
|
85
|
+
- **不要加测试。** 需要测试的原型不再是原型。
|
|
86
|
+
- **不要接入真实数据库。** 使用内存存储,除非问题本身就是关于持久化的。
|
|
87
|
+
- **不要泛化。** 不要"如果我们以后想支持 X 呢"。原型只回答一个问题。
|
|
88
|
+
- **不要把逻辑和 TUI 混在一起。** 如果 reducer / 状态机引用了 `console.log`、提示符或终端转义码,它就不可移植了。让 TUI 成为纯模块外面的薄壳。
|
|
89
|
+
- **不要把 TUI 外壳发布到生产环境。** 外壳是为在终端中手动驱动而优化的。背后的逻辑模块才是值得保留的部分。
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: prototype
|
|
3
|
+
description: 构建一个一次性原型来回答设计问题。当用户想要快速验证某个状态模型或逻辑是否正确,或探索 UI 应该长什么样时使用。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 原型
|
|
7
|
+
|
|
8
|
+
原型是**回答问题的 disposable 代码**。问题决定形态。
|
|
9
|
+
|
|
10
|
+
## 选择分支
|
|
11
|
+
|
|
12
|
+
确定正在回答哪个问题——从用户的提示、周围代码中推断,或用户在场时直接询问:
|
|
13
|
+
|
|
14
|
+
- **"这个逻辑 / 状态模型对吗?"** → [LOGIC.md](LOGIC.md)。构建一个微小的交互式终端应用,推动状态机经过那些在纸面上难以推理的用例。
|
|
15
|
+
- **"这个应该长什么样?"** → [UI.md](UI.md)。在单个路由上生成几个截然不同的 UI 变体,通过 URL 查询参数和底部浮动栏切换。
|
|
16
|
+
|
|
17
|
+
两条分支产生的产物截然不同——选错会浪费整个原型。如果问题确实模糊且无法联系用户,默认选择与周围代码更匹配的分支(后端模块 → logic;页面或组件 → UI),并在原型顶部声明假设。
|
|
18
|
+
|
|
19
|
+
## 通用规则
|
|
20
|
+
|
|
21
|
+
1. **从第一天起就是 disposable,并明确标注。** 将原型代码放在离实际使用位置近的地方(紧邻它正在为哪个模块或页面做原型),这样上下文一目了然——但命名要让随便一个读者都能看出这是原型而非生产代码。对于 disposable UI 路由,遵循项目已有的路由约定,不要发明新的顶层结构。
|
|
22
|
+
2. **一条命令即可运行。** 使用项目已有任务运行器支持的方式——`pnpm <名称>`、`python <路径>`、`bun <路径>` 等。用户必须能不加思考就启动它。
|
|
23
|
+
3. **默认无持久化。** 状态存在于内存中。持久化是原型正在_检查_的东西,而非原型应该依赖的东西。如果问题明确涉及数据库,用一个临时库或本地文件,名称要清楚标注"PROTOTYPE — 可随时清除"。
|
|
24
|
+
4. **跳过打磨。** 不写测试,不做超出让原型_可运行_范围的错误处理,不建抽象。目的是快速学习。
|
|
25
|
+
5. **展示状态。** 每次操作后(logic)或每次变体切换时(UI),打印或渲染完整的相关状态,让用户能看到什么发生了变化。
|
|
26
|
+
6. **完成后捕获结论。** 将验证通过的决策融入正式代码。然后将答案和结论持久化到变更目录:
|
|
27
|
+
- 在 `<Path>{roots.state}/<workflow>/changes/{change}/prototype/</Path>` 下创建答案文件
|
|
28
|
+
- 维护 `prototype/index.md` 索引表
|
|
29
|
+
- 原型代码本身仍为一次性代码:提交到 throwaway 分支,保持脱离主分支。答案文件中记录该分支的引用指针
|
|
30
|
+
- 具体持久化规范见下方「持久化约定」章节
|
|
31
|
+
|
|
32
|
+
## 持久化约定
|
|
33
|
+
|
|
34
|
+
### 产物位置
|
|
35
|
+
|
|
36
|
+
原型答案写入当前 change 目录下的 `prototype/` 子目录:
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
<Path>{roots.state}/<workflow>/changes/{change}/prototype/<type>-<topic>.md</Path>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
- `<type>` 为 `logic` 或 `ui`,对应原型分支类型
|
|
43
|
+
- `<topic>` 为 kebab-case 主题名,概括原型所回答的问题,如 `auth-state-machine.md`、`settings-page-layout.md`
|
|
44
|
+
- `<workflow>` 为当前 workflow 目录名(如 `specdev`)
|
|
45
|
+
- `<change>` 为当前活跃变更目录名(格式 `<YYYY-MM-DD>-<topic>`,从 `<Path>{roots.state}/<workflow>/status.json</Path>` 的 `active` 数组中获取)
|
|
46
|
+
|
|
47
|
+
### 答案文件内容
|
|
48
|
+
|
|
49
|
+
每个答案文件包含以下信息:
|
|
50
|
+
|
|
51
|
+
- **问题**:原型所回答的具体问题
|
|
52
|
+
- **结论**:验证后的结论——什么可行、什么不可行、为什么
|
|
53
|
+
- **验证内容**(仅 logic 原型):被验证的 reducer / 状态机 / 函数集的描述
|
|
54
|
+
- **UI 评估记录**(仅 UI 原型):哪个变体胜出及原因、各变体的结构差异分析、从落选变体中提取的有价值元素
|
|
55
|
+
- **原型代码引用**:throwaway 分支名称,指向原型代码所在的 git 分支
|
|
56
|
+
|
|
57
|
+
### 维护 prototype/index.md
|
|
58
|
+
|
|
59
|
+
在 `prototype/` 目录下维护一个索引文件 `<Path>{roots.state}/<workflow>/changes/{change}/prototype/index.md</Path>`,仅包含一张表格:
|
|
60
|
+
|
|
61
|
+
| 类型 | 文件 | 问题概述 | 结论摘要 |
|
|
62
|
+
|------|------|---------|---------|
|
|
63
|
+
| logic | `auth-state-machine.md` | 认证状态机能否正确处理 token 过期 + 并发刷新 | 可行;需增加 TOKEN_EXPIRED 中间态 |
|
|
64
|
+
| ui | `settings-layout.md` | 设置页三种布局方案对比 | B 方案(侧边栏布局)胜出;吸收 C 的面包屑导航 |
|
|
65
|
+
|
|
66
|
+
- 表格四列:类型(`logic` / `ui`)、文件(`prototype/` 下的相对路径)、问题概述(一句话概括)、结论摘要(一句话概括结论)
|
|
67
|
+
- 每次新增答案文件后,向表格追加一行
|
|
68
|
+
- `index.md` 除表格外无需其它内容
|
|
69
|
+
|
|
70
|
+
### 去重与增量更新
|
|
71
|
+
|
|
72
|
+
在开始新原型之前:
|
|
73
|
+
|
|
74
|
+
1. 先读取 `<Path>{roots.state}/<workflow>/changes/{change}/prototype/index.md</Path>`,检查是否已有同名或高度相关的原型记录
|
|
75
|
+
2. 如已存在对应 `.md` 文件,先读取其完整内容
|
|
76
|
+
3. 如现有结论已覆盖当前问题,直接引用,无需重复原型
|
|
77
|
+
4. 如需更新(新发现补充、结论修正),在原文件基础上增删改,并同步更新 `index.md` 中对应行的概述
|
|
78
|
+
5. 如需回答全新问题,创建新文件并追加到 `index.md` 表格
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# UI 原型
|
|
2
|
+
|
|
3
|
+
在单个路由上生成**几个截然不同的 UI 变体**,通过底部浮动栏切换。用户在浏览器中翻看变体,选一个(或从每个中偷一些元素),然后丢弃其余。
|
|
4
|
+
|
|
5
|
+
如果问题是关于逻辑/状态而非界面外观——选错了分支。用 [LOGIC.md](LOGIC.md)。
|
|
6
|
+
|
|
7
|
+
## 适用场景
|
|
8
|
+
|
|
9
|
+
- "这个页面应该长什么样?"
|
|
10
|
+
- "我想在提交之前看几个仪表盘方案。"
|
|
11
|
+
- "给设置页试一种不同的布局。"
|
|
12
|
+
- 任何用户本来会在脑子里花一天时间在三个模糊线框图之间犹豫不决的场景。
|
|
13
|
+
|
|
14
|
+
## 两种子形态 —— 强烈偏好子形态 A
|
|
15
|
+
|
|
16
|
+
UI 原型在**与应用的其余部分产生摩擦**时才最容易评判——真实的 header、真实的 sidebar、真实的数据、真实的信息密度。单独的一次性路由是真空:每个变体在隔离状态下看起来都不错。只要有合理的现有页面可以承载变体,就默认使用子形态 A。只有当原型确实没有邻近的宿主时才使用子形态 B。
|
|
17
|
+
|
|
18
|
+
### 子形态 A — 调整现有页面(首选)
|
|
19
|
+
|
|
20
|
+
路由已存在。变体在**同一路由**上渲染,通过 `?variant=` URL 查询参数控制。现有的数据获取、参数和认证全部保留——只替换渲染部分。这是默认选项;除非有明确的理由不这样做,否则选它。
|
|
21
|
+
|
|
22
|
+
如果原型针对的东西还没有页面,但*自然地应该存在于某个页面内部*(仪表盘的新区域、设置页的新卡片、现有流程中的新步骤)——这仍然是子形态 A。将变体挂载在宿主页面内部。
|
|
23
|
+
|
|
24
|
+
### 子形态 B — 新建页面(最后手段)
|
|
25
|
+
|
|
26
|
+
仅当被原型化的事物确实没有现成页面可以嵌入时使用——例如一个全新的顶层界面,或一个无法合理嵌入任何地方的流程。
|
|
27
|
+
|
|
28
|
+
按照项目已有的路由约定创建一个**一次性路由**——不要发明新的顶层结构。命名要让人一眼看出是原型(例如在路径或文件名中包含 `prototype` 字样)。同样使用 `?variant=` 模式。
|
|
29
|
+
|
|
30
|
+
在提交子形态 B 之前,做一个合理性检查:真的没有现成页面可以嵌入吗?空路由会隐藏有内容的页面能够暴露的设计问题。
|
|
31
|
+
|
|
32
|
+
两种子形态下,底部浮动栏完全相同。
|
|
33
|
+
|
|
34
|
+
## 流程
|
|
35
|
+
|
|
36
|
+
### 1. 陈述问题并确定变体数量 N
|
|
37
|
+
|
|
38
|
+
默认 **3 个变体**。超过 5 个就不再是截然不同,而是噪音——以此为上限。
|
|
39
|
+
|
|
40
|
+
将计划写在一行内,放在原型所在位置或文件顶部注释中:
|
|
41
|
+
|
|
42
|
+
> "设置页的三个变体,通过 `?variant=` 切换,在现有 `/settings` 路由上。"
|
|
43
|
+
|
|
44
|
+
无论用户是否在场反对,这都能成立。
|
|
45
|
+
|
|
46
|
+
### 2. 生成截然不同的变体
|
|
47
|
+
|
|
48
|
+
起草每个变体。每个变体必须满足:
|
|
49
|
+
|
|
50
|
+
- 页面的目的和它能访问的数据。
|
|
51
|
+
- 项目的组件库 / 样式系统(TailwindCSS、shadcn、MUI、纯 CSS,等等)。
|
|
52
|
+
- 清晰的导出组件名,例如 `VariantA`、`VariantB`、`VariantC`。
|
|
53
|
+
|
|
54
|
+
变体必须在**结构上不同**——不同的布局、不同的信息层次、不同的主要操作入口,而不仅仅是不同的颜色。三个微调过的卡片网格不是 UI 原型,是壁纸。如果两份草稿太相似,用明确的"不要用卡片网格"指引重做其中一个。
|
|
55
|
+
|
|
56
|
+
### 3. 将它们串接起来
|
|
57
|
+
|
|
58
|
+
在路由上创建一个单一的切换器组件:
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
// 伪代码 —— 根据项目框架调整
|
|
62
|
+
const variant = searchParams.get('variant') ?? 'A';
|
|
63
|
+
return (
|
|
64
|
+
<>
|
|
65
|
+
{variant === 'A' && <VariantA {...data} />}
|
|
66
|
+
{variant === 'B' && <VariantB {...data} />}
|
|
67
|
+
{variant === 'C' && <VariantC {...data} />}
|
|
68
|
+
<PrototypeSwitcher variants={['A','B','C']} current={variant} />
|
|
69
|
+
</>
|
|
70
|
+
);
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
对于子形态 A(现有页面):将现有数据获取保持在切换器上方;每个变体只替换渲染的子树。
|
|
74
|
+
|
|
75
|
+
对于子形态 B(新建页面):`/prototype/<名称>` 下的一次性路由挂载同一个切换器。
|
|
76
|
+
|
|
77
|
+
### 4. 构建浮动切换器
|
|
78
|
+
|
|
79
|
+
一个位于屏幕底部中央的固定定位小栏,包含三个元素:
|
|
80
|
+
|
|
81
|
+
- **左箭头**——切换到上一个变体(循环)。
|
|
82
|
+
- **变体标签**——显示当前变体标识,如果变体导出了名称,也显示名称。例如 `B — 侧边栏布局`。
|
|
83
|
+
- **右箭头**——切换到下一个(循环)。
|
|
84
|
+
|
|
85
|
+
行为:
|
|
86
|
+
|
|
87
|
+
- 点击箭头更新 URL 查询参数(使用框架的路由器——Next 上用 `router.replace`、React Router 上用 `navigate`,等等),使变体可分享且在刷新后保持。
|
|
88
|
+
- 键盘:`←` 和 `→` 方向键也可切换。当 `<input>`、`<textarea>` 或 `[contenteditable]` 元素聚焦时不要拦截方向键。
|
|
89
|
+
- 在视觉上与页面区分(如高对比度胶囊形、微妙阴影),使其明显不是被评估的设计的一部分。
|
|
90
|
+
- 在生产构建中隐藏——通过 `process.env.NODE_ENV !== 'production'` 或等价检查进行门控,这样即使原型不小心合入也不会把切换器发布给用户。
|
|
91
|
+
|
|
92
|
+
将切换器放在一个共享组件中,供两种子形态复用。放置在项目中共享 UI 组件的通常位置。
|
|
93
|
+
|
|
94
|
+
### 5. 交付
|
|
95
|
+
|
|
96
|
+
给出 URL(以及 `?variant=` 的各个键值)。用户会在有空时翻看。最有趣的反馈通常是**"我想要 B 方案的头和 C 方案的侧边栏"**——那才是他们真正想要的设计。
|
|
97
|
+
|
|
98
|
+
### 6. 捕获答案并清理
|
|
99
|
+
|
|
100
|
+
一旦某个变体胜出,按 [SKILL](SKILL.md) 中持久化约定的方式捕获答案:
|
|
101
|
+
|
|
102
|
+
1. **融入正式代码**:
|
|
103
|
+
- **子形态 A** — 将胜出变体融入现有页面;从主分支移除落选变体和切换器。
|
|
104
|
+
- **子形态 B** — 将胜出变体提升为正式路由;从主分支移除一次性路由和切换器。
|
|
105
|
+
2. **持久化评估记录**:在 `<Path>{roots.state}/<workflow>/changes/{change}/prototype/ui-<topic>.md</Path>` 创建答案文件,记录:
|
|
106
|
+
- 所回答的 UI 问题
|
|
107
|
+
- 哪个变体胜出及原因——完整的评估推理
|
|
108
|
+
- 各变体的结构差异分析
|
|
109
|
+
- 从落选变体中提取的有价值元素(如果适用)
|
|
110
|
+
- throwaway 分支指针
|
|
111
|
+
3. **UI 规范沉淀**:将评估过程中产生的 UI 规范洞察(如布局原则、信息层次、交互模式选择理由)写入答案文件,供后续 spec 编写引用。
|
|
112
|
+
4. **清理原型代码**:将完整变体集(包括落选变体和切换器)提交到 throwaway 分支,不进入主分支。变体组件和切换器留在主分支会快速腐烂并误导后续读者。
|
|
113
|
+
5. **更新索引**:将新答案追加到 `prototype/index.md` 表格中。
|
|
114
|
+
|
|
115
|
+
## 反模式
|
|
116
|
+
|
|
117
|
+
- **变体仅颜色或文案不同。** 那是微调,不是原型。真正的变体在结构上存在分歧。
|
|
118
|
+
- **变体之间共享过多代码。** 共享一个 `<Header>` 没问题;共享一个 `<Layout>` 就失去了意义。每个变体应该能够自由地抛弃布局。
|
|
119
|
+
- **将变体接入真实的数据变更。** 只读原型完全没问题。如果变体需要变更数据,将其指向一个桩——问题是"这个应该长什么样",不是"后端是否正常工作"。
|
|
120
|
+
- **将原型直接提升到生产环境。** 变体代码是在原型约束下编写的(无测试、最小错误处理)。融入时要正确重写。
|