# Nomi — 工程纪律

> 单一文档。**上半部「速览」每次 session 读完再动手**；下半部「详细规则 R1–R14」按编号随时查。
> （原 `CLAUDE-RULES.md` 已并入本文件，不再分两份。）
> `docs/coding-standards.md` - 通用编码规范与实现风格补充。

## 项目概览

Nomi：本地优先 AI 视频创作工作台。
**技术栈**：Electron + React 18 + Tailwind 3 + Zustand + Vercel AI SDK。
**主要模块**：项目库 → 创作（文本）→ 生成画布（节点系统）→ 时间轴预览 → 导出 MP4。
**设计系统**：`Design.md` + `src/design/`，token-only，光模式，密度优先。
**工作树**：`/Users/aoqimin/Desktop/Nomi/`，分支 `main`，直接在 main 上 commit + push。

## 常用命令

| 命令 | 用途 |
|---|---|
| `pnpm dev` | 开发模式启动（Vite + Electron） |
| `pnpm build` | Vite 构建 + electron tsc |
| `pnpm run test` | Vitest 单测 |
| `pnpm run test:e2e` | Playwright smoke（零额度，CI-ready） |
| `pnpm run lint:ci` | Lint + max-warnings=98 棘轮（新增 1 个 warning 即红）|
| `pnpm run typecheck` | TypeScript 双向类型检查 |
| `pnpm run check:filesize` | 巨壳文件门岗 |
| `pnpm run check:audit` | 审计节奏提醒（≥25 commit 提示） |

**Push 前必须全过**：`check:filesize` → `lint:ci` → `typecheck` → `test` → `build`

## 五条核心原则

**P1 加新必删旧** — 引入新实现时同 commit 删旧实现，无并行版、无 fallback、无逃生口。CSS 同理：新样式只写组件 `className`，迁 Tailwind 即删旧 CSS；全局 CSS 只可减不可增。

**P2 修根因不修症状** — 看到 bug 先分：症状 / 根因 / 这类 bug 的入口集。修在根因层，让整类不再复发，配结构保证（测试/不变量）。自检：「修完后这个问题还能从别的入口出现吗？」答不出"不能" = 没到根因。

**P3 全绿 ≠ 完成** — CI 五门只证代码健康，证不了体验对不对。用户可见改动报完成前：① 和获批样张逐项并排对账；② 真体感走查（Playwright 截图人眼判断，不是 expect 断言）。缺一不算完成。

**P4 通用第一** — 能力/组件/交互按「模型身份 / 通用场景」设计，与具体供应商/模型解耦。不为不同模型写两套 UI（那是并行版，违反 P1）。档案声明槽，通用系统负责填。

**P5 想清楚再动手** — UI 改动**先读设计系统 `docs/design/nomi-design-system.md`（token/组件/规范）再画**，再出可视样张（HTML mockup）+ 用户拍板；架构改动先查 Context7 官方文档 + 读顶尖开源代码 + 6 角色评审；多文件改动先写 `docs/plan` 文档。

## 动手前自检

1. **在修 bug？** 挖到根因了吗，还是在让现象消失？症状/根因/类分清了吗？（→ P2）
2. **被现有代码框住了？** 从「这东西本该是什么」推解法，而不是从「它现在怎么写」推。
3. **hardcode 了？** 随输入变的东西必须 derive，不能钉死常数。
4. **造第二份真相源 / 并行版了？** 有没有现成可复用的？（→ P1）
5. **旧的删了吗？** 加新 = 删旧，CSS 同理。（→ P1）
6. **碰第三方库？** 先查 Context7 官方文档，不凭记忆手搓。（→ R5）
7. **用户可见改动？** **先读设计系统 `docs/design/nomi-design-system.md`（token/组件/规范）再画**，再出样张；token-only（禁非 token 的 px/hex）；渐进展开 / 防遮挡 / 空间极简。（→ R8）
8. **准备 push？** 五门全过了吗？（→ R11）
9. **要报完成？** 和样张对账过了吗 + 体感走查过了吗？（→ P3）

