7.9 KiB
任务书 · 普通聊天人物资料服务端真值与会话绑定(2026-09-22)
0. 基线与依赖
- 基线 commit:
1bc6a954c597e2817fe72bfedad93119ab19a527(当前origin/staging);执行方开工前必须git fetch origin --prune并重新确认。 - 执行分支:
codex/consultation-subject-binding-20260922;工作树:.worktrees/consultation-subject-binding-20260922。 - 本单先于人物选择 UI。
TASK-chat-subject-picker-20260922.md必须等待本单的服务端 subject resolver 与集成测试完成后再开工;两轮不得同时改page.tsx、会话绑定或咨询 route。 - 本轮 other 资料只覆盖普通聊天;报告、今日星语、生时校正和其他入口另立任务,不得顺手扩范围。
- 与本日人物选择 UI 任务严格串行:本单先完成并合入 staging,
TASK-chat-subject-picker-20260922.md才能开工;两单不得同时改frontend/src/app/api/consult/route.ts、会话 binding 契约或聊天顶栏接线。
1. 事故实证
会议反馈:切换到其他人物后,聊天仍使用用户本人的资料。
调查已确认:
frontend/src/hooks/use-session-management.ts的startNewChat通过账户级activeChartId调用chartSnapshotForSession,会话已有chart_profile_id/name/role元数据,但没有服务端 subject 解析。frontend/src/app/api/consult/route.ts读取会话时只取chart_profile_role,不取chart_profile_id;随后通过frontend/src/lib/consultation-route-service.ts的loadProfile(userId)无条件读取登录用户的profiles。frontend/src/hooks/use-consultation-run.ts从全局profile组装客户端出生字段;这些字段不能作为服务端真值,否则会绕过所有权和资料完整性边界。frontend/src/app/api/chart-profiles/route.ts与[id]/route.ts有基本user_id所有权约束,但没有接入咨询真值。- 删除 other 资料后前端可能显示“资料已删除”,而
/api/consult仍回退到本人,形成 fail-open 和用户误导。
相关历史:BUG-001、BUG-010、BUG-018、BUG-073、BUG-076、BUG-011、BUG-188;当前最大号开工前重新核对。
2. 根因
会话绑定字段目前只是客户端可写/可读的展示元数据,不是服务端确认过的 subject binding。咨询服务没有按 chat_sessions.chart_profile_id 解析人物,而是固定使用当前登录用户 profiles,所以 UI、会话显示与实际排盘对象可以不一致。
3. 决策记录(产品已拍板)
| 决策 | 本轮口径 |
|---|---|
| 会话人物 | 第一条消息发送后锁定;如需换人,新建会话,不改绑已有消息的会话 |
| 资料编辑 | 会话保留人物 ID,后续普通聊天使用该人物最新资料 |
| 删除人物 | 旧会话保留阅读;资料不存在/被删除时阻断继续发送,不自动退回本人 |
| other 范围 | 本轮只打通普通聊天;报告、每日星语、生时校正另立任务 |
| self 真值 | self 始终映射当前用户权威 profiles,不信任 chart library JSON 或客户端出生字段 |
| 名称/关系 | 会话快照用于历史展示;排盘对象由服务端按 ID/role 重新解析 |
4. 硬红线
- 不得只改
activeChartId、顶部文案或客户端 request body 就声称修复。 - 不得信任客户端
name/year/month/day/hour/minute/city/lat/lon/tz覆盖服务端 resolver。 other必须校验当前用户所有权、存在性、role、资料完整性;越权、伪造 role/name、删除或缺失必须 fail-closed,不得 fallback 到 self。- 有消息的会话不得 PATCH 改绑;若实现层需要字段存在,必须由服务端拒绝并覆盖测试。
- 不改数据库结构,除非任务书另列迁移;不改计费语义、模型选择、普通聊天以外入口。
- 不把出生资料、姓名、会话原文或凭据写进测试 fixture、日志、BUG 历史或任务记录;fixture 只能使用 synthetic/golden 数据。
- 不增加
page.tsx的 Home 状态/引用,不新增第二个输入框、滚动跟随或加载动画。
5. 任务分解与验收标准
T1 · 服务端 subject resolver
新增独立服务端 helper(建议放 frontend/src/lib/,不要把实现堆进 route.ts):
- 输入当前
userId与会话 binding;读取服务端会话和 chart profile。 self→ 当前用户profiles;other→ 当前用户拥有的chart_profiles.profile。- 服务端重新派生 name/role;会话展示快照不作为排盘真值。
- 统一处理不存在、删除、越权、role 不一致、资料不完整,并返回稳定内部错误类别。
- 客户端出生字段只能被忽略或用于非权威一致性检查,绝不能覆盖 resolver。
验收:self/other 都由服务端真值生成;other 不会取当前用户 profiles;越权 ID、伪造 name/role、缺资料、删除资料均 fail-closed;错误不泄露内部数据库细节。
T2 · 接入咨询主链
frontend/src/app/api/consult/route.ts读取chart_profile_id及必要 binding 信息。- 将 resolver 注入
frontend/src/lib/consultation-route-service.ts的 prepare 流程。 - 保留会话 owner 校验、会话类型校验、计费前失败语义;resolver 失败不得扣点、不得进入模型调用。
- 只修改普通聊天咨询入口,不扩到报告、每日星语、校正。
验收:同一问题分别绑定 self/other 时,传入计算与最终咨询上下文的出生资料来自各自服务端 profile;resolver 失败时没有模型调用、没有扣点、公开错误可理解。
T3 · 会话绑定写入与不可变边界
frontend/src/app/api/sessions/route.ts与[id]/route.ts在写入时校验 chart profile ID/role/name 的一致性,或由服务端根据 ID 填充展示字段。- 未发送第一条消息的空会话可按产品规则创建绑定;已有消息的会话不得改绑。
- 刷新、深链、跨设备恢复保持 binding;打开历史会话不能只靠全局
activeChartId改变实际对象。
验收:客户端提交任意伪造 name/role 不能伪造会话主体;服务端保存的绑定可被再次解析;有消息后改绑明确失败;空会话策略与下一轮 picker 一致。
T4 · 服务端回归测试
新增或扩展测试,至少覆盖:
- self 使用权威 profiles;other 使用当前用户拥有的 chart profile;
- 另一用户 ID、随机 ID、role/name 不匹配、删除/缺失/不完整资料;
- 客户端出生字段与服务端资料冲突时仍使用服务端;
/api/consult集成确实把 other 资料交给 chart/consultation preparation;- resolver 失败不扣点、不调模型、不 fallback;
- 会话有消息后改绑拒绝;刷新/深链保留 binding;
- 并发删除与发送时结果确定且 fail-closed。
保留现有测试名;若改变既有断言,进度记录写原值/新值/原因。
6. 让步顺序
T1 > T2 > T4 > T3 的 UI 接线。若时间不足,优先完成 resolver、咨询接入和服务端集成测试;不得先交付只改前端显示的 picker。
7. 开工前置命令
git status -sb
git fetch origin --prune
git worktree add -b codex/consultation-subject-binding-20260922 .worktrees/consultation-subject-binding-20260922 origin/staging
cd .worktrees/consultation-subject-binding-20260922/frontend
./node_modules/.bin/tsc --noEmit
npm run lint
npm test
开工时核对:grep -n '^## BUG-' docs/BUG_HISTORY.md | tail -1。当前任务书编写时最大号是 BUG-995;若确认新 Bug,按开工时最大号 +1,若为 BUG-073/076 复发则关联原记录。
8. 交付与记录
实现同轮必须更新 docs/BUG_HISTORY.md,说明现象、触发条件、根因、修复、验证和防复发;如尚不能闭环,状态只能是 investigating 或 blocked。写 docs/tasks/PROGRESS-consultation-subject-binding-20260922.md,并在 docs/tasks/README.md 状态板登记。不得声称已部署,除非 staging SHA 与 /api/health.deployment.gitCommit 核对一致。