# KAP「高频动作首页直达」P1×4 整改修复报告

- **修复日期**: 2026-09-16
- **分支**: `feat/ui-direct-action-fix`(自 feat/yue2-full HEAD 89b46319 新建;基线脏文件 562 行已记 `/tmp/kimi_fix_baseline.txt`,零触碰——commit 后 tree 与基线逐字节一致)
- ** commits **:
  - `3544c3b4` fix(ui-direct-action): P1×4(8 文件,+388/-10)
  - `ee87383c` fix(ui-direct-action): P2×2(2 文件,+24/-4)
- **白名单符合性**: 全部 10 个改动文件 ∈ `packages/infinite-canvas/src/components/**` + `packages/portal/src/pages/**` + 组件直引 css,白名单外零 diff。

---

## P1-1 分镜时间轴:审核判定行级直达 ✅

- **改动**:
  - `packages/infinite-canvas/src/components/ReviewActionButtons.tsx` — 新增 `inline`(行内流式容器)与 `corner('tr'|'br')` 变体 prop;`btnBase opacity: 0`(hover-only)改 `0.95` 常显。
  - `packages/infinite-canvas/src/components/StoryboardTimeline.tsx:1028-1031, 1211-1219` — ShotRow 订阅 `useCanvasStore.approveNode`;ReviewBadge(原 1207 行)旁对 `reviewStatus === 'pending'` 行挂 `<ReviewActionButtons inline>`,驳回走组件自带意见对话框(内部直调 store.rejectNode)。
- **接的 handler**: `canvasStore.approveNode(nodeId)` / `canvasStore.rejectNode(nodeId, feedback)` —— canonical optimistic + 回滚 + toast,与 CanvasContextMenu 右键审核**同一条既有路径**(WRITE-02 注释钦定消费方式)。
- **修复后击数**: 时间轴页待审行点「✓」= **1 击**(原 4 击:切画布→找节点→右键→点审核项)。驳回为 1 击 + 对话框内确认(与右键菜单驳回同交互深度)。
- **偏离说明**: 无偏离,按审计建议原位挂载;按钮对 approved/rejected 行不渲染(ReviewBadge 已表终态,避免视觉重复)。

## P1-2 详情抽屉:剧本变体选定直达 ✅

- **改动**:
  - `packages/infinite-canvas/src/components/nodes/AssetCardNode.tsx`(script 卡 meta 行下)— `stage === 'script' && reviewStatus === 'pending'` 时渲染常显按钮「🔍 选定变体」。
  - 新建 `packages/infinite-canvas/src/components/panel/reviewAnchor.ts` — 一次性锚定信号(模块级,非响应式;store 层零改动以守白名单)。
  - `packages/infinite-canvas/src/components/panel/NodeDetailPanel.tsx` — 根 div 挂 `panelRef`;`node?.id` 变化时 `consumeReviewAnchor` 命中则 `scrollIntoView` 到新增的 `data-review-section` 包裹层(ReviewSection 原位,详情层完整审核面保留)。
- **接的 handler**: 打开路径 = 双击画布节点同一组 `setSelectedNode + setDetailNode`;选定动作本体仍由抽屉内既有 ReviewCard(`/api/v2/canvas/review/*`)执行——未新建任何数据通道。
- **修复后击数**: 画布 script 卡点「🔍 选定变体」= **1 击**直达抽屉审核区(原 3 击全程下钻:双击→滚审核区→点选定)。
- **偏离说明**: 采纳审计建议的「开抽屉并锚定审核区」方案而非卡内内联三选一 chip——ReviewCard 选项依赖后端 review/options 接口(现 404 优雅降级),内联 chip 会复制该拉取逻辑且违背「复用详情层同一调用路径」约束。

## P1-3 管线视图:待决策资产抽屉内选定 ✅

- **改动**:
  - `packages/infinite-canvas/src/components/pipeline/NodeDetailPanel.tsx` —
    - `handleSelectWinner`: 从 graph 查节点所属 variantGroup,`frameSlotOfGroup`(store/variantOps,D-11 组键推导)取 slot → `canvasStore.selectWinner(nodeId, {frameSlot})` → 成功 `triggerStaleCascade`(53-02 同款),与 VariantWall「选定」**逐拍同路径**。
    - AssetThumb root 由 `<button>` 转 `div[role=button][tabIndex]`(合法嵌套 + 键盘可达);○待选缩略图底部常显「★ 选定」18px 条,stopPropagation 与跳画布定位互不干扰。
    - 资产区头部加「⚠ N 个待决策 · 缩略图底部『★ 选定』直接定优胜」提示行(与 DagNode 卡同词汇)。