## 规则索引（详见下方「详细规则 R1–R14」）

| # | 规则 | 一句话 |
|---|---|---|
| R1 | 加新必删旧 | 新替旧必同 commit 删旧；CSS 只可减不可增（R10 = R1 的 CSS 实例）|
| R2 | 用户视角 + 极简 | 每条信息问「有行动价值吗」，没有删；好产品不靠文字解释 |
| R3 | 决策对比表 | 涉及取舍先给用户对比表（方案/用户看到/代价），不单方面开干 |
| R4 | 执行前写文档 | 多文件/多步改动先写 `docs/plan`：范围/不动项/回滚/验收门 |
| R5 | 查官方文档 | 碰第三方库必先 Context7，不凭记忆；不查就写 = 工作错误 |
| R6 | 读顶尖开源 | 做方案前先读真实代码（Cline/ComfyUI/xyflow…），给出 file:line |
| R7 | 6 角色评审 | 项目方案定稿前：CTO / 设计 / PM / 前端 / 后端 / 真实用户各审一遍 |
| R8 | 先出样张 | 用户可见改动先出 mockup + 用户拍板；实现后必须与样张逐项对账 |
| R9 | 模块化 + 防巨壳 | 写码前想清楚分层；单文件 ≤800 行；白名单巨壳只减不增（R12 = R9 的量化门岗）|
| R10 | → R1 CSS | `src/styles/` 只可减不可增；新样式只写组件 className |
| R11 | 自动 commit/push | 验证通过即自己 commit + push；五门全过才能 commit |
| R12 | → R9 巨壳 | `check:filesize` 门岗；白名单基线只降不升 |
| R13 | 体验走查 | Playwright 走真实用户旅程 J1-J5（创作目标，不是功能探索）；截图人眼判断 |
| R14 | 周期审计 | ≥25 commit 或发版前：多维 subagent 审计 + 走查 + `docs/audit` 文档 |

## 决策自治

**自己定**（做完一句话说明）：实现细节、命名、模块拆法、测试策略、bug 修复顺序。

**才问用户**（AskUserQuestion，合并成一轮，给推荐项）：产品方向/不可逆取舍 / 架构岔路（影响大、多个合理解）/ 需要用户独有资源（API key、额度、真实素材）。

**遇到样张/需求自相矛盾**：停下上报，不许自己挑一条实现。

## 工作目录

唯一工作树：`/Users/aoqimin/Desktop/Nomi/`，分支 `main`。
操作文件用绝对路径；若用 worktree 将来扩展分支，务必放在仓库目录**同级**（非嵌套）。

---

# 详细规则 R1–R14

> 上面是速览，这里是细节。规则索引触发对应编号后到这里查。

## R1 加新必删旧（No Parallel Versions）

引入新组件/新流程替代旧的，必须同一 commit 删除被替代的旧代码。

- 不留"逃生口 / Collapse / 备选 fallback"——这些都是借口
- 死代码（grep 不到外部引用）立刻物理删除，不留"以防万一"
- 旧的有点价值 → 把价值合并进新代码，再删旧
- **CSS（R10 = R1 的 CSS 实例）**：新样式一律用 Tailwind utility 写在组件 `className` 上；不用 `@apply`；CSS 文件分工固定只可减不可增
  - `src/theme/nomi-tokens.css` — 设计 token
  - `src/styles/index.css` — 全局 reset/keyframes
  - `src/styles/vendor-overrides.css` — Mantine 等第三方 DOM 覆盖
  - `src/styles/globals.css` — 只准删，不准加组件样式

## R2 用户视角 + 极简

做 UI/文案/卡片设计前，先问：用户进来要看什么？每条信息有行动价值吗？没有 → 删。

**0 权重嫌疑犯**：节点功能描述文字 / 每次出现的 onboarding 文案 / 重复的分组标签 / 长 error stack 灌满卡片（应缩成 ⚠️ + tooltip）/ 双层 border / 永远 80px 的空信息区。

