fix(web): keep rectification sessions listed and persist chats on first send
Independent Staging Quality Gate / validate (push) Failing after 16m52s
Independent Staging Quality Gate / publish (push) Skipped

Empty-consultation filtering is now session_type scoped so birth-time rows stay in the sidebar. Subtitles use updatedAt with sort/group/cursor. New chats stay local until the first send.
This commit is contained in:
jesse-ux
2026-09-21 16:39:32 +08:00
parent f8d65e484d
commit e71e4f9200
21 changed files with 461 additions and 186 deletions
+48
View File
@@ -13055,3 +13055,51 @@
- 相关记录:BUG-142
- 复发自:BUG-142(publish 超时预算;本次是缓存被过早丢掉后的冷构建)
- 修复版本:待本修复合入 staging 的门禁 run
## BUG-987 | 列表用 messages=[] 过滤,生时校正会话全部从侧栏消失
- 状态:resolved
- 首次发现:2026-09-21
- 最近更新:2026-09-21
- 影响面:`GET /api/sessions``excludeEmptyConsultations`、侧栏历史
- 用户现象:staging 侧栏一条生时校正都没有;深链 `?c=` 仍能打开同一会话。
- 触发条件:打开 `/` 或次级页拉会话列表。校正会话的 `chat_sessions.messages` 永远是 `[]`
- 根因:`e4e73f56` 用全表 `messages <> '[]'` 当「有没有内容」。该判据只对咨询成立。客户端 `isListedSidebarSession` 放行校正,服务端先把行拿走。源码正则 `not("messages", "eq", [])` 从未拿真实行验证。
- 修复:过滤收窄为 `session_type.neq.consultation,messages.neq.[]`。归档视图同一条规则。校正内容在 case 表,不在 `messages` 列。
- 验证:`database-session-list-visibility.test.ts`(真实 Postgres,无 Docker 时 skip)、`session-list-filter.test.ts` 跨层对断、`chat-session-authority.test.ts`
- 防复发:不得用「某列为空」推断会话有没有内容。列表过滤必须带 `session_type` 限定。不得用源码正则代替真实行测试。
- 相关记录:BUG-928、BUG-704
- 复发自:BUG-928(空咨询过滤写成全表 `messages <> '[]'`
- 修复版本:待本修复合入 staging
## BUG-988 | 侧栏副标题用创建时间,排序用最后活动时间,列表读起来是乱的
- 状态:resolved
- 首次发现:2026-09-21
- 最近更新:2026-09-21
- 影响面:`sessionSidebarSubtitle``SESSION_LIST_COLUMNS`
- 用户现象:侧栏时间不单调(9/18 → 9/17 → 9/7 → 9/16),9/7 创建、9/16 仍在用的会话落在「最近 7 天」却显示 9/7。
- 触发条件:会话创建后隔天继续使用。
- 根因:`e4e73f56``created_at` 加进列表列,副标题读创建时间;排序、分组、游标仍用 `updated_at`。产品 2026-09-21 推翻 BUG-929 的「副标题为创建时间」。
- 修复:副标题改 `updatedAt`。列表列去掉 `created_at`。详情接口仍返回 `created_at`
- 验证:`session-groups.test.ts`(早创建晚活动:位置按活动时间,副标题显示活动时间)、`chat-session-authority.test.ts` 列合同。
- 防复发:显示、排序、分组、游标必须用同一个时间字段(`updated_at`)。`SESSION_LIST_COLUMNS` 不得再含 `created_at`
- 相关记录:BUG-929、BUG-553
- 复发自:BUG-929(副标题改创建时间后与排序键分叉)
- 修复版本:待本修复合入 staging
## BUG-989 | 点「新建对话」复用旧空会话,第一问之前就落库
- 状态:resolved
- 首次发现:2026-09-21
- 最近更新:2026-09-21
- 影响面:`startNewChat``send()``GET /api/sessions``draft`、首页启动
- 用户现象:点新建落到 9/17 的旧「新对话」;连点不会开新的。空咨询在开口前就已经在库里。
- 触发条件:点「新建对话」,或启动时并入 `draft`
- 根因:BUG-928 让步成「复用空咨询 + 服务端 draft」。复用既不 bump 时间也不改创建时间。产品 2026-09-21 收掉该让步。
- 修复:`startNewChat` 只在本地创建,不 `POST`、不写 `?c=`。第一问 `send()``POST /api/sessions` 再走既有 append。落库失败撤销本地会话并保留输入框草稿。删除 `draft` 字段、`draftRow``findReusableEmptyConsultation`
- 验证:`session-list-filter.test.ts` 源码合同(新建无 POST / 无 URLsend 时 create,失败不 `setDraft`)、`chat-session-url.test.ts``session-list-lifecycle.test.tsx` 仍覆盖无 draft 的列表响应。
- 防复发:第一问之前不得 `POST /api/sessions`。未落库会话不得写入 `?c=`。列表响应不得再带 `draft`
- 相关记录:BUG-928、BUG-987
- 复发自:BUG-928(延迟落库让步未收口)
- 修复版本:待本修复合入 staging
@@ -0,0 +1,50 @@
# PROGRESS · 会话列表重建(2026-09-21
工作树:`.worktrees/session-list-rebuild-20260921`
分支:`codex/session-list-rebuild-20260921`
基线:`origin/staging` @ `f8d65e48`(任务书;代码基线 `0c3c9d6b`
## 任务状态
| 任务 | 状态 | 说明 |
| --- | --- | --- |
| T1 列表不得吞掉生时校正 BUG-987 | 完成 | 过滤改为 `session_type.neq.consultation,messages.neq.[]`;归档同一规则 |
| T2 显示时间与排序同源 BUG-988 | 完成 | 副标题改 `updatedAt``SESSION_LIST_COLUMNS` 去掉 `created_at` |
| T3 第一问之前不落库 BUG-989 | 完成 | 删除复用与 `draft`;本地新建;`send()` 先 POST 再建问 |
| 记录 | 完成 | BUG-987/988/989、CHANGELOG、DESIGN、BLOCKED 划掉 120 行那条 |
## 开工基线
| 项 | 值 |
| --- | --- |
| 分支 | `codex/session-list-rebuild-20260921` 跟踪 `origin/staging` @ `f8d65e48` |
| `page.tsx` 行数 | 1826 |
| `Home()` useState / useRef | 未改装配;帽 36 / 37 |
## 实现要点
- 服务端与客户端共用 `excludeEmptyConsultations` / `isListedSidebarSession` / `cloudListIncludesSession`。校正会话 `messages` 永远是 `[]`,不能当空咨询删。
- 副标题、排序、分组、游标都用 `updated_at`。详情接口仍返回 `created_at``home-cloud-sync` 回落链未动)。
- `startNewChat` 只替换本地未开口咨询,不 POST、不写 `?c=``persistSession("create")` 成功后才 push URL。落库失败撤销本地会话,且发生在 `setDraft("")` 之前,输入框草稿仍在。
- 已成功建行的 id 记入 `cloudCreatedIds`,避免第一问失败后再发时重复 POST。
## 测试
| 项 | 结果 |
| --- | --- |
| `page.tsx` 行数 | **1791**(开工 1826,未增长) |
| `tsc --noEmit` | 0 错 |
| `npm run lint` | 0 error(既有 warning 未动;去掉新建路径里未再使用的 `setCreatingSession` 解构,避免新增 warning |
| 定向 | chat-session-authority / session-list-filter / session-groups / chat-session-url / local-postgres-or / chart-library-session / session-list-lifecycle / composer-isolation / home-shell-growth / chat-session-write **全部通过** |
| `npm test` | **3598** 条,**3509** 通过 / **89** 失败。失败几乎全是本机 Windows:`database-*.test.ts` 因 Docker 网段耗尽(`all predefined address pools have been fully subnetted`,其它 worktree 留下的 postgres 网络),以及 symlink / chmod / shell 脚本类用例。与本单相关的源码合同失败 1 条已修(`chat-session-write.test.ts` 启动不再 `writeChatSession(initialSession)`)。未在本机重跑 `0c3c9d6b` 全量作逐条基线。 |
| `npm run test:db` | Docker 在 PATH 里,但创建 compose 网络失败(同上网段耗尽)。**不得写成通过。** `database-session-list-visibility.test.ts` 照提交。 |
| `next build` / `--webpack` | 编译与 tsc 通过;收集 `/api/consult` 等页数据时 `symlink ... EPERM`(Windows 无开发者模式)。本机看不到 `/` Static 与 gzip。 |
## 让步
T1T3 都做了,功能上无让步。
环境缺口(替代证据):
1. **真实 Postgres 列表可见性**:本机 Docker 网段被其它会话占满,`test:db` 跑不起来。替代:`local-postgres-or.test.ts` 编译 `session_type.neq.consultation,messages.neq.[]``session-list-filter.test.ts` 跨层对断;`chat-session-authority.test.ts` 断言路由走 `excludeEmptyConsultations`
2. **next build 静态与 gzip**Windows symlink EPERM。未量首屏 gzip。
+1 -1
View File
@@ -152,7 +152,7 @@
| `TASK-consultation-answer-start-anchor-20260917.md` | `PROGRESS-consultation-answer-start-anchor-20260917.md` | 主会话回答落在结尾:`useConversationScrollAnchor` 是贴底跟随,流式期间视口钉在最后一个字,回答开头滚出视口;改为发送后问题钉顶、回答向下长、长出视口显示「跳到最新」、末尾动态留白;产品追加拍板:校正面同一语义(推翻 BUG-041/048 贴底),本轮开头 = 用户行或新助手行。BUG 段 930 起 | 已验收(经修复单) | `worktree/green-harbor-5be3` |
| `TASK-consultation-answer-start-anchor-fix-20260917.md` | `PROGRESS-consultation-answer-start-anchor-fix-20260917.md` | 验收修复单:F1 头就是留白行时留白按整视口算(BUG-931);F2 留白只在钉住期间存在(BUG-932);前置:先修 e4e73f56 的两处 TS 错否则门禁不过 | 已验收 | `cc1a8980`Claude 验收:tsc 0 / lint 0 error / npm test 3457 条 39 红与 11c0028d 逐条一致、新增 2 条绿 / `next build --webpack` 通过、`/` Static、首屏 gzip 591,242(较 09-16 基线 582,800 +1.45%,含会话列表单)/ Chrome 真实布局 S1–S6 全部通过,S6 新助手行距顶 16px 且增高不动,S5 不再写留白);真机六条欠 |
| — | `PROGRESS-starter-greeting-20260917.md` | 首页开场语改成 claude.ai 式单行问候:`starter-greeting.ts` 的「称呼 + 追问句」五时段十五条收成三个池子(时段 / 星期 / 回访,按 `variantSelection` 确定性取一条),副标题行与 `.starter-salutation` 下线,h1 降到 `clamp(24px, 2.8vw, 30px)`;追问移到输入框占位符「想聊什么都可以」。产品直接拍板,非 Bug,不占 BUG 号 | 待验收 | `codex/starter-greeting-20260917` |
| `TASK-session-list-rebuild-20260921.md` | | **P0 会话列表重建(`e4e73f56` 的返工)**:服务端 `GET /api/sessions``messages <> '[]'` 排除空咨询,但校正会话的 `messages` 永远是 `[]`(建行写死空数组、创建合同只收空数组、PATCH 忽略、`touch_chat_session_from_rectification_case` 只 bump `updated_at`)→ **全部生时校正会话从侧栏消失**,而客户端 `isListedSidebarSession` 明确放行校正会话,两层规则相反且无跨层对断(BUG-987);同一提交把副标题换成 `created_at`,排序/分组/游标仍是 `updated_at`,列表可见时间不单调(9/18→9/17→9/7→9/16)(BUG-988);BUG-928 让步下来的「复用空会话」不 bump 时间,点新建变成跳回 9/17 的旧草稿(BUG-989)。**产品 09-21 拍板**:D1 副标题改最后活动时间(推翻 BUG-929 该条,其余保留)、D2 收掉 `BLOCKED.md:120` 的让步,第一问前不落库、删复用与 `draft`。T1 不得让步,T3 可拆轮。BUG 段 987 起 | 待领取 | |
| `TASK-session-list-rebuild-20260921.md` | `PROGRESS-session-list-rebuild-20260921.md` | **P0 会话列表重建(`e4e73f56` 的返工)**:服务端 `GET /api/sessions``messages <> '[]'` 排除空咨询,但校正会话的 `messages` 永远是 `[]`(建行写死空数组、创建合同只收空数组、PATCH 忽略、`touch_chat_session_from_rectification_case` 只 bump `updated_at`)→ **全部生时校正会话从侧栏消失**,而客户端 `isListedSidebarSession` 明确放行校正会话,两层规则相反且无跨层对断(BUG-987);同一提交把副标题换成 `created_at`,排序/分组/游标仍是 `updated_at`,列表可见时间不单调(9/18→9/17→9/7→9/16)(BUG-988);BUG-928 让步下来的「复用空会话」不 bump 时间,点新建变成跳回 9/17 的旧草稿(BUG-989)。**产品 09-21 拍板**:D1 副标题改最后活动时间(推翻 BUG-929 该条,其余保留)、D2 收掉 `BLOCKED.md:120` 的让步,第一问前不落库、删复用与 `draft`。T1 不得让步,T3 可拆轮。BUG 段 987 起 | 待验收 | `codex/session-list-rebuild-20260921` |
### 个人报告