Files
Jyotisha/docs/tasks/TASK-consultation-subject-binding-20260922.md
T
2026-09-22 11:19:35 +08:00

7.9 KiB
Raw Blame History

任务书 · 普通聊天人物资料服务端真值与会话绑定(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. 硬红线

  1. 不得只改 activeChartId、顶部文案或客户端 request body 就声称修复。
  2. 不得信任客户端 name/year/month/day/hour/minute/city/lat/lon/tz 覆盖服务端 resolver。
  3. other 必须校验当前用户所有权、存在性、role、资料完整性;越权、伪造 role/name、删除或缺失必须 fail-closed,不得 fallback 到 self。
  4. 有消息的会话不得 PATCH 改绑;若实现层需要字段存在,必须由服务端拒绝并覆盖测试。
  5. 不改数据库结构,除非任务书另列迁移;不改计费语义、模型选择、普通聊天以外入口。
  6. 不把出生资料、姓名、会话原文或凭据写进测试 fixture、日志、BUG 历史或任务记录;fixture 只能使用 synthetic/golden 数据。
  7. 不增加 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 核对一致。