# 交接文档：生成节点「通用化」项目（给下一个 AI）

> **你是接手这个项目的 AI，对之前的对话零记忆。这份文档让你能直接开干。**
> 阅读顺序：**① 本文 → ② `CLAUDE.md`（工程纪律，强制，每会话必读）→ ③ 下面三份 plan → ④ 按需读代码。**
> 工作目录 `/Users/aoqimin/Desktop/Nomi/`，分支 `main`，直接在 main 上提交并 push。

---

## 0. 一句话现状 + 你要做什么

**现状**：内置「模型档案（archetype）」层 + 生成节点 composer 已落地并对齐样张（C0–C4 + 保真，全部 push、
CI 五门绿、598 测试）。**接下来**要把生成节点重构成「**档案声明 + 通用原语组装**」，并建一套**通用「素材引用」系统**
（tile + @ 内联引用 + 三来源统一选择器）。设计 + 架构 + 用户旅途 + 6 角色评审都做完了，**就差按计划执行**。

**第一步该做**：P0 + P1（通用素材原语 + 参考槽归一），**且必须先修掉一个会让真实生成失败的坑**（见 §5 R1）。

> **⚠️ 起点优先级有一处需用户拍板（别自己挑）**：本文「节点重构主轨」从 P0+P1 起；但 §5「旅途盲点」把**接入页留存轨**标为「优先级高于节点重构 / 比节点重构更前置」——这是**独立并行轨**。两条都被标了「优先」，按 CLAUDE.md「歧义/矛盾必停上报」纪律，**接手时第一件事就把「先做 P0+P1 主轨，还是先做接入页留存轨」这个取舍摆给用户拍板**，不要自行选一条开干。

---

## 1. 三份核心 plan（读它们拿全貌，本文只给提要）

- `docs/plan/2026-06-06-composable-node-execution-plan.md` —— **主执行计划**（含 §0 压缩上下文、P0–P5 分块、§4 **6 角色评审回填**=必改项）。**先读这份。**
- `docs/plan/2026-06-06-composable-node-roadmap.md` —— 路线图 + **现状盘点（哪些可复用 / 要替换，带 file:line）** + 用户旅途。
- `docs/plan/2026-06-06-reference-at-and-sources.md` —— 素材引用系统的原语 + 三来源细节。
- 样张：`docs/design/mockups/2026-06-06-reference-at-v4.html`（4 态：tile+@主态 / 打@选择 / 空态 / 统一选择器）。
  渲染图：`tests/ux/shots/MOCKUP-v4.png`、`MOCKUP-picker.png`。**注意：HTML 顶部的说明文字是给评审看的注释，绝不进产品。**

---

## 2. 已落地（别重做，别推翻）

**模型档案层（C0–C4，供应商无关，按模型身份认）**：Seedance 2.0（首帧/首尾帧/全能参考）+ Seedance 2.0 Fast +
HappyHorse（文生/图生/角色参考/视频编辑 4 模式合 1）。
- 档案声明：`src/config/modelArchetypes/{types,index,seedance,happyhorse}.ts`。`resolveArchetypeForModel(model)`
  按 `meta.archetypeId` 或 modelKey/alias 模式匹配，**认不出返回 null**（当前走 flat 兜底；P4 要让它永远返回档案）。
- **传输单源链（已用真实额度验证、绝不能从中间另起一条，见 §5 R-传输）**：
  renderer `archetypeMeta.buildArchetypeInputParams`（据当前模式产 snake input，含 per-mode enum，互斥在此发生）
  → `extras.archetypeInput` → runtime `electron/catalog/archetypeInput.referenceInputParams`（原样采用）
  → kie mapping body（`electron/catalog/kieSeedance.ts` / `kieHappyhorse.ts` 的尾随空格键单源在这）。
- `archetype.transportTaskKind`（'image_to_video'/'text_to_video'）显式声明 mapping 桶（修了 omni 误路由）。