**好产品不靠解释**：优先让东西本身一眼说明该干嘛（图标/形态/位置），把文字降到最少。审视每条文案："删了它，用户还懂吗？"——懂 → 删。

**词汇 = 模型真名，别替用户翻译**：模式/能力标签用模型自己的叫法（vendor 原词，如「全能参考」）为主——用户已熟悉；自创意图词可能把能力说窄。

## R3 决策格式

涉及范围/取舍时，先给用户对比表，再让用户拍板：

```
| 方案 | 用户看到什么 | 代价 |
|---|---|---|
```

不要单方面"我建议 A"然后开干。样张或 plan 内部有两条「拍板」互相冲突 → 停下上报，不许自己挑一条实现。

## R4 执行前必写文档

涉及多文件/多步骤改动，先在 `docs/plan/` 或 `docs/audit/` 写执行文档：
- 范围
- 不动什么
- 回滚策略
- 验收门

写完用户能预读/反驳；执行完回填结果。

## R5 查官方文档（Context7 强制）

凡涉及第三方框架/库（React Flow、AI SDK、Mantine、Electron、Tiptap、Vite…）的实现或改动，动手前必须先用 Context7 查官方文档。

1. `resolve-library-id` 拿库的 Context7 ID
2. `get-library-docs` 拉相关主题官方文档
3. 对照官方推荐 API/模式实现
4. 官方无此能力时才允许自定义，注释写明理由

**不查就写 = 工作错误**。若 Context7 工具未加载，退回 WebFetch 查对应官方站（等价）。

## R6 读顶尖开源代码

做任何项目方案前，先去 GitHub 找 1~N 个顶尖开源项目读真实代码（不是扫 README），产出：
- 它们怎么做的（具体到文件/代码位置）
- 我们能直接借鉴什么
- 哪里不适用、为什么

参考池（非穷尽）：Cline / OpenHands / Aider / Continue / Cherry Studio / LobeChat；画布向：tldraw / ComfyUI / xyflow。

## R7 6 角色评审

**触发条件**：任何涉及架构/取舍/UX 的项目方案，在给用户拍板前。

并行审查 6 个角色：
1. **CTO** — 架构合理性、技术债、扩展性、与现有内核是否冲突
2. **设计师** — 视觉一致性、对齐 `Design.md`、密度优先
3. **产品经理** — 用户价值、范围是否过大、是否解决真痛点
4. **前端** — 可行性、状态管理、性能、组件复用
5. **后端** — runtime/数据/IPC/持久化影响
6. **真实用户** — 用起来顺不顺、爽不爽、看着舒不舒服

流程：研究开源（R6）→ 起草方案 → 6 角色并行审查 → 汇总修方案 → 对比表（R3）给用户拍板。

## R8 先出可视样张

**触发条件**：任何用户会看到的东西（界面/卡片/节点/面板/交互/文案排版）。

**铁律（实现前必须走完）**：
1. **先读完整设计系统 `docs/design/nomi-design-system.md`**（§2 token 全表 / §3 通用组件 / 规范）+ `src/design/` 现有组件——文档原话"做任何新设计前请先读完整版"；token-only（**禁非 token 的 px/hex/圆角**，如 `h-[34px]`/`text-[12.5px]`/`gap-2.5` 都违规，用 `h-8`/`text-[13px]`/`gap-2`），不凭空造组件
2. 出 HTML mockup（最低成本让用户先看到样子）
3. 设计师 Agent + 真实用户 Agent 审一遍（R7）
4. 用户本人确认后才进入实现
5. **实现后必须与获批样张逐项对账**：截图并排比，列出每一处差异；差异当场补齐或说明暂缓原因 → 样张是验收合同，不是参考图

**设计交付要配齐参考文档**：样张只是「理想态快照」，必须同时交付：① 设计系统 token/组件 ② Context7 库官方文档（R5）③ 数据/代码现状（先 Explore 摸底）④ 实现规范（精确 token + DOM 结构 + 状态 + 数据绑定）。缺参考 = 凭空设计 = 实现卡死/跑偏。

