docs(tasks): 归档能力整体下线任务书(BUG-991)
产品拍板下线而非装回入口:删前端链路与 ?archived=1, 迁移清空存量 archived_at(不 bump updated_at),列留待后轮。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0199rbQDTsUbCVw84wc8BTFe
This commit is contained in:
co-authored by
Claude Opus 5
parent
11e4d5ff09
commit
f8cb1dcdee
@@ -154,6 +154,7 @@
|
||||
| — | `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` | `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 起 | **已验收**(经修复单) | `e71e4f92`(Claude 验收:T1/T2/T3 实现均正确;tsc 0 / lint 0 error 119 warning 与基线同 / `npm test` 本机 3640 条 34 红与 `f8d65e48` 逐条一致、净增 5 绿 / `next build --webpack` 通过、`/` Static、首屏 gzip 643,115 较基线 −0.027% / page.tsx 1826→1791。**P1:门禁 run 2832 红、staging 未部署**——新加的真实 Postgres 测试在有 Docker 的门禁机上跑出 `unsupported not filter`,兼容层 `not()` 不支持 `is` 算子,归档分支炸。修复单 `TASK-session-list-rebuild-fix-20260921.md` 已验收通过,`df329fa9` 已部署) |
|
||||
| `TASK-session-list-rebuild-fix-20260921.md` | `PROGRESS-session-list-rebuild-fix-20260921.md` | **验收修复单(挡着三条修复上线)**:本地 PG 兼容层 `LocalPostgresQueryBuilder.not()` 只认 `eq` / `cs` 两个算子,`not("archived_at","is",null)` 直接 throw → `GET /api/sessions?archived=1` 落外层 catch 回 500,**侧栏「归档记录」自 `a1956deb`(09-06,BUG-553)起一直打不开**,两周无人发现是因为只有源码正则合同看着它;本轮新加的真实 Postgres 测试第一次真跑到这条路径,把它照出来,于是门禁 run 2832 红、`e71e4f92` 没部署。**决策**:在兼容层补 `is not null / is not true / is not false`,不得反过来改调用方绕开,不得删改测试断言让门禁变绿。与 BUG-926 的 `order()` 缺口同类。BUG 段 990 起 | **已验收通过** | `df329fa9`(Claude 验收:`not()` 增 `isNot` kind,统一走新的 `compileUnaryIsClause` 编译 `is [not] null/true/false`,非法取值仍 throw;`filterClause` 是唯一消费者、已覆盖,未落进兜底分支。**门禁 run 2833 全绿:3653 条 / 3653 过 / 0 红 / 0 skip**,`ok 1238` 即那条真实 Postgres 归档测试;migrate 2834、deploy 2835 成功,`/api/health` 的 `deployment.gitCommit` = `df329fa9`。本机复跑:tsc 0 / lint 0 error 119 warning 未增 / `npm test` 用例名逐条 diff 较 `e71e4f92` **恰好 +2 无删除**、失败清单完全一致 / `next build --webpack` 通过、`/` Static、首屏 gzip 643,116(+1 B)。**顺带查出 BUG-991**:归档视图的 UI 入口在 `92558ee6`(08-22)就被删了,`onToggleArchivedView` 全仓无调用点 → BUG-990 原描述「用户点归档记录后 500」不成立(接口层 500 属实,用户碰不到),已在 Bug 历史更正;归档后看不回来也恢复不了是真问题,待产品决策) |
|
||||
| `TASK-archive-retire-20260921.md` | — | **归档能力整体下线(BUG-991)**:行菜单能「归档」,归档后没有任何界面能列出或恢复它——切换按钮 `session-nav-toggle`(「归档 N」/「返回」)在 `92558ee6`(2026-08-22)被删,但 `onToggleArchivedView` / `archivedCount` / `showingArchived` / `toggleArchivedView()` / `GET /api/sessions?archived=1` / `applyArchiveFilter` 整条链路留着,全仓无渲染调用点;行菜单的「恢复」同样不可达(归档行永远不进 `visibleSessions`)。接口自 `a1956deb`(09-06)起就是 500(BUG-990),一个月没人发现正因为点不到。**产品 09-21 拍板整个下线**(口径:多余入口宁可删除也不修),不装回入口。D2 本轮不删 `archived_at` 列(§7.6 拆两轮),只清空存量;D3 不得回退 BUG-990 的兼容层修复;D4 存量归档会话一律放回列表。**迁移禁止 bump `updated_at`**,否则全部历史会话跳顶、毁掉刚修好的 BUG-988。让步顺序里 T3 必须先于 T1/T2。删除能力不动 | 待领取 | — |
|
||||
|
||||
### 个人报告
|
||||
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
# TASK 归档能力整体下线(BUG-991)
|
||||
|
||||
- 日期:2026-09-21
|
||||
- 基线 commit:`origin/staging` head(开工时 `git fetch` 后以实际 head 为准;当前 `11e4d5ff`,最近一次含门禁路径的提交是 `df329fa9`,staging 已部署该 SHA)
|
||||
- 分支:`codex/archive-retire-20260921`
|
||||
- BUG 编号:**BUG-991**(已在 `docs/BUG_HISTORY.md` 以 `investigating` 记录,本轮改成 `resolved`,不新占号)
|
||||
- 关联记录:BUG-990、BUG-553、BUG-926
|
||||
|
||||
---
|
||||
|
||||
## 1. 事故实证
|
||||
|
||||
会话行菜单里有「归档」,点了之后这条会话从侧栏消失,**此后没有任何界面能把它列出来或恢复**。
|
||||
|
||||
- 侧栏分组页眉上原本有切换按钮 `session-nav-toggle`(文案「归档 N」/「返回」),在 **`92558ee6`(2026-08-22,`fix(web): stack mobile audit tables and restore sidebar hierarchy`)** 被删除。
|
||||
- 但它背后整条链路都留着:`onToggleArchivedView`、`archivedCount`、`showingArchived`、`toggleArchivedView()`、`GET /api/sessions?archived=1`、`applyArchiveFilter`。
|
||||
- 全仓 `grep -rn "ToggleArchivedView" frontend/src` 的非测试命中只有两处:`sidebar-session-row.tsx` 的**类型声明**,和 `use-home-shell-registration.ts` 里**提供**它的那一行。**没有任何渲染调用点。**
|
||||
- `app-sidebar.tsx:278` 与 `:286` 两处 `sessionControls?.showingArchived ? "归档记录" : …` 因此恒取后半边。
|
||||
- 行菜单里的「恢复」(`sidebar-session-row.tsx:178`)同样不可达:归档行永远不进 `useSessionManagement` 的 `visibleSessions`(非归档视图分支 `isListedSidebarSession` 直接 `if (session.archivedAt) return false`)。
|
||||
|
||||
顺带一提,这个半截状态直到 2026-09-21 才被发现,是因为 BUG-987 新加的真实 Postgres 测试第一次跑到归档分支,炸出了 BUG-990(兼容层 `not(col,"is",null)` 未实现 → `?archived=1` 回 500)。也就是说**归档视图的接口自 `a1956deb`(09-06)起就是坏的,而没人发现,因为界面上根本点不到它**。
|
||||
|
||||
`docs/testing/` 里还有两份让人「切到归档视图」的走查条目(`session-list-title-and-order-20260906.md` 第 63–67 行、`home-state-lowering-batch2-20260916.md` 的 C2 / C4),从 08-22 起就无法执行。
|
||||
|
||||
---
|
||||
|
||||
## 2. 根因
|
||||
|
||||
删掉一个视图的唯一入口时,只删了按钮,没有下线它的数据链路,于是留下「接口活着、界面没有」的半截功能。类型声明、provider 装配、服务端查询参数和数据库列都还在,源码正则合同与 `tsc` 都不会报——它们只检查写了什么,不检查有没有人调用。
|
||||
|
||||
---
|
||||
|
||||
## 3. 决策记录
|
||||
|
||||
- **D1(产品 2026-09-21 拍板):把归档能力整体下线,不是把入口装回去。** 依据:这个功能半截了一个月没人发现,说明没人用;产品一贯口径是「多余入口宁可删除也不修」。删除会话的能力保留不动。
|
||||
- **D2:`archived_at` 列本轮不删。** AGENTS.md §7.6 要求删列拆两轮。本轮只写一条向前迁移把存量 `archived_at` 清空,并保留列表查询里的 `is("archived_at", null)` 守卫;删列另开一轮,优先级低,不做也行。
|
||||
- **D3:不得回退 BUG-990 对兼容层的修复。** `not(col,"is",null)` 编译 `is not null` 是 PostgREST 语义补全,`local-postgres-not.test.ts` 必须原样保留,即使本轮之后它在业务代码里没有调用者。
|
||||
- **D4:存量被归档的会话一律放回列表**,不询问、不保留标记。它们本来就是用户自己的对话,而且他现在没有别的办法看到它们。
|
||||
|
||||
---
|
||||
|
||||
## 4. 硬红线
|
||||
|
||||
1. 不得顺手改会话**删除**能力(`DELETE /api/sessions/[id]`、行菜单的「删除」、BUG-505 那条链路)。归档和删除是两回事。
|
||||
2. 不得回退或弱化 `frontend/tests/local-postgres-not.test.ts`(D3)。
|
||||
3. 不得删 `archived_at` 列(D2)。迁移只能是 `update … set archived_at = null`,幂等、可重复执行。
|
||||
4. 删掉任何既有断言都要在进度记录里写「原值 / 新值 / 原因」三栏(AGENTS.md §7.3);测试总数下降必须逐条解释。
|
||||
5. `frontend/src/app/(app)/page.tsx` 的 `useState` / `useRef` 数不得增长(本轮预期是减少或持平)。
|
||||
6. 旧 bundle 兼容:`PATCH /api/sessions/[id]` 收到 `archived_at` **不得回 400**,按 `messages` 的既有做法解析后忽略。
|
||||
|
||||
---
|
||||
|
||||
## 5. 任务分解
|
||||
|
||||
### T1|前端下线(BUG-991)
|
||||
|
||||
要删的(以符号定位,不写行号):
|
||||
|
||||
- `sidebar-session-row.tsx`:行菜单里的「归档 / 恢复」项与 `Archive` / `ArchiveRestore` 两个图标 import;`SidebarSession.archived` 字段;`SidebarSessionControls` 的 `archivedCount`、`showingArchived`、`onToggleArchivedView`、`onToggleArchived`。
|
||||
- `app-sidebar.tsx`:两处 `showingArchived ? "归档记录" : …` 三元条件塌成常量(`aria-label="最近对话"`、页眉文案「最近」)。
|
||||
- `use-home-shell-registration.ts`:`archivedCount` 计算与 `onToggleArchivedView` / `onToggleArchived` 装配。
|
||||
- `use-session-management.ts`:`toggleArchived`(`restoring` / `nextArchivedAt` 那段)、`toggleArchivedView`、`showArchivedSessions` 状态与它在 `visibleSessions`、`loadMoreSessions`、`loadArchivedSessions` 里的分支;`visibleSessions` 收敛成单一的 `isListedSidebarSession` 过滤。
|
||||
- `session-list-filter.ts`:`applyArchiveFilter` 的 `archived` 参数与 `not(...)` 分支(改成固定 `is("archived_at", null)`,函数可并入调用处);`cloudListIncludesSession` 的 `archivedView` 入参。
|
||||
- `home-cloud-sync.ts`:`fetchSessions` 的 `archived` 选项与 `params.set("archived","1")`;`applyLegacySessionControls` 里 archived 的那一半(**这段会从旧 localStorage 重新造出归档行,必须删**,`pinned` 那一半保留);`readLegacySessionControlIds` / `clearLegacySessionControlKeys` / `sessionControlsStorageKey` 的 `"archived"` 分支——`clearLegacySessionControlKeys` 仍要清掉那个 localStorage key,只是不再据它写库。
|
||||
- `session-sidebar-row.ts`:`archived: Boolean(session.archivedAt)` 映射。
|
||||
|
||||
**保留**:`ChatSession.archivedAt` 类型与 `home-cloud-sync` 的读取映射(列还在,读回来不报错),`isListedSidebarSession` 里 `if (session.archivedAt) return false` 这条守卫。
|
||||
|
||||
- **验收标准:**
|
||||
- `grep -rn "ToggleArchivedView\|showArchivedSessions\|archivedCount\|showingArchived" frontend/src` 无命中。
|
||||
- `grep -rn "归档" frontend/src --include=*.tsx --include=*.ts`(非测试)无命中。
|
||||
- `sidebar-contract.test.ts` 补一条:行菜单项恰好是 {重命名、收藏/取消收藏、分享、删除},不含归档——**这条是防复发的主断言**。
|
||||
- 会话删除路径的测试全部不变且仍绿。
|
||||
|
||||
### T2|服务端下线(BUG-991)
|
||||
|
||||
- `GET /api/sessions`:删 `archived` 查询参数与 `isArchivedSessionQuery`,列表恒查未归档;`session-cursor.ts` 里 `isArchivedSessionQuery` 若再无调用者一并删除。
|
||||
- `chat-session-write-contract.ts`:`chatSessionMetadataPatchSchema` 去掉 `archived_at`;`extractChatSessionMetadataPatch` **保留** `"archived_at" in record` 的读取但不放进 patch(D2/红线 6 的向后兼容),并在注释里写明「旧 bundle 仍可发送,服务端解析后忽略」;`ChatSessionMetadataPatch` 类型去掉该字段。
|
||||
- `/api/sessions/[id]` 的 PATCH 不得再写 `archived_at`。
|
||||
- **验收标准:**
|
||||
- `chat-session-authority.test.ts`:新增「PATCH 收到 `archived_at` 返回 2xx 且不落库」的合同断言(与既有 `messages` 那条同形)。
|
||||
- `database-session-list-visibility.test.ts`:**删除归档视图那一半断言**(原值:`applyArchiveFilter(..., true)` 返回 `[archivedRectification]`;新值:删除;原因:归档视图已下线,该分支不再有调用者)。**保留**「`archived_at` 非空的行不出现在列表里」这条正向断言——列还在,守卫仍需被测。同步收敛 `cloudListIncludesSession` 的六组组合到三组。
|
||||
- `local-postgres-not.test.ts` 一字不动仍绿(D3)。
|
||||
|
||||
### T3|存量数据放回列表(BUG-991)
|
||||
|
||||
- 新增向前业务迁移 `frontend/supabase/migrations/<ts>_retire_chat_session_archive.sql`:
|
||||
`update public.chat_sessions set archived_at = null where archived_at is not null;`
|
||||
幂等(跑第二次匹配 0 行),带注释说明列保留待后续轮次删除。
|
||||
- 迁移器会在部署前自动应用(AGENTS.md §7.6),与 T1/T2 同轮安全:迁移只清数据不改结构,对当前已部署的旧代码向后兼容(旧代码读到的只是「没有归档会话」)。
|
||||
- **验收标准:**
|
||||
- `npm run test:db` 中新增或扩展一条:迁移后 `select count(*) from public.chat_sessions where archived_at is not null` = 0,重复执行迁移仍 0 行受影响。
|
||||
- 迁移不得触碰 `pinned`、`messages`、`updated_at`(**尤其不得 bump `updated_at`**,否则全部历史会话会一起跳到列表顶端并被当成今天活动过——这会直接毁掉刚修好的 BUG-988)。
|
||||
|
||||
### T4|记录(BUG-991)
|
||||
|
||||
- `docs/BUG_HISTORY.md` 的 BUG-991:`investigating` → `resolved`,补修复、验证、修复版本;防复发写「删除某个视图的唯一入口时必须同轮下线其数据链路,或留一条断言入口存在的测试」。
|
||||
- `CHANGELOG.md`:一句用户能懂的——归档功能撤掉了,以前归档过的对话回到列表里,删除不受影响。
|
||||
- `frontend/DESIGN.md`:会话行菜单的项目清单同提交更新。
|
||||
- `docs/testing/session-list-title-and-order-20260906.md` 第 63–67 行、`docs/testing/home-state-lowering-batch2-20260916.md` 的 C2 与 C4 里涉及归档视图的条目:划掉并注明「归档能力已于 2026-09-21 下线(BUG-991)」,不要删除整份清单。
|
||||
- `docs/tasks/README.md` 状态板加行。
|
||||
|
||||
---
|
||||
|
||||
## 6. 交付前必须全跑
|
||||
|
||||
- `cd frontend && ./node_modules/.bin/tsc --noEmit` → 0 错
|
||||
- `npm run lint` → 0 error,warning 不得超过 **119**
|
||||
- `npm test` → 全量。**本轮测试总数会下降**(删了归档断言),必须逐条列出删了哪些、原值新值原因;失败清单与基线逐条比对
|
||||
- `npm run test:db` → T3 必跑;无 Docker 时按 §7 让步
|
||||
- `npm run build` → `/` 仍 `○ Static`,首屏 gzip 与基线 ±2%(预期略降)
|
||||
|
||||
---
|
||||
|
||||
## 7. 让步顺序
|
||||
|
||||
1. **无 Docker** → `test:db` 跑不了:迁移与测试照写照提交,`BLOCKED.md` 记明,最终以门禁 run 转绿为准;BUG-991 在门禁绿之前不得标 `resolved`。
|
||||
2. **T3 迁移与 T1/T2 拆轮** → 允许,但顺序必须是**先 T3 后 T1/T2**:先把存量放回列表,再拆掉读取归档的能力。反过来会让已归档会话在两次部署之间彻底看不到。
|
||||
3. T1、T2 不得拆开单独交付——只删界面不删接口,等于把今天这个半截状态原样留着。
|
||||
|
||||
---
|
||||
|
||||
## 8. 开工前置命令
|
||||
|
||||
```bash
|
||||
cd /workspace/Jyotisha
|
||||
git status -sb | head -1
|
||||
git fetch origin --prune
|
||||
git worktree add -b codex/archive-retire-20260921 \
|
||||
.worktrees/archive-retire-20260921 origin/staging
|
||||
cd .worktrees/archive-retire-20260921/frontend
|
||||
ln -s /workspace/Jyotisha/frontend/node_modules node_modules # 或 npm ci
|
||||
./node_modules/.bin/tsc --noEmit
|
||||
npm test 2>&1 | tail -20 # 记录基线总数与失败清单
|
||||
grep -rn "归档" ../frontend/src --include=*.tsx --include=*.ts | grep -v '\.test\.' # 记录开工命中
|
||||
```
|
||||
|
||||
开工必读:`docs/BUG_HISTORY.md` 的 BUG-991、BUG-990、BUG-553;AGENTS.md §2、§3、§5、§6、§7(尤其 §7.3 改断言三栏说明、§7.6 迁移拆轮)。
|
||||
|
||||
纯前端 + 一条数据迁移,不要求 `scripts/pre_work_check.py`。
|
||||
|
||||
---
|
||||
|
||||
## 9. 部署后真人走查(产品负责人)
|
||||
|
||||
1. 会话行的「…」菜单里不再有「归档」。
|
||||
2. 以前归档过的对话重新出现在侧栏(如果你归档过的话),且它们的时间**没有**被改成今天。
|
||||
3. 删除会话仍然正常。
|
||||
Reference in New Issue
Block a user