**节点 composer（已对齐样张 v3，命名用 vendor 真名）**：
- `src/workbench/generationCanvas/nodes/NodeGenerationComposer.tsx`（浮动壳：布局/prompt/生成按钮/settings 开合）。
- `NodeParameterControls.tsx`（**当前最大件 ~649 行**，按 section 分发：references/parameters/settings；模型芯片+徽标在这）。
- `controls/`：`ModeBar`（模式分段，**已是纯展示+档案驱动原语**）/ `ReferenceSlots`（数组参考槽原语）/
  `SettingsPopover`（标量参数弹层原语）/ `archetypeMeta`（档案→UI 桥 + 投影）/ `parameterControlModel`（flat 启发式解析，~600 行）。
- **命名铁律**：模式标签用 **vendor 真名**（首帧/全能参考/文生视频…），**不用意图词**（用户拍板，覆盖过样张旧写法）。
  「全能参考」是多模态、别写成「角色参考」（说窄了）。

---

## 3. 已定方向（别推翻，用户已拍板）

**通用「素材引用」系统**（= 不是 Seedance 专用）= 3 原语 + 2 规律，住共享模块 `src/workbench/assets/`：
- **AssetTile**：正方形、形态自明（图缩略图 / 视频缩略图+播放三角+暗蒙 / 音频波形）。
- **AssetPicker**：统一添加（画布 / 项目素材[搜索+最近+浏览全部] / 上传 / 拖入 / 连线），一个「+」点开就是它。
- **AssetMention**：描述里 `@` 或**点 tile** → 缩略图进句子；**发送前才转 `character1..N`，用户永不可见 character1**。
- 规律：**快速取（弹层搜+最近）vs 全量浏览（素材面板）**；**形态自明 > 文字解释**。
- **别混淆**：P1 的 `AssetReference.tsx` **不是第 4 个通用原语**——它是**节点侧的 slot 组件**（吃档案 slots、消费上面 3 原语、替换旧 `ReferenceSlots`），住节点 `controls/`，**不进** `assets/` 共享模块。通用原语永远只有 3 个。

**完整用户旅途（整个架构的收口）**：① 接入（放文档+API）→ ② 识别分层（**一切皆档案**：认得用精修档案 / 认不出用
文档**派生**的档案，**永不裸奔**）→ ③ 统一使用（节点=档案+原语组装）。关键：flat 启发式从「渲染兜底」翻转成
「**接入时派生引擎**」→ UI 端永远只认档案。

---

## 4. 执行计划（P0–P5）+ 6 角色评审结论（全员 GO-WITH-CHANGES）

阶段（细节见执行计划 §2/§3）：**P0** 通用素材原语 → **P1** 参考槽三套归一（生成参考第一个消费方）→
**P2** 模型切换原语+单轨 meta → **P3** 文本节点档案化 → **P4** onboarding→档案桥（旅途入口，最难）→ **P5** 节点壳收编+巨壳净减。

**产品定调**：别把 6 段当不可分割大项目。**先发 P0+P1**（唯一用户可感知 + 风险最低 + 还最重重复债），跑通收反馈再分批。
P4 高风险窄受众、缓做。P2/P3 见缝插针（用户基本无感）。

---

## 5. 关键约束 / 必踩清单（6 角色评审交叉命中，**执行前务必读**）

- **R1（最致命，会让真实生成失败）`nomi-local://` 素材 vendor 取不到**：上传/项目文件素材 URL 是 `nomi-local://`，
  runtime 把参考 URL 原样发 vendor、**无 local→remote 上传**。统一素材池一上就炸。**P0/P1 定契约**：`AssetRef` 区分
  「渲染 URL」vs「传输 URL」；**发送前在 runtime 侧**把 nomi-local 上传成 vendor 可达 URL（走已有 `hardenedFetch`
  私网拦截 + 200MB 上限）。证据：`electron/runtime.ts`（`writeAsset`/`importRemoteAsset`/`taskTemplateParams`）。
- **R-传输（别动中间）**：§2 那条传输单源链是「代码即合同」，重构只能动两端（UI 原语在上、vendor body 在下），
  **绝不在中间另起参考组装路径**。
- **R2 P4 拆分 + 不落库**：派生档案**按需算+进程缓存、不入库**（落库=陈旧档案债=第二真相源，违背总纲）。拆
  **P4a**（`deriveArchetypeFromCatalogMeta` 纯函数，内部调现有 `parameterControlModel` 启发式）/ **P4b**（缓存，可缓做）。
  **硬门 = 逐键等价快照测试**：派生档案产出的 archetypeInput/slots/params 必须和旧 flat 路径**键级相等**；尤其删
  `catalogTaskActions.buildReferenceExtras` 的 flat else 兜底前，否则老模型首尾帧**静默丢传**。删的永远是「UI 第二条路径」，
  **保的是解析/派生能力本体**（别把派生引擎当死代码误删→认不出的模型会裸奔）。