**设计落地 = 规范驱动 + computed style 核对**：改设计前先写/读实现规范；改完跑 `tests/ux/design-fidelity.e2e.mjs`（它把规范写成 computed-style/DOM 结构断言，能抓 twMerge 吞字号 / Mantine 吃样式等隐藏覆盖）；加自定义 Tailwind token 务必同步进 `cn()` 的 `extendTailwindMerge`。

## R9 模块化 + 防巨壳

写码前先想清楚：

- 这块逻辑该放哪一层？（UI / 状态 / 领域逻辑 / runtime / 持久化——别混一个文件）
- 关注点分离了吗？渲染归渲染、状态归 store、领域逻辑归领域层
- 新东西和现有内核的边界在哪？会不会引入第二份真相源（→ R1）

架构决策过：Context7 查官方推荐架构（R5）→ 读顶尖开源分层（R6）→ CTO+前端+后端三角讨论（R7 子集）→ 有取舍出对比表（R3）。

**R12 = R9 的量化门岗**：
- 单个非测试 `.ts`/`.tsx` 文件硬上限 800 行
- 现存巨壳列白名单并记录基线行数，棘轮只减不增
- 门岗命令：`pnpm run check:filesize`（已接入 CI）

## R10 → 见 R1（CSS 实例）

CSS 文件分工与「只可减不可增」规则详见 R1 最后一节。

## R11 自动 commit + push

完成一个有意义的、验证通过的改动就自己 commit + push，不用等用户催。

**验证门槛**：`pnpm build` 绿 + `npx vitest run` 不回归（重大改动按速览「Push 前必须全过」走五门）。

**commit 规范**：
- 一个逻辑改动一个 commit
- Message：做了什么 + 为什么 + 验证结果，中文风格，结尾按 harness 要求挂 `Co-Authored-By:` 行
- 只 `git add` 本次改动文件，不用 `-A`

**例外（先问再 push）**：改动未验证 / 破坏性操作（删历史/force push/发版 tag）/ 用户说先别提交 / 混入多个不相关改动。

## R12 → 见 R9（量化门岗）

白名单巨壳基线与 check:filesize 详见 R9 最后一节。

## R13 穿透式体验走查

**定义**：Playwright `_electron` 驱动真实 app，按真实用户旅程逐步截图，以真实用户视角判断顺不顺、美不美——这是体验穿透，不是功能 pass/fail。

**触发条件（任一）**：
- 用户可见改动报完成前
- 整条功能链路实现完成时
- 用户提出「用不顺 / 看不懂」反馈时
- 重构/重大改动后确认主链路未拆坏
- ≥25 commit 或发版前（配合 R14）

### 旅程构建铁律

**走查旅程必须是真实创作目标，每条要有「任务成功标准」。**

| 错误示范（功能探索）| 正确示范（创作目标）|
|---|---|
| 「查看生成配置面板」 | 「为商品图选好模型，准备生成」|
| 「探索 AI 面板」 | 「用 AI 把故事稿拆成镜头」|
| 「添加新节点」 | 「在画布上补加一个空镜头，填好提示词」|

构建格式：`我有 [输入]，我想得到 [输出]，成功标准是 [可验证的结果]`

### Nomi 5 条标准核心旅程（J1-J5）

| # | 旅程名称 | 输入 | 成功标准 |
|---|---|---|---|
| J1 | **产品宣传视频（主链路）** | 产品文案（可用示例项目）| 文案 → 拆镜头 → 画布节点排布 → 每个镜头选好模型配好参数 → 「可以生成了」|
| J2 | **故事 → 漫画短片（定妆链路）** | 漫画剧本 | 写剧本 → 拆镜头 → 定妆建角色/场景卡 → 角色卡有提示词 → 「可以批量生成了」|
| J3 | **新用户 30 秒上手** | 无（冷启动）| 首页点「30 秒体验」→ 自动创建项目 → 画布展开 → 能说出「这些格子是什么」→ 能点开一个节点看参数 |
| J4 | **参考图驱动生成** | 几张素材图 | 上传图片 → 图出现在素材库/画布 → 作为节点参考图挂好 → 「参数配好，可以生成了」|
| J5 | **修改旧节点并导出** | 已有项目（示例项目即可）| 打开项目 → 找到节点 → 修改 prompt → 知道怎么重新生成 → 进入导出面板 → 知道怎么导出 |

