Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0193vBv6w5MV2cifdTUu9H5P
98 lines
9.4 KiB
Markdown
98 lines
9.4 KiB
Markdown
# 验收修复单 · 会话列表单一数据源:四条源码合同没跟着外壳搬家,门禁卡住(2026-09-17)
|
||
|
||
## 0. 基线
|
||
|
||
- 验收对象:`e4e73f56`(会话列表单)与 `6d81062b`(校正输入框守卫单),当前都在 `origin/staging`,head `cc1a8980`。
|
||
- 分支:`codex/session-list-single-source-fix-20260917`,从 `origin/staging` 起。
|
||
- 范围:`frontend/tests/` 四个合同测试;`tests/test_daily_and_rectification_entrypoints.py`;`frontend/src/app/(app)/page.tsx`(F3,只允许减行或持平);`docs/`。不动 provider、不动排序、不动路由组。
|
||
- BUG 段:**BUG-933 起**(基线最大号 BUG-932)。
|
||
- **本单是 staging 能否部署的唯一拦路石**:`/api/health` 仍是 `dc2f2a16`,今天四个代码提交一个都没上线。
|
||
|
||
## 1. 验收结论
|
||
|
||
### 校正输入框守卫单 `6d81062b`:**通过**
|
||
|
||
| 任务 | 结果 |
|
||
| --- | --- |
|
||
| T1 `send()` 会话类型守卫 | 通过。`consultSendBlockedByRectificationSession` 在扣点与撤回窗口之前拦下,保留草稿,转 `openRectificationSession`;刷新恢复重放同样受守卫,pending 存储里指向校正会话的记录直接丢弃 |
|
||
| T2 普通输入框只有禁用态 | 通过。`inputDisabled` / `submitBlocked` 都加了 `activeRectificationSession`,占位文案「正在打开生时校正…」,打开失败出「重新打开生时校正」按钮并先清 `rectificationError` |
|
||
| T3 三条回退路径 | 通过,且多修了归档一条。`fallbackSessionId` 优先非校正会话,落到校正会话时走 `selectSession` 的延迟切换 |
|
||
| T4 服务端错误码 | 通过。类型不对回 `409 session_not_consultation`(在扣点预留之前,不扣点),真查不到仍 404;客户端 `payloadCode` 把 code 带到 `ConsultationResponseError`,命中后回滚乐观消息、不显示报错、直接开校正面 |
|
||
| T5 记录 | 通过。BUG-924 / 925、CHANGELOG、DESIGN、VOICE、真机清单齐 |
|
||
| `page.tsx` 行数 | 1830 → 1830 |
|
||
|
||
残留一条 P3,见 F3。
|
||
|
||
### 会话列表单 `e4e73f56`:**未通过(P1)**
|
||
|
||
实现本身按任务书落地:多键排序(`ordering` 改数组 + `formatOrderClause`)、`(app)` 路由组 + `(app)/layout.tsx` 常驻外壳 + `SessionListProvider`、`app/` 下 `SidebarProvider` 恰好一次、`use-sidebar-data.ts` 与 `sidebar-data-cache.ts` 已删、服务端排除 `messages = []` 并单独回一条 `draft` 供启动复用、标题改「类别 · M月D日」并删 `uniquify`、`toSidebarSessionRow` 两处侧栏共用。两条让步(元数据操作未上移、延迟落库未做)任务书都允许。
|
||
|
||
**但 `npm test` 多出 4 条红,且这 4 条会让 staging 门禁必红。**
|
||
|
||
`.gitea/workflows/backend-quality-gate.yml` 的 `Validate backend, package, frontend, and database contracts` 步骤在 `set -euo pipefail` 下依次跑 `npm test --prefix frontend`、`npm run lint --prefix frontend`。这 4 条是纯源码正则合同,不依赖 Docker、不依赖环境,在门禁机上照样红:
|
||
|
||
| 测试文件 | 断言找不到的东西 | 它现在在哪 |
|
||
| --- | --- | --- |
|
||
| `chat-navigation-a11y-contract.test.ts` | `<main className="chat-app">` 紧跟 `replyAnnouncement` 的 sr-only 播报 | `<main>` 在 `(app)/layout.tsx`,播报仍在 `page.tsx`,只是不再相邻 |
|
||
| `rectification-history-open-20260909.test.ts` | `page.tsx` 里 `openErrorSessionId={rectificationErrorSessionId}` | `(app)/layout.tsx` 从 `registration.openErrorSessionId` 传给 `AppSidebar` |
|
||
| `rectification-surface-contract.test.ts` | `page.tsx` 里 `openingSessionId={rectificationOpeningSessionId}` | 同上,经 `registration.openingSessionId` |
|
||
| `chat-session-url.test.ts` | `page.tsx` 里 `const [nextAccount, modelCatalogResult, sessionsPayload]` | 启动改成 `await sessionListReady` + `sessionListBoot()`,这个解构没了 |
|
||
|
||
四条被保护的性质**都还成立**(`/` 仍 `○ Static`,侧栏行仍拿得到 opening / openError,播报仍在),所以是合同陈旧、不是行为回归。但它们被留在红里,等于把门禁堵死。
|
||
|
||
根因是流程:执行方只跑了定向测试(进度记录写「定向 26 + 先前 114/115」),没跑全量 `npm test`,也因此没发现同批次带进的两处 TS 错(`session-sidebar-row.ts(30,41)`、`chart-library-session.test.ts(89,42)`,进度记录写着「tsc 0 错」,实测 2 错,后来由滚动单的 `cc1a8980` 顺手修掉)。
|
||
|
||
## 2. 任务分解
|
||
|
||
### F1 四条源码合同跟着外壳搬家(BUG-933)
|
||
|
||
四条都按「原值 / 新值 / 原因」三栏改写,**不得删除测试、不得弱化成只判存在**:
|
||
|
||
- `chat-navigation-a11y-contract.test.ts`:改为分别断言 `(app)/layout.tsx` 里有且只有一处 `<main className="chat-app">`,且 `page.tsx` 里 `replyAnnouncement` 的 sr-only 播报仍带 `role="status" aria-live="polite" aria-atomic="true"`,并且它是 `SidebarInset` 内容的第一个子节点。
|
||
- `rectification-history-open-20260909.test.ts`、`rectification-surface-contract.test.ts`:改为断言 `(app)/layout.tsx` 把 `openErrorSessionId` / `openingSessionId` / `openErrorMessage` 传给 `AppSidebar`,且 `page.tsx` 的 `registerShellControls` 载荷里带这三个字段(两端都断,缺一端就等于断链)。
|
||
- `chat-session-url.test.ts`:把「首页自己并发拉账户/目录/列表」的断言改成「首页 `await sessionListReady` 后读 `sessionListBoot()`,并且 `page.tsx` 里不再出现 `fetchSessions(`」,保留它原本要守的「`/` 是静态路由上的客户端读」这一性质。
|
||
- 验收:`npm test --prefix frontend` 的失败清单与 `cc1a8980` 比**少这 4 条、不多任何一条**;`npm run lint` 0 error。
|
||
|
||
### F2 两条陈旧的 Python 入口合同(BUG-934)
|
||
|
||
`tests/test_daily_and_rectification_entrypoints.py` 断言首页出现 `daily-starlanguage-card` / `birth-rectification-card`,这两个类名在 `62903b7a`(空状态改成「问候 + 输入框 + 两枚入口 pill」)就已删除,全仓已无。**这是既有欠账,不是本批引入**,也不在 `CORE_PYTEST_TARGETS` 里,所以不挡门禁,但任何一次 Python 定向跑都会红。
|
||
|
||
- 改为断言当前真实的入口形态(`StarterEntries` 的两枚 pill 与 `startDailyStarlanguageConsultation` / 校正入口),或在确认无对应实现后连同该断言一并删除并在测试里写明原因。选哪种由执行方判断后写进进度记录。
|
||
- 验收:`.venv/bin/python -m pytest tests/test_daily_and_rectification_entrypoints.py` 全绿。
|
||
|
||
### F3 校正守卫的残留死角:一个会话都不活跃时输入框静默吞发送(BUG-935)
|
||
|
||
`page.tsx` 的 `activeSession` 现在是 `sessions.find(id === activeSessionId) ?? sessions.find(非校正)`。当**列表里只剩校正会话**时它是 `undefined`(旧代码 `?? sessions[0]` 不会)。此时:`activeRectificationSession` 为假 → 普通输入框**可用**;按发送 `send()` 因 `!currentSession` 直接 `return false`,没有任何提示;因为 `activeRectificationSession` 为假,「重新打开生时校正」按钮也不出现。
|
||
|
||
触发路径:账号里只有校正会话,或删除当前普通会话后剩下全是校正会话,且此时校正面正在打开或打开失败。窄,但正是本单要消灭的那类死角。
|
||
|
||
- 修法:`activeSession` 为空且列表里存在校正会话时,按「校正会话占位」处理——输入框禁用、占位文案「正在打开生时校正…」、打开失败出重开按钮;派生值放进现有 hook 或 `rectification-session-composer-guard.ts`,`page.tsx` 不增行。
|
||
- 验收:`rectification-session-composer-guard.test.ts` 加一条纯函数断言(输入「列表全是校正会话、activeSession 为空」→ 锁定态为真);`send()` 在无活跃会话时不得静默返回而不给任何反馈。
|
||
|
||
### F4 记录
|
||
|
||
- `docs/BUG_HISTORY.md` 新增 BUG-933 / 934 / 935(933 关联 BUG-927 与 BUG-745;935 关联 BUG-924)。
|
||
- BUG-927 的「防复发」补一条:**外壳搬家必须同步搬走针对 `page.tsx` 的源码合同,且交付前必须跑全量 `npm test` 与 `tsc --noEmit`,不得只跑定向。**
|
||
- `docs/tasks/PROGRESS-session-list-single-source-fix-20260917.md`;状态板三行改状态。
|
||
- T2 的「元数据操作未上移到 provider」目前只写在进度记录里,补进 `BLOCKED.md`,与延迟落库那条并列。
|
||
|
||
## 3. 硬红线
|
||
|
||
1. 不得靠删测试或放宽断言过门;四条合同必须仍然守住原来的性质。
|
||
2. 不动排序、provider、路由组的既有行为。
|
||
3. `page.tsx` 不增行。
|
||
4. 交付前必须跑**全量** `npm test --prefix frontend`、`tsc --noEmit`、`next build --webpack`,并把失败清单与 `cc1a8980` 逐条比对后写进进度记录。
|
||
|
||
## 4. 开工前置命令
|
||
|
||
```bash
|
||
git fetch origin --prune
|
||
git worktree add -b codex/session-list-single-source-fix-20260917 .worktrees/session-list-single-source-fix-20260917 origin/staging
|
||
cd .worktrees/session-list-single-source-fix-20260917/frontend
|
||
./node_modules/.bin/tsc --noEmit && npm run lint && npm test 2>&1 | tail -20
|
||
```
|
||
|
||
## 5. 验收口径
|
||
|
||
`tsc --noEmit` 0 错;`npm run lint` 0 error;`npm test` 相对 `cc1a8980` 少 4 条红、不新增;`next build --webpack` 通过且 `/` 仍 `○ Static`、首屏 gzip ±2%;`pytest tests/test_daily_and_rectification_entrypoints.py` 全绿。合入后必须核对 `/api/health` 的 `deployment.gitCommit` 等于最近一次含门禁路径的 staging 提交——今天四个代码提交至今一个都没上线。
|