- **接的 handler**: `canvasStore.selectWinner`(v3 乐观 + select-winner 端点 + 失败回滚 + toast)。
- **修复后击数**: 管线页点节点开抽屉 → 点缩略图「★ 选定」= **2 击且零跨视图**(原 ≥4 击跨两视图);抽屉本身由 DagNode 卡 1 击打开,故从管线首页计为 2 击。
- **偏离说明**: 采纳审计备选方案(行内 ★ 选定)而非主建议(「去画布选片 →」导航钮)——导航只搬家不直达,行内选定才是真 1 击;handler 已实证与变体墙同一 canonical 通道,失败安全回滚。

## P1-4 门户首页:成片终审直达 ✅

- **改动**:
  - 新建 `packages/portal/src/pages/EpisodeFinalReview.tsx` — 集行组件:挂载即 `fetchGateState(projectId, ep)`(DeliveryPage 同款既有 GET 端点);p13 门 `pending + reviewId` → 状态点(阻塞态金/非阻塞灰,54 四态色同源)+「等你决策」+ [放行] 主按钮 + ghost「驳回→」(`/deliver/:ep`);已决门 → 被动状态 chip(54 词表);快照 null/无门 → 不渲染(fail-closed)。
  - `packages/portal/src/pages/PortalHome.tsx:181-185` — 集行 PipelineRibbon 后挂载。
- **接的 handler**: `runTerminalOp`(lib/gateOpsFlow)+ `gateOps`(@ic/services/canvasApi)—— 乐观翻转/409 幂等重拉/失败回滚三分支全套,与 DeliveryPage 终审动作条**同一状态机**;回滚原因行内透传 3s 自散。驳回必填理由走 ReasonDialog(在交付页),故行内给深链——与 57-06 交互契约一致。
- **修复后击数**: 门户首页直接看到「哪集待决策」并点「放行」= **1 击**(原:首页无待办可见,逐集盲进交付页 2 击且不可发现);驳回 1 击进交付页 ReasonDialog。
- **偏离说明**: 未采纳「gates 传入集行 ribbon 改 full 档」的最低限度方案——full 档 24px + 22 段按钮会显著改变首页行高与动线;独立行内组件信息量更准(只报 p13 人工门)。[放行] 用 `theme.button.primary` 填充,依据 57-06 UI-SPEC §Color reserved 新增的「放行主按钮」条目(PortalHome 头部 U-09 注为 57-02 时旧约)。

---

## P2 修复(2/6;4 条 blocked)

### P2-1 ReviewActionButtons 死组件挂载 + 常显 ✅

- `packages/infinite-canvas/src/components/badges/NodeBadges.tsx` — 右下审核角(四角产权制原位):`reviewStatus === 'pending'` 时纯展示圆点换成 `<ReviewActionButtons corner="br">`,接 `store.approveNode/rejectNode`;approved/rejected 保持被动圆点(终态无高频动作)。
- `ReviewActionButtons.tsx` `opacity: 0` → `0.95` 常显(随 P1 commit 落地)。
- 画布待审卡: **1 击**(原右键菜单 2 击且无视觉 affordance)。
- 注: 生产渲染器为 C 层 `badges/NodeBadges`(registerCInteractions 覆盖 B 默认),已实证故挂这层。

### P2-2 资产库「📍 定位」常显 ✅

- `packages/infinite-canvas/src/components/assetManager/assetManager.css:115-121` — `.am-card__locate` 去 `display:none` + hover 规则,改 `opacity:.9` 常显/hover 1(与同卡「★ 选定」同策略);位置由 left:7px 改 **right:7px**——避让同位(bottom:46px;left:7px)的 ★选定/✕取消选定 按钮,消除常显后的叠盖。

### P2-3 / P2-4 / P2-5 / P2-6 — blocked

- **原因**: 四条均在 `/data/workspace/kais-story-map`(Analysis.tsx / Cases.tsx / home/FinalCTA)——**另一个仓库**,在本批白名单(`packages/infinite-canvas/src/components/**` + `packages/portal/src/pages/**` + 直引 css)之外;「白名单外文件出现 diff = 验收失败」故零改动。
- **建议**: 移交 story-map 仓对应批次的司机按审计 fix 字段执行(四条修复建议均已实证可行,无技术阻塞)。

---

## 验证摘录