每条旅程走查时问：① 新用户不看文档能走通吗？卡在哪？ ② 每步美吗、顺吗？ ③ 每步到下一步的过渡自然吗？

**发布前必须全部过一遍。**

### 工具栈

- **`tests/ux/ui-driver.mjs` + `tests/ux/ui.mjs` — 常驻交互式驱动（交互探索/调 UI 首选，开一次·边看边点）**：
  后台起 `node tests/ux/ui-driver.mjs`（Bash `run_in_background:true`；**app 启动一次保持开着，不再每步 launch→close 闪屏**），
  就绪后逐步发命令 `node tests/ux/ui.mjs <action>`：
  `snap`（列当前所有可点元素：标签/文字/aria/中心坐标——**据此决定点哪，不靠提前盲猜选择器**）｜
  `shot [名]`（截图到 `tests/ux/shots/<名>.png`，再用 Read 看）｜`click "文字"`（也支持 `aria:` / `css:` / `text:` / `xy:x,y`）｜
  `fill <css> <值>`｜`eval <js>`｜`wait <ms>`｜`quit`（关 app+停驱动）。
  **循环 = snap/shot 看真实界面 → 判断 → click/fill 操作 → 再 shot 看结果**（感知→决策→行动→再感知）。
  Electron 专用（Nomi 要主进程+IPC，普通浏览器预览工具附不上去）。用完务必 `quit`，别留后台进程/窗口。
- `tests/ux/walkthrough.mjs` — 一次性探索式走查（逐步截图 + DOM dump）；**新工作优先用上面的常驻驱动**，盲脚本只在固定流程时用
- `tests/ux/smoke.e2e.mjs`（`pnpm run test:e2e`）— 可断言冒烟，失败即非零退出，CI-ready

**「特别完整的用户测试」标准方法（定稿）**：不引入外部工具——**自主点击的「computer-use 智能体」就是 AI 本身**，驱动层用上面的常驻驱动。标准动作：① 清场（`osascript -e 'quit app "Nomi"'` 关已装 app 释放 single-instance 锁 + 杀残留 Electron/驱动）→ ② `pnpm build` 全新构建（防 stale-chunk 伪 bug）→ ③ 起常驻驱动 → ④ 逐旅程走 J1–J5（snap→判断→click/fill/setfile→shot+Read 人眼判断）→ ⑤ 逐个打开交互态看遮挡 → ⑥ Explore agent 挖根因到 file:line、分症状/根因/地基 → ⑦ 落 `docs/audit`（问题分级 + 局部/地基拆分）→ ⑧ `quit`。完整方法 + 外部工具调研：`docs/workflow/2026-06-10-autonomous-ui-test-method.md`。
**外部工具结论**：Midscene.js 等是最方便的外部自主探索器（支持 Electron CDP/desktop），但需 vision-model 额度（用户资源、要拍板）且不比现有 DOM 感知驱动更准——**常规续用现驱动**，无人值守批量爬再评估接入。

能力边界：渲染层交互 + 感知判断（~90%）用 Playwright 完全覆盖；原生 OS 边界（系统文件对话框/Finder 拖拽）可经 `electronApp.evaluate` stub `dialog.*` 走通。

### UI 有「打开/交互态」的额外要求

任何有打开态的 UI（弹层/面板/菜单/下拉/picker/modal），交付前必须：
1. 真机逐个打开每一个交互态截图，以真实用户视角看「能不能看全 / 会不会被挡」
2. 几何实测：`getBoundingClientRect` 对照祖先 overflow 容器 + 视口，确认不被裁、不溢出、不重叠（含节点在画布边缘等极端位置）；弹层默认放不裁剪层（BodyPortal / 外层锚，仿 `SettingsPopover`），带向上翻转 + 视口 clamp
3. 把上述结果落成可复跑回归断言（`tests/ux/design-fidelity.e2e.mjs`）

