# 任务书 · 开始聊天选择人物与会话对象锁定(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.md` Chat header 目前是一行 quiet chart chip;本轮改动必须同步 DESIGN,并保持 44px 透明命中区、暖色 palette、popover 120ms 与 reduced-motion。 ## 2. 决策记录(产品已拍板) - 第一条消息发送前可以选择人物;本人可作为明确的默认选中项,但必须让用户看得到当前对象。 - 第一条消息发送后人物锁定;需要换人时新建会话,不改绑旧会话。 - 历史会话人物绑定固定;打开历史会话不得修改其 binding。 - 人物资料编辑后,后续普通聊天读取该人物最新资料。 - 人物删除后旧会话可阅读,但发送阻断,不自动退回本人。 - 顶部人物名是人物入口;空会话可选择,有消息会话点击其他人物时走“新建会话并使用此人物”路径,不能静默改绑。 ## 3. 硬红线 1. 不得只改 `activeChartId` 或 localStorage;选择必须改变新会话的服务端 binding,或创建新会话。 2. 有消息的会话不得改人物;不得通过重写 `chart_profile_name` 伪造切换。 3. 人物列表和错误态不得泄露其他账户资料;删除人物不得回退 self。 4. 使用现有 `ChatComposer`、`useConversationScrollAnchor`、既有 popover/加载机制;不新增输入框、滚动跟随或 spinner。 5. `Home()` 的 useState/useRef 和行数增长冻结不得被绕过;新状态放 hook/lib/组件。 6. 所有可见文案对照 `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. 开工前置命令 ```bash 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 时写环境缺口,不得写通过。