```
$ cd packages/infinite-canvas && npx tsc -b        # 零输出 = 通过
$ cd packages/infinite-canvas && npm run build
  ✓ built in 2.69s (dist/assets/index-*.js 1,453.60 kB;chunk 体积警告为既有)
$ cd packages/infinite-canvas && npx vitest run
  Test Files  48 passed (48) / Tests  527 passed (527)

$ cd packages/portal && npx tsc -b                 # 零输出 = 通过
$ cd packages/portal && npm run build
  ✓ built in 936ms (kap-nav.css runtime 解析提示为既有)
$ cd packages/portal && npx vitest run
  Test Files  1 failed | 2 passed (3) / Tests  4 failed | 30 passed (34)
  → 失败 4 条全在 src/__tests__/ribbon.test.ts(断言注册表 22 段,实际已 24:
    既有提交 736889db「注册表 22→23 跟进」+ 8839d49b「p036 钩子接入」改了
    phaseRegistry 但未跟进该测试)——基线既有债,与本批改动文件零交集
    (本批 portal 仅 pages/PortalHome.tsx + pages/EpisodeFinalReview.tsx)。
```

- **deploy**: 未执行(工单禁令,部署另批)。
- **push**: 未执行(工单禁令)。

## 逐条 P1 击数自查

| Finding | 修复前 | 修复后 |
|---|---|---|
| P1-1 时间轴审核判定 | 4 击(切视图+右键) | **1 击**(行内 ✓/✕) |
| P1-2 剧本变体选定 | 3 击(全程下钻) | **1 击**(卡上按钮→抽屉锚定审核区) |
| P1-3 待决策资产选定 | ≥4 击(跨两视图) | **2 击零跨视图**(开抽屉 1 + ★选定 1) |
| P1-4 成片终审放行 | 2 击且待办不可发现 | **1 击 + 首页待办可见**(驳回 1 击进交付页) |

FIX_DONE P1修4/4 P2修2/6

---

## 验收缺陷修正（追加批 · commit 7c575949）

**缺陷**：真机验收 11:30 实测——外星铲屎官项目时间轴 14 行全 ○待审（头部统计「✓0 ○14 ✕0」），行上 0 个 ✓/✕ 按钮。根因：P1/P2 引入的按钮渲染条件用严格等值 `=== 'pending'`，但真实库 review_status 绝大多数为 NULL（从未审过 = 待审语义；全库 NULL 11,453 / 'pending' 仅 3 / approved 28 / rejected 1）。按钮条件与统计口径（`pending = !reviewStatus || reviewStatus === 'pending'`，StoryboardTimeline.tsx:2718）不一致 → 页面自相矛盾。

**逐处改动 old → new**：

| # | 位置 | old | new |
|---|---|---|---|
| 1 | StoryboardTimeline.tsx:1212（P1-1 行内 ✓/✕） | `node.reviewStatus === 'pending'` | `(!node.reviewStatus \|\| node.reviewStatus === 'pending')` |
| 2 | AssetCardNode.tsx:803（P1-2 script 卡「🔍 选定变体」） | `asset?.reviewStatus === 'pending'` | `(!asset?.reviewStatus \|\| asset.reviewStatus === 'pending')` |
| 3 | NodeBadges.tsx:151（P2-1 卡角活按钮） | `asset.reviewStatus === 'pending'` | `(!asset.reviewStatus \|\| asset.reviewStatus === 'pending')` |
| 4 | NodeBadges.tsx:149（reviewColor pending 分支，同色连带对齐） | `asset.reviewStatus === 'pending' ? v3theme.signal.running` | `(!asset.reviewStatus \|\| asset.reviewStatus === 'pending') ? v3theme.signal.running` |

**边界核查**：
- ReviewActionButtons 内部逻辑/handler/数据通道零改动——其内部对非 approved/rejected（含 NULL）本就走「待审核：显示操作按钮」分支，无需改。
- pipeline/NodeDetailPanel ★选定：全文 0 处 reviewStatus 引用，依赖 variantGroup 待选语义——确认无此缺陷，不动。
- panel/NodeDetailPanel：本批未引入任何按 reviewStatus 的按钮渲染条件（仅 reviewAnchor 锚定滚动），存量 `prevStatus === 'pending' && === 'approved'` toast 条件（:62）为存量行为，不动。
- ReviewBadge `!status → null` 为存量行为，按工单本批不改。

**验证**：`cd packages/infinite-canvas && npx tsc -b --force` → 零输出，通过。

**仓库纪律**：分支 feat/ui-direct-action-fix 追加 1 commit（7c575949），逐文件 add（3 文件均在白名单域），基线脏文件零触碰，禁 push/deploy 照守。

FIX2_DONE 4处