## R14 周期性多维审计

**触发条件（任一）**：距上次 `docs/audit/` 审计文档 ≥25 个 main commit（`pnpm run check:audit` 提醒）/ 发布新 minor 版本前 / 巨壳逼近上限 / lint warning 基线明显上涨。

**执行**：
1. 多维 subagent 深审真实代码（R7 6 角色 + 技术栈/架构/测试/产品多维度）
2. Playwright 走查（R13）
3. 落 `docs/audit/<date>-*.md`：现状 + 分级问题（带 file:line）+ 立即/中期/长期路线
4. 清掉 P0，方案级取舍留用户拍板（R3）；关键论断亲自实跑核实

---

## 工作流框架（阶段 × agent 编排）

> 核心三原则：① 独立工作并行、共享文件顺序；② 评审/验证用对抗式多视角（让 agent 挑毛病）；③ UI 收尾必过真实用户体验 agent。范围按事情大小缩放：小改省略中间阶段，项目级大改全走。

| 阶段 | 防的根因 | 用什么 | 过门标志 |
|---|---|---|---|
| 0 调研 | 凭记忆手搓 | Context7（R5）+ 顶尖开源代码（R6）+ Explore agent 摸现状 | 现状盘点（带 file:line）|
| 1 设计/方案 | 想清楚再动手 | 实现规范（HTML 长相 + 精确 token/结构/状态/数据）；架构拉 CTO+前端+后端（R9）| 实现规范文档（R4）|
| 2 方案评审 | 带病开工 | 6 角色评审（R7）+ 对抗评审（专开 agent 挑毛病）；有取舍出对比表（R3）| 评审回填 + 必改项 |
| 3 实现 | 加新不删旧 / 喂巨壳 | 主 loop 顺序实现；互不碰同一文件的独立项 → 多 agent worktree 并行 | 代码 + 单测 |
| 4 逐元素核对 | twMerge/Mantine 隐藏覆盖 | `tests/ux/design-fidelity.e2e.mjs`（computed-style/DOM 结构断言）| 门全绿 |
| 5 交互态视觉收尾 | 遮挡/溢出/重叠（逐元素绿也抓不到）| 真实用户体验 agent + Playwright 逐个打开每个交互态 + 截图 + 几何实测 | 遮挡/溢出回归断言绿 |
| 6 代码评审 | 正确性/复用/效率 | `/code-review` 或评审 agent + 对抗验证（多 agent 各挑一角度）| 评审通过 |
| 7 迭代 | 全绿 ≠ 完成 | 发现问题回到对应阶段 | 全门绿 + 样张/体验对账过 |

**UI 可见改动的最后一道永远是「真实用户体验 agent 视觉走查」（R13 固化）。**

---

## 固化的工作纪律

以下是从实际踩坑中提炼的、不属于任何单一规则的独立纪律。

**接入即验证 + 真实生成 E2E 回路**：一个模型/生成链路不算「接入成功」，直到一次真实 E2E 生成跑通。验证链路：定义真实任务 → 真机驱动 + 主进程埋点（Playwright 渲染层抓不到 vendor HTTP，它在 Electron 主进程发）→ 分层暴露所有问题（UI/交互/配置/传输/渲染）→ 逐个挖根因 → 分级修 → 补可观测 + 锁回归断言。缺 archetype 的模型先补 archetype 再配 mapping，别手配（手配必漂）。参考：`docs/workflow/2026-06-06-real-generation-e2e-loop.md`。

**词汇 = 模型真名，别替用户翻译**：模式/能力标签用模型自己的叫法（vendor 原词）为主。自创意图词可能把能力说窄（「全能参考」写成「角色参考」会让人以为只能放角色）。