- **R3 必补样张（先出样张再写码）**：P5 满配节点合成（四原语同屏垂直节奏=验收合同）、P4 派生档案（真实「认不出模型」
  渲染，证明不是 snake_case Swagger）、P2 ModelPicker、P3 文本节点。
- **R4 派生档案视觉兜底**：参数 >N 折叠进「高级」、snake_case 展示层美化（title-case，真名留传输层）、出样张审。
- **R5 AssetReference 声明式 slot 描述符**：slot 声明 `{cardinality:single|array, persistAsEdge, form:tile|textRef|singleImage}`，
  原语按它分叉渲染。**数组绝不变持久边**（崩 `(target,mode)` 唯一性、回归全能参考；M6 的 meta-only 要保留）。
  文本节点「选中文本」槽是 `textRef` 形态、不是 tile。
- **R6 @ 编号单源 + Tiptap**：@ 投影（prompt 文本）和参考槽有序 URL 数组**在同一纯函数一次产出**（否则两套编号漂移），
  复用 `archetypeMeta` 已有的 `characterIndexed` ①②③ 语义。**Tiptap 能做、不用 spike**（官方 inline atom node +
  `ReactNodeViewRenderer` + Mention/suggestion，且 Tiptap 已是项目依赖、已在用 `WorkbenchEditor`/`useNomiRichTextEditor`）。
  prompt 现在是纯 `<textarea>`（NodeGenerationComposer），换 Tiptap 是真替换（换了删旧、别两套并存）。
- **R7 P2 最安全**：只删**写侧**双轨（`imageModel/videoModel`），**读侧保留 `||imageModel/videoModel` 兜底 + legacy backfill**，
  0 migration；`adapters/modelOptionsAdapter.ts` 实测 **13 个死导出可删**。runtime（catalogTaskActions）已 `modelKey` 优先。
- **assetPool 别建新 store**：用 useMemo 派生 selector 合流（画布 store 切片 + `useWorkspaceFiles`，后者已 limit 500），
  Map 去重 O(n)、最近切前 N、全量走虚拟滚动；否则就是「第二份真相源」。
- **节点级 onDrop**（P0.4）：现在没有；加时要 `onDragOver+preventDefault+dropEffect='copy'` + `stopPropagation`
  （否则要么 drop 不触发、要么既加参考又被画布 onDrop 建新卡）；三种 drag payload 共用一套解析。

**🔴 旅途盲点（真实用户最痛，独立并行轨，优先级高于节点重构）**：整条路最劝退的是第一步**「贴接入文档 URL」**——
非技术创作者做不到、进门就走。用户要：**模型列表直接点（别贴 URL）/ 最多填 key（配截图）/ 先空手玩起来尝甜头**。
接入页 = `src/ui/onboarding/`（`OnboardingWizard`/`OnboardingDrawer`/`OnboardingFloatingPanel`）。**建议作为并行轨先评估**。
徽标：模板/通用/角色/vendor 真名多套 → design system 登记一套 pattern；**精修档案不打标、只克制标派生**，别逼用户懂内部两档。

---

## 6. 工程纪律（强制，违反即工作错误 —— 全在 `CLAUDE.md`，每会话必读）

- **全绿 ≠ 完成**：CI 五门只证明代码健康，**catch 不到设计保真/体感**。用户可见改动报「完成」前，必须 ①
  **真渲染样张并排逐项对账**（规则 8 AFTER 步，不是拿脑中印象）② **真体感走查**（规则 13，看截图比样张，不是 `expect(元素存在)`）。
