Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Ad9rqAB8pXXvyzh7f4X8v
9.1 KiB
9.1 KiB
任务书:从合盘报告进入对话问依据(2026-10-18)
基线与顺序
origin/staging以开工时最新为准(git fetch origin --prune)。- 分支:
codex/synastry-report-chat-20261018;工作树:.worktrees/synastry-report-chat-20261018。 - 前置:
TASK-synastry-full-report-20261018.md(#82)合入后开工。本单读 #82 存下的合盘报告与每节分析逻辑。 - 上线:与 #79、#82 一起提升到
main。#82 的「对哪一段想再问清楚?」入口在本单合入前隐藏,本单负责打开它。 - 原型(v13,报告末尾的入口):https://claude.ai/artifact/Tq6vhXFMnGitDZhJdamaHe
现状实证(按符号定位)
| # | 事实 | 位置 |
|---|---|---|
| 1 | 对话只能绑一个人:chat_sessions.chart_profile_id / chart_profile_name / chart_profile_role;人物选择器单选 |
迁移 20260830010000_chat_session_chart_profile_binding.sql;components/chat-profile-picker.tsx、lib/chat-profile-picker-model.ts |
| 2 | 会话已有「类型 + 外键」的先例:session_type(consultation / birth_time_rectification)+ rectification_case_id,非普通会话锁定人物选择器 |
迁移 20260721110000_chat_session_surface_metadata.sql;lib/rectification-in-chat*.ts、chatSessionSubjectLocked |
| 3 | 建会话:POST /api/sessions,chatSessionCreateSchema 为 .strict()、session_type 是枚举;服务端用 authoritativeSessionBinding 校验绑定 |
app/api/sessions/route.ts、lib/chat-session-write-contract.ts、lib/consultation-subject-resolver.ts |
| 4 | 「新会话 + 预填问题」只有 continueInNewChat 一处(setDraft(prompt.question));地址栏只认 c= 与 new=1 |
hooks/use-session-management.ts、lib/chat-session-url.ts |
| 5 | 每轮对话固定先调 run-jyotish-consultation(单人本命),再按同源卡回答;证据查阅工具只认固定的 EVIDENCE_LOOKUP_SECTIONS |
mastra/index.ts(jyotishInstructions)、mastra/consultation-tools.ts、lib/consultation-evidence-card.ts |
| 6 | 入口枚举 consultationEntrypointSchema(daily_starlanguage / birth_time_rectification / guided_topic);有入口时跳过闲聊分类 |
lib/consultation-entrypoint.ts、lib/consultation-smalltalk.ts |
| 7 | 计费:每轮 POST /api/consult 预留 chat.standard;开会话不扣点 |
lib/consult/settlement.ts |
| 8 | 护栏:Home() 的 useState ≤ 36、useRef ≤ 37;普通对话提示词 + 卡 ≤ 20,000 字符(含方法论 ≤ 26,000) |
frontend/tests/home-shell-growth-contract.test.ts;consult-card-full-source-20261007.test.ts 等 |
决策记录(产品负责人 2026-10-18)
| # | 决定 |
|---|---|
| G1 | 合盘报告正文是关系语言,印度占星的依据要能讲给用户听,引导到对话里(产品 10-18)。入口只在报告末尾一处:「对哪一段想再问清楚?」列出第一到八节标题(含关系时间线),选一节进入对话 |
| G2 | 点一节 → 新建一个绑定这份合盘报告和这一节的对话,输入框预填「合盘报告里『<节名>』这一段,在我们两人的盘里依据是什么?」(英文报告用英文),由用户自己点发送,不自动发出 |
| G3 | 对话里可以用印度占星术语(星曜、宫位、分盘、大运),这是它和报告正文的分工;仍守 VOICE 的口吻,判断禁词照旧(合不合、注定、必然、灵魂伴侣等);不替两人做决定 |
| G4 | 回答只依据这份报告已经存下的计算结果(证据包快照、每节分析逻辑、时间线分段、技术依据摘要),不在对话里重新排盘、不新增结论;问到报告没覆盖的内容,如实说明这份合盘没算到,并建议回到各自的盘单独问 |
| G5 | 计费照普通对话:每轮 chat.standard,开会话不扣点 |
| G6 | 对话侧栏里这类会话标题写「A × B · 合盘追问」;当前人物是两人中任意一个时都显示;人物选择器锁定显示「A × B」 |
| G7 | 报告被删或任一人被删:报告随之删除(#82),这些会话保留文字记录,但不能再继续提问,输入框处显示「这份合盘已删除」 |
| G8 | 不许瞎说(产品 10-18 追问「对话能拿到合盘数据吗,不是在瞎说吧」):今天的普通对话拿不到合盘数据,本单就是把数据接进来。三道保证:① 每轮回答前必须先调「读取合盘报告某节材料」工具,没拿到材料不回答;② 回答里提到的每一个盘面事实(某星落某宫 / 某星座、某两星相位、某段大运与起止)都要能在本轮工具返回的材料里找到;③ 回答生成后由程序逐条核对②,对不上就带着差异重写一次,仍对不上则改为固定答复「这一点在这份合盘里没有算到,我不能确定」,不输出未核实的说法 |
硬红线
scripts/jyotish_api_server.py不动;app/(app)/page.tsx的Home()状态数不增长(新逻辑进 hook / lib)。- 普通单人对话的提示词、同源卡、工具集一字不改;合盘追问用独立的提示与工具,独立的长度上限测试(提示 + 本节材料 ≤ 20,000 字符)。
- 证据只从本人拥有的报告里读(服务端按
user_id校验,行级安全照旧);不把出生日期、出生地、经纬度放进提示。 - 改既有断言写三栏;动表必须真跑
npm run test:db;只加列、加枚举值,不删旧列。 - 合盘追问会话不进入单人对话的记忆与上下文继承(
continued_from_session_id、context_summary不跨类型)。
任务分解
| # | 内容 | 验收标准 |
|---|---|---|
| T1 | 数据:chat_sessions.session_type 加 synastry_report;加 synastry_report_id(指向 personal_reports.id,删除时置空并标记会话只读)与 synastry_section(节键)。chatSessionCreateSchema、authoritativeSessionBinding 同步:该类型必须带本人拥有的 synastry_full 报告 id 与合法节键 |
test:db:他人报告 id 被拒;节键非法被拒;删报告后会话仍在、标记只读 |
| T2 | 入口:#82 阅读页「对哪一段想再问清楚?」的节按钮 → 建会话 → 跳到 c=<id> 并预填问题(G2);去掉 #82 里的隐藏开关 |
渲染测试;点按钮不调用 /api/consult、不扣点 |
| T3 | 对话链路:POST /api/consult 遇到 synastry_report 会话,走独立分支:不调 run-jyotish-consultation;用新的合盘追问提示(建议 mastra/synastry-report-chat.ts)+ 一个只读工具「读取合盘报告某节材料」(返回该节结论、分析逻辑、对应证据条目;时间线节返回分段与两人大运;可另取技术依据摘要),每轮强制先调用(G8①);入口枚举加 synastry_report,跳过闲聊分类;计费照 chat.standard。回答核对(G8②③):新增 lib/synastry-chat-fact-check.ts,从回答里抽出星曜、宫位、星座、相位、大运与日期的组合,逐条在本轮工具材料里查找;对不上 → 重写一次 → 仍对不上走固定答复;核对结果记进本轮遥测 |
单测:不调用单人排盘;工具只能读本会话绑定的报告;工具没被调用时不输出回答;用名人报告材料造三条回答样例(全对 / 编了一个不存在的「金星在 7 宫」/ 大运起止写错一年),核对分别判通过、失败、失败;失败两次时用户看到的是固定答复;问报告外的内容时回答里有「这份合盘没算到」类说明 |
| T4 | 侧栏与选择器:标题「A × B · 合盘追问」;两人任一为当前人物时显示;选择器锁定显示「A × B」;只读会话的输入框状态(G7) | 渲染测试 |
| T5 | 记录:DESIGN.md(入口与只读态)、VOICE.md(合盘追问的口吻与术语边界)、CHANGELOG、docs/testing/synastry-report-chat-20261018.md 真人清单(从报告进对话、预填不自动发、追问两轮、问报告外问题、删报告后只读、英文报告进对话) |
文档齐 |
让步顺序
T1–T3 必须一起交,T3 的回答核对不可省(没有它本单不得合入);T4 若来不及,侧栏先按第一个人显示,写进 PROGRESS;不得为赶工复用单人对话的提示或工具。
开工前置命令
git fetch origin --prune
git worktree add -b codex/synastry-report-chat-20261018 .worktrees/synastry-report-chat-20261018 origin/staging
cd .worktrees/synastry-report-chat-20261018
git log --oneline -5 -- frontend/src/components/personal-report # 确认 #82 已合入
grep -n "BUG-1[0-9][0-9][0-9]" docs/BUG_HISTORY.md | tail -3
验证:前端 tsc --noEmit、npm run lint、npm test(含 grep 源码文本合同测试与 Home 增长护栏)、npm run test:db、npm run build(/ 仍 Static,首屏 gzip ±2%)。真实模型回答(无模型凭据时)写成环境缺口,部署后由 Claude 在 staging 用名人合盘走一遍。
BUG 编号
本单是产品需求;只为执行中发现的真实缺陷新增编号,起点为开工时最大号 + 1。