# 核销表 · 第8环 审核

> 90分级验收（孙总方法论·硬门禁）。已吸收独立交叉验证修正（⚠️）。auto=机器判定，human=目视。
> ⚠️审计结论：原清单 S1-S6 全部"描述了但代码里没有"——即这是**待执行**清单，不是已完成。落点行号经核对基本准确。

## 模块目标
①Sidebar 死链清零 + 加审核台入口 ②组长待审队列(通过/打回带原因) ③打回回路闭环(operator 能感知重选)。

## Ralph 修改步骤

| # | 改什么 | 落点 |
|---|--------|------|
| S1 | 删4条死链(/tasks /images /tasks/build /tasks/list)，加 `{label:'审核台',href:'/review',roles:['supervisor','admin'],icon:'✅'}`，共5条全有页面 | Sidebar.tsx:19-28 NAV_ITEMS |
| S2 | _fmt_task return dict 末尾加 `reject_reason`/`review_status`(均可None)；模型 task.py:50-55 已有字段直接引用 | tasks.py:58-64 |
| S3 | TaskListItem 加 `reject_reason?:string\|null` + `review_status?:'pending'\|'approved'\|'rejected'\|null` | dto.tasks.ts:8-16 |
| S4 | 状态 span 下加 `{task.reject_reason && <p className='text-xs text-status-error mt-0.5 line-clamp-1'>打回：{task.reject_reason}</p>}`(需先 S3) | RecentTasksBar.tsx:49-61 |
| S5 | h2 后插3个 Tab(待审/已通过/已打回)，已通过→router.push('/history?status=approved')，已打回→rejected；顶部 import useRouter | review/page.tsx:54-57 |
| S5b | ⚠️审计补：history/page.tsx 改 `useSearchParams` 初始化 statusFilter，否则跳来后筛选器停"全部"(A7假通过) | history/page.tsx:26 |
| S6 | useEffect setCurrentTask 后补 GET /tasks/{id} 取 reject_reason 存 state；ProgressBar 下、FlywheelBanner 上插红色 alert 横幅(需先 S2/S3) | tasks/[id]/text/page.tsx:23-25 |

## 验收清单（做完一个勾一个）

- [ ] **A1**〔auto〕Sidebar 无死链：`grep -n href Sidebar.tsx` 不含四条死链且含 '/review'。
- [ ] **A2**〔auto〕待审队列可用：supervisor GET /api/v1/review/queue → code=0 且 data.items 是数组。
- [ ] **A3**〔auto〕通过写库正确：`SELECT status,review_status,approved_at,reviewer_id FROM generation_tasks WHERE id=<id>` → 四项全满足(status=review_status='approved'、approved_at 与 reviewer_id 均非NULL)。⚠️审计修正：assert 要含 reviewer_id(r[3])。
- [ ] **A4**〔auto〕打回写库正确：RejectModal 空提交报"原因必填"不发请求；填后 `SELECT status,review_status,reject_reason` → status='pending_selection'、review_status='rejected'、reject_reason 非空。
- [ ] **A5**〔auto〕GET /tasks 含字段：被打回任务 reject_reason 为字符串、review_status='rejected'；未打回为 null，接口不500。
- [ ] **A6**〔human〕operator 感知打回：supervisor 打回填"标题太硬广"→切 operator 访问 /tasks/new，RecentTasksBar 卡上见红字"打回：标题太硬广"→点卡进 text 页顶部有含该文字红色横幅。
- [ ] **A7**〔human〕Tab 跳转无404：/review 三 Tab，点已通过→/history?status=approved 页面有内容**且筛选器自动选中"已通过"**(非"全部")，点已打回同理。⚠️审计补：需验筛选器自动预选。
- [ ] **A8**〔auto〕角色守卫：⚠️审计修正——分两查：`curl -w '%{http_code}'` 验 HTTP 403 + 读 body 验 `code==40301`(原命令 -o /dev/null 读不到 body)。

## 待北哥定义（不阻断代码）
- 已通过/已打回 Tab 是跳 /history 还是 /review 内分 Tab 调独立接口。
- 打回原因卡片截断字数 + 展开交互(tooltip/点击)。
- RejectModal 是否预设快选分类(标题太广告体/图文不符/违规词)。