- **设计/UI 工作自己主动跑评审**：动 UI/定方向时**自动**起设计师 + 真实用户 agent 审（规则 7/8），不等用户点名。大方案过 6 角色。
- **好产品不靠解释**：UI 一堆名称/说明=设计没做好；优先形态自明，文字降到最少。
- **通用第一（能力层 + 交互层）**：档案按模型身份认、与供应商解耦；UI/交互原语通用、谁用谁声明槽——别给单模型/功能写专用版（=规则 1 并行版）。
- **加新必删旧**（规则 1）；**先想模块化/架构**（规则 9）；**碰库先查官方文档**（规则 5，Context7 或 WebFetch）+ **读顶尖开源**（规则 6）；
  **歧义/矛盾必停上报**（别自己挑一条）；**文件 ≤800 行、巨壳白名单只减不增**（规则 12，`pnpm run check:filesize`）。
- **每个完整改动自己 commit + push**（规则 11，结尾带 `Co-Authored-By`）；**push 前跑全套 CI 门**（见 §7）。
- **Context7 可能没接通**：R1（`hardenedFetch`）、R6（Tiptap）都触发规则 5「碰库先查官方文档」。但 Context7 MCP 常因项目级 `.mcp.json` 未审批而**不可用**（`✓ Connected` 只是可达性、不代表工具可调）——若 `ToolSearch "resolve-library-id"` 找不到工具，跑 `/mcp` 批准，或**直接退回 WebFetch 查官方站**（`tiptap.dev` 等，等价），别卡在审批上。

---

## 7. 怎么验证 / 命令 / 测试 harness

**CI 五门（push 前必全绿，别漏 lint:ci 的 max-warnings 棘轮）**：
```
pnpm run check:filesize     # 文件体积（≤800，巨壳白名单基线只减不增）
pnpm run lint:ci            # eslint --max-warnings=98（当前 94，棘轮）
pnpm run typecheck          # tsc 渲染层 tsconfig.app.json + electron tsconfig
npx vitest run             # 当前 598 测试
pnpm run build              # vite + electron tsc
```
> **「598 测试 / 94 warning」是 2026-06-06 快照，以实测为准、别当硬指标**——隔几天接手看到不同数字是正常的。真正的硬线是**棘轮门槛**（`max-warnings=98`、filesize 基线只减不增），不是当时的具体计数。

**走查 harness（`tests/ux/`，真实 Electron，零额度）**：
- `smoke.e2e.mjs`（`pnpm run test:e2e`）：主链路冒烟，重构后必跑。
- `archetype-modebar.e2e.mjs`：模式切换/参考槽/设置弹层 可断言走查（17 断言）。
- `journey.mjs`：完整创作旅程逐步截图（**真体感判断用**，截图存 `tests/ux/shots/journey-*.png`，含 `-node` 节点特写）。
- `seedance.e2e.mjs` / `happyhorse.e2e.mjs`：**花额度真实生成**，`KIE_API_KEY` 门控、**先问用户**（默认跳过）。
- **渲染样张并排比**：用 `chromium`（已装 headless）开 mockup HTML 截图，和 app 节点特写对账。

---

## 8. 立即下一步（建议起点）

0. **先建绿色基线（动任何代码前）**：`pnpm install` → 跑一遍 §7 CI 五门，确认**干净检出本来就全绿** + `git log/git status` 看现状。没有这个基线，后面任一门变红就分不清是不是自己改坏的。
1. **读** `CLAUDE.md` + 执行计划 §0/§2/§4。
2. **主轨 P0**：建 `src/workbench/assets/`（assetPool 派生 selector + AssetTile + AssetPicker），**含 R1 的 AssetRef 渲染URL/传输URL 区分 + 发送前 runtime 上传契约**。先出/复用 picker 样张对账。
3. **P1**：AssetReference 声明式 slot 驱动（R5）合并三套参考 UI；@ 引用接 Tiptap（R6，编号单源）；样张 v4 对账 + 走查。**先发、收反馈。**
4. **并行高优**：评估接入页留存（贴文档 URL → 点模型/先空手玩）。
5. 之后分批 P2（R7）/ P3 / P4a（派生纯函数+逐键等价快照，R2/R4 视觉兜底+样张）/ P4b / P5（满配样张 R3）。

**每一步**：先想架构/模块化 → 用户可见先出样张 + 自跑设计师/用户 agent 审 → 写码 → CI 五门 → 真渲染样张对账 + 走查 → commit/push。
**真实生成花额度先问用户。歧义/矛盾停下来上报。**
