6.6 KiB
任务书 · 开始聊天选择人物与会话对象锁定(2026-09-22)
0. 基线与串行依赖
- 基线 commit:
1bc6a954c597e2817fe72bfedad93119ab19a527;执行方开工前必须 fetch 并以最新origin/staging重核。 - 执行分支:
codex/chat-subject-picker-20260922;工作树:.worktrees/chat-subject-picker-20260922。 - 强依赖:必须在
TASK-consultation-subject-binding-20260922.md的服务端 resolver、/api/consult接入和服务端测试合入 staging 后再开工;不得两个分支并行改会话 binding 或聊天顶栏。 - 与首页可靠性、starter-entry 和图标动效任务遵守文件级串行:若这些任务尚未合入,不能同时修改
page.tsx、starter-home.tsx、聊天顶栏或相关共享 CSS;功能绑定先于 UI 选择器,入口视觉和图标审计随后进行。 - 本轮 other 只覆盖普通聊天。报告、每日星语、生时校正不接入人物选择器。
1. 会议事实与现状
会议明确:切换其他人物后聊天不能继续使用本人资料;“我的星盘”不适合查看他人,建议改为“当前星盘”;业务认可点击聊天顶部人名展开人物列表;开发侧提出开始聊天时选对象,会话进行中不再更换,业务认可方向但未展开所有边界。
代码事实:
frontend/src/hooks/use-session-management.ts的新会话目前隐式继承账户级activeChartId,没有开始聊天前的显式确认流程。frontend/src/app/(app)/page.tsx的.chat-header-chart当前是静态span,没有 button/popover/键盘语义。frontend/src/components/chart-library-panel.tsx是账户设置资料管理,不应整块复制成聊天选择器。frontend/src/lib/home-profile.ts已有chartSnapshotForSession与sessionChartLabel;ChatSession已有 chart profile 三字段,但 UI 选择必须最终写会话 binding,而不是只改 localStorage。frontend/DESIGN.mdChat header 目前是一行 quiet chart chip;本轮改动必须同步 DESIGN,并保持 44px 透明命中区、暖色 palette、popover 120ms 与 reduced-motion。
2. 决策记录(产品已拍板)
- 第一条消息发送前可以选择人物;本人可作为明确的默认选中项,但必须让用户看得到当前对象。
- 第一条消息发送后人物锁定;需要换人时新建会话,不改绑旧会话。
- 历史会话人物绑定固定;打开历史会话不得修改其 binding。
- 人物资料编辑后,后续普通聊天读取该人物最新资料。
- 人物删除后旧会话可阅读,但发送阻断,不自动退回本人。
- 顶部人物名是人物入口;空会话可选择,有消息会话点击其他人物时走“新建会话并使用此人物”路径,不能静默改绑。
3. 硬红线
- 不得只改
activeChartId或 localStorage;选择必须改变新会话的服务端 binding,或创建新会话。 - 有消息的会话不得改人物;不得通过重写
chart_profile_name伪造切换。 - 人物列表和错误态不得泄露其他账户资料;删除人物不得回退 self。
- 使用现有
ChatComposer、useConversationScrollAnchor、既有 popover/加载机制;不新增输入框、滚动跟随或 spinner。 Home()的 useState/useRef 和行数增长冻结不得被绕过;新状态放 hook/lib/组件。- 所有可见文案对照
frontend/docs/VOICE.md;交互合同同步frontend/DESIGN.md;源码符号/旧文案删除前先git grep -n覆盖tests/ frontend/。
4. 任务分解与验收标准
T1 · 会话启动选择器
- 从 chart library 读取本人和当前用户拥有的 other 资料,建立独立的
ChatProfilePicker/等价组件,不复制账户设置编辑表单。 - 空会话首次发送前显示当前人物与切换入口;选择状态清晰;资料加载中/失败/不完整有明确非阻塞状态。
- 新会话创建时写入服务端确认过的
chart_profile_id,服务端名称/关系不能被客户端任意覆盖。
验收:新建聊天能看到 self + owned other;选择 other 后创建/发送的会话绑定正确;未选择时默认行为可理解且不依赖隐式 localStorage;加载失败不创建错误 binding。
T2 · 顶部当前星盘入口
- 将
page.tsx中.chat-header-chart静态 span 改为可访问 button/等价控件,名称改为“当前星盘”语义,实际显示当前人物名。 - click、Enter、Space 均能打开/关闭 popover;实现
aria-expanded、aria-controls、焦点回收、Escape、点击外部关闭。 - 列表显示本人、owned other、当前选中、资料删除/不可用状态;至少 44px 触控目标,遵循现有 popover motion。
验收:键盘和触摸均可用;顶栏仍保持单行/移动端尺寸;不引入第二个顶部入口或第二套列表。
T3 · 已有消息的锁定行为
- 空会话选择人物直接更新该会话的待发送 binding。
- 有消息的会话选择其他人物不改旧会话;给出“新建会话并使用此人物”的单一路径,创建后才发送。
- 发送中/流式请求期间不允许创建冲突 binding;等待当前请求结束后再执行新会话动作。
验收:旧会话历史和服务端 subject 不变;新会话绑定新人物;刷新、深链、跨设备仍一致;删除人物显示阻断而不是改成本人。
T4 · UI/行为回归
至少覆盖:picker self/other、默认状态、空会话选择、已有消息锁定、新建会话路径、键盘/焦点/44px、资料加载失败、删除资料、刷新/深链。扩展现有 chart-library-session、session-open-preserves-identity、chat session write/authority 合同;不删除旧测试名。
5. 让步顺序
T1 服务端绑定接线 > T2 可访问入口 > T3 锁定/新建路径 > T4 细节视觉。若无法安全实现顶部入口,宁可保留静态显示并记录 blocked,不得交付假切换。
6. 开工前置命令
git status -sb
git fetch origin --prune
# 确认 consultation-subject-binding 已合入 origin/staging 后再创建本分支
git worktree add -b codex/chat-subject-picker-20260922 .worktrees/chat-subject-picker-20260922 origin/staging
cd .worktrees/chat-subject-picker-20260922/frontend
./node_modules/.bin/tsc --noEmit
npm run lint
npm test
npm run build
7. 记录与验收
同轮更新 frontend/DESIGN.md、必要的 frontend/docs/VOICE.md、docs/tasks/PROGRESS-chat-subject-picker-20260922.md 和 docs/tasks/README.md 状态板;若确认原始人物错配 Bug 尚未有记录,按开工时 BUG_HISTORY.md 最大编号 +1 连续登记并关联相关历史。浏览器级登录验收写入 docs/testing/;未具备登录态/Chrome 时写环境缺口,不得写通过。