Files
Jyotisha/docs/tasks/TASK-synastry-full-report-20261018.md
T

24 KiB
Raw Blame History

任务书:婚恋合盘第二步——完整合盘报告(2026-10-18)

基线与顺序

  • origin/staging 以开工时最新为准(git fetch origin --prune)。
  • 分支:codex/synastry-full-report-20261018;工作树:.worktrees/synastry-full-report-20261018。
  • 前置:#78(档案上限 20 人)、#79(引擎、证据包接口、存储;第六版起不做页面)、#81(full-reading 补齐 5 个模块)三单都合入后开工。上游参照以 7a773d13 为准(见实证 8b)。三单彼此不冲突,可并行。
  • 上线:#79 与本单要一起提升到 main(#79 单独上线时旧入口还在、新入口不存在)。本单从 #79 合入后的 origin/staging 开分支,复用 #79 的引擎、证据包接口、选人页和 synastry_reports 新列。
  • 正文写作首要准则:docs/research/synastry_relationship_language_guide_2026_10_18.md(产品负责人 10-18 提供,「从星曜语言到关系语言」)。凡与上游写作规范冲突处,以它为准。
  • 原型(v13,关系画像 + 折叠的分析逻辑 + 时间线 + 技术依据摘要):https://claude.ai/artifact/Tq6vhXFMnGitDZhJdamaHe 。原型里的人名和正文全是虚构示例。
  • 上游参照:占星师仓库 yinduzhanxing 提交 bde5d615 的 skills/jyotish-full-synastry-reading/。上游仓库私有,原样副本在 docs/research/upstream_full_synastry_bde5d615/(skill/ 下是 Skill 本体,tests/ 下是五个测试文件;.py.txt 搬入时去掉 .txt)。

现状实证(按符号定位)

# 事实 位置
1 个人报告已经不用模型写:PERSONAL_REPORT_WRITER_ENABLED = false,正文是 Python professional_report_reference 渲染的 Markdown,中英两版同一次引擎调用产出 frontend/src/lib/personal-report-writer-flag.ts、personal-report-longform-generate.ts 的 generatePersonalReportLongform、scripts/professional_report_reference.py
2 旧的模型写作路径还在但不调用:按节写、节状态 pending / ready / blocked frontend/src/mastra/personal-report.ts(createPersonalReportAgent、buildReportPrompt)、personal-report-generation.ts、personal-report-section-service-core.ts
3 报告任务基础设施可复用:personal_report_jobs(queued / running / suspended / retrying / ready / failed / cancelled,max_attempts 3)、租约 600 s、心跳 20 s、阶段进度、删报告释放预留 personal-report-worker-core.ts、personal-report-worker.ts、迁移 20260814040000_personal_report_jobs_v2.sql、20260925040000_deleted_report_release.sql
4 personal_reports.report_type 只允许 personal_full / personal_thematic;同一枚举在 zod 请求、v2 文档 schema、列表标签里各有一份 迁移 20260806010000_personal_reports.sql、lib/contracts/personal-report-request.ts、contracts/personal-report/report-document.v2.schema.json、PersonalReportCenter
5 计费键写死在多处:SQL CHECK(feature_pricing.feature_key、resolve_feature_pricing、admin_save_feature_pricing_draft、usage_reservations.feature_key、权益表)与 TS(consultation-billing.ts 的 featureKey、后台定价 / 商品 / 用量聚合路由的 zod、feature-pricing-labels.ts、pricing-simulator.tsx、membership.ts)。feature_pricing 不带价格,价格由后台发布 迁移 20260831010000_feature_pricing.sql 等
6 阅读页轮询 /api/reports/:id,有等待面板与阶段文案;语言切换写 ?lang=en components/personal-report/personal-report-page.tsx、personal-report-progress.ts、report-language.ts
7 上游证据包在我们引擎上能直接跑:Claude 10-18 用两位公开名人(布拉德·皮特、安吉丽娜·朱莉,出生资料来自公开星盘库)在 origin/staging 上跑 full-reading 两次再跑上游 build_full_synastry_dossier.py,delivery_readiness = ready_for_narrative,九节写作素材齐全。整个证据包约 56 万字符;九节素材 3k~96k 字符不等 本地实测,未入库
8 与上游引擎相比我们缺 5 个模块(上游同一对人能算出):Lagna Vargottamamsa、Navamsha Dasha、Niryaana Shoola Dasha、Punarphoo、西占对照层。结果是「次要大运互证」一项 blocked,其余照常 由前置单 TASK-full-reading-module-parity-20261018.md 补齐
8b 上游 10-18 7a773d13 已修:D4 键名统一为 D4_Turyamsa,「共同生活」那条双人比对不再恒为 blocked;另加了单语言交付、把 Niryaana Shoola 从恋爱婚姻应期里排除。#79 搬的是 bde5d615 版本,本单开工第一步把 Skill 同步到 7a773d13(改动的 6 个文件与测试副本在 docs/research/upstream_full_synastry_7a773d13/;marriage_match.py 两版相同) 上游 main 7a773d13
9 上游五个测试在我们这边 46 / 49 通过。3 条失败:① scripts/aspects.py 比上游旧(上游 09-24 改为按方向角和速度判相位,我们没同步);② 一条读上游桌面端 jyotish-app/main.js,我们没有该文件;③ 一条调上游 API 主文件里的 _compute_marriage_match,我们按薄注册另写 同上

决策记录(产品负责人 2026-10-18)

# 决定
E1 完整报告就是合盘结果:点「开始合盘」即生成整份报告(要等几分钟),走报告任务的后台生成与等待面板;没有先出的即时结果页
E2 先写中文,切到英文时再写:英文不翻译中文稿,从同一份证据包按同一准则重新写;第一次切换时生成,不另扣点
E3 单独计费项 synastry.full,默认价格同个人完整报告(report.full)。价格由后台发布,代码里不写死。同一对人、两人出生资料没改、规则版本没变,再次打开读已有报告,不扣点
E4 报告写作重新启用模型,但只用于合盘报告;个人报告保持引擎渲染不变(PERSONAL_REPORT_WRITER_ENABLED 不动)
E5 正文是关系画像(产品 10-18 第三版,推翻第一版「九节 + 传统配对参考」):按准则文件把星曜语言翻译成关系经验语言。结构九节:一、关系基调(底层天气);二、情感节奏;三、沟通与理解;四、安全感与亲密;五、冲突与修复;六、生活节律与现实协作;七、长期方向;八、关系时间线(E6);九、共同练习(两三条可操作的建议)。之后是「对哪一段想再问清楚」入口(E5d)、「可以一起聊聊」四问、折叠的「技术依据摘要」(E5b)、脚注。开头不放分数、不放结论档位
E5b 正文最后一个默认折叠的「技术依据摘要」(标题就叫「技术依据摘要」,英文 Technical basis,不加「给懂占星的人看」之类的副标题;产品 10-18;格式照上游:full-technique-execution-matrix.md 要求报告最后有技术附录、每项技法标明执行状态,SKILL.md 要求传统配对只能放附录)。全部由程序从证据包生成,不经模型,六块:① 计算参数(岁差、交点、宫制、两人出生时间精度,不显示出生日期地点);② 各自的婚恋结构(上升、7 宫与宫主、金星、月亮、DK、UL / UL 二宫、A7、D9 上升与 7 宫主、关键功能吉凶星);③ 两盘交感(各节分析逻辑里用到的那几条);④ 大运对照(时间线覆盖期间两人的大运 / 子运与起止);⑤ 传统配对参考(七项 35 分含例外候选、两人逐处的火星冲克与 Raman 一派差异、婚姻宫压力,标「只作一层参考,不决定结论」);⑥ 技法执行情况(按上游执行矩阵逐行:已执行 / 参数敏感 / 无法计算 / 不适用;最后一行固定写「外部资料与真实案例核对:未进行,本报告是计算层归纳」)
E5e 每节一个默认收起的「分析逻辑」(产品 10-18 转达占星师:「原因写成分析逻辑,想看的人点开,不看的可以不点」;推翻同日「原因直接展示不折叠」)。第一到七节结论段落之后放一个折叠块「分析逻辑」,展开后两到三步,每步两行:「盘面」一行写盘面事实(可以出现星曜、宫位、分盘、大运,必须对得上证据包里的具体条目),「所以」一行写由此得出的关系判断(白话,不含术语)。时间线每段也带一个同样的折叠块。结论段落本身不出现术语
E5d 依据可以去对话里问(产品 10-18):不在每节挂按钮。报告末尾一个入口「对哪一段想再问清楚?」,列出第一到八节的标题,选一节进入一个绑定这份合盘报告的对话,由对话用印度占星的语言细讲。对话侧另立任务书(Claude 写),与本单、#79 一起上线;对话侧合入前这个入口不显示
E5c 写法守五条:说「你们之间」不说「你 / 他是什么人」;说倾向不说必然;说需求不说缺点;给条件不给结论;保留双方主体性。可以写「一方……另一方……」或点名谁需要什么,但不得把一方写成问题源
E6 关系时间线(产品 10-18,采纳占星师「要加入大运:这段关系在哪段时间会发生什么」,推翻同日「只写大致阶段、不提大运」):取两人接下来约五年的 Vimshottari 大运 / 子运分界,合并成三到五段(短于 6 个月的段并入相邻段)。每段:起止年月、阶段名(如「靠近、了解」「两人同步,适合确认」「现实考验」)、一段关系语言的描述、一个折叠的「分析逻辑」(盘面:两人各自的大运 / 子运与起止、相关行运)。按上游规则,两人时间重叠本身不算共同事件:每段写成「两人各自走到什么阶段 → 这段关系在这期间会是什么样子」,不写「某年结婚 / 领证 / 分开」。不收集两人现在的关系阶段与经历
E7 上游的发版前检查(pre_work_check)、全网资料核验(MEVG)、书库检索是写稿代理的开发流程,不进产品运行时;报告脚注照常写「计算层归纳,不是定论」
E8 缺的 5 个模块全部补上(产品 10-18):TASK-full-reading-module-parity-20261018.md,必须在本单开工前合入

引擎与写作链路

选人页「开始合盘」
  → POST /api/reports(reportType = synastry_full,两个人物 id)
  → 预留 synastry.full → 建报告行 + 任务
  → worker:取两人出生资料
      → Python POST /api/synastry_full_dossier(#79 已做):两次 full-reading → 上游证据包 + 两个 profile 的 marriage_match
      → 时间线分段(确定性:两人大运 / 子运分界合并成三到五段,「盘面」行由程序生成)
      → 九节中文:每节一次模型调用,只喂这一节对应的证据(映射见下表)与公共上下文,并发 ≤ 3
      → 中文质检(Python,本单自己的关系语言质检)
          不过:只重写不过的节,最多 1 轮,把失败原因附进提示
          仍不过:任务失败、释放预留
      → 存:中文九节(含分析逻辑)、时间线分段、证据包快照、质检回执
  → 结算 synastry.full
阅读页切到英文(第一次)
  → POST /api/reports/:id/english → 任务(不扣点)→ 九节英文 + 英文质检 → 存

九节与证据映射

节 英文标题 喂给模型的证据(上游证据包字段)
一、关系基调 Relationship Climate reader_section_brief 的 relationship_thesis、what_each_person_brings;relationship_lenses 总览
二、情感节奏 Emotional Rhythm emotional_safety
三、沟通与理解 Communication and Understanding communication_conflict_and_repair(沟通部分)
四、安全感与亲密 Safety and Intimacy attraction_and_closeness + mutual_affection_activation(两个方向都要写:什么让对方感到被重视、什么让对方退缩)
五、冲突与修复 Conflict and Repair communication_conflict_and_repair(冲突与修复部分)、can_it_stay_steady 的修复部分
六、生活节律与现实协作 Daily Life and Practical Partnership shared_life_gains_and_pressures
七、长期方向 Long-term Direction relationship_progression、can_it_stay_steady(D9 双人互动已执行时必须用上,结论段落不提 D9,「盘面」行可以提)
八、关系时间线 Relationship Timeline 程序分好的三到五段(两人各自的大运 / 子运、起止)+ parallel_timing_context、event_resonance_gate;模型只写每段的阶段名和描述,「盘面」行由程序生成
九、共同练习 Practices to Share 前八节的依据;每条建议必须能回指一条具体依据

marriage_match(35 分、火星冲克、婚姻宫压力)作为背景放进第二、四、五、六节的输入;结论与白话推理里只能翻译成关系语言(例如「压力下一个追、一个退」),「盘面」行可以写名称,但不写分数。

模型提示(新文件,建议 frontend/src/mastra/synastry-report.ts)

  • 系统提示以准则文件为主体(原文要点全部进提示,含五条写法与示例);上游 chinese-synastry-writing-contract.md、customer-report-shape.md 只取「证据怎么用」的部分:每个判断要有两条同向依据、冲突要降级、时间要有条件、补充方法(宿曜、西占、问卦)不得点名。上游要求在正文里写术语、写「七宫主落……」句式、点名 D9 的部分不采用。英文提示同理,以准则文件为主体,参考 english-synastry-writing-contract.md 的证据部分。
  • 每节输入:两人称呼(档案里的称呼,不给出生日期、出生地)、这一节的标题与要求、上表对应的证据。
  • 第一到七节输出 JSON:{ text, reasons: [{ plain, basis, evidence, label }] },reasons 两到三条,渲染成「分析逻辑」的步骤:basis 是「盘面」行(可含术语),plain 是「所以」行(白话,不含术语),evidence 指向这一节输入里的具体条目,label 取上游六个证据标签之一(只存不显示)。第八节输出 { segments: [{ id, phase, text }] }(id 对应程序分好的段);第九节输出 { items: [{ text, evidence }] }。

中文 / 英文质检(本单自己的,scripts/synastry_full_reader_gate.py)

上游校验器(validate_bilingual_synastry_delivery.py)原样搬入不改,但它的读者规则与准则冲突(要求正文出现 D9、要求首句含特定靠近感词),所以不用它判正文;只复用它的 PRIVATE_EVIDENCE_TERMS、SUPPLEMENTARY_METHOD_TERMS 两张表。本单自己的质检,中英各一套,逐条:

  1. 九个标题齐全、顺序对;第一到七节结论段落中文至少 120 字(英文 80 词),全文中文至少 1800 字(英文 1100 词);第一到七节每节有两到三条原因;时间线三到五段。
  2. 术语禁表(只查结论段落、「所以」行、时间线描述、共同练习;「盘面」行不查)(中文):行星名(太阳、月亮、水星、金星、火星、木星、土星、罗睺、计都)、「宫」、星座名、星宿、上升、分盘 / 九分盘 / 婚姻分盘、D1 / D9 / D 加数字、大运、小运、行运、Dasha、Koota、Kuja、火星冲克、Nadi、Yoni、Varna、Ashtakoota、Papasamya、UL、DK、「数字 + 分」、满分。英文对应一套(planet names、house、sign names、nakshatra、ascendant、Navamsa、D9、dasha、transit、koota、points/score 等)。
  3. 判断禁表:克、煞、凶、吉、注定、必然、命中、一定会、合不合、不合、相合、不适合、适合结婚、不宜、灵魂伴侣、宇宙安排、百分百、会不会在一起;「年份 / 月份 + 结婚 / 领证 / 分手 / 离婚」。
  4. 上游私有词表、补充方法名表一个都不能出现。
  5. 首句不得是分析腔(「这段关系的核心不是……」「不是有没有……」)。
  6. 每条原因的 evidence 能在这一节的输入里找到,basis 里出现的星曜 / 宫位 / 大运与该条证据一致(逐字段比对,不一致即失败);第九节每条建议都有 evidence;时间线的「盘面」行由程序生成,模型不得改写。

不过的节带着失败原因重写一次,仍不过则任务失败、释放预留。

硬红线

  1. scripts/jyotish_api_server.py 不增长(类方法数、__new__ 伪造点、行数护栏);新接口在独立模块,主文件只加注册行。app/(app)/page.tsx 的 Home() 状态数不增长。
  2. 上游 skills/jyotish-full-synastry-reading/ 的脚本与参考文件原样搬入,diff 为空;不得为了让我们这边的测试通过去改上游文件。
  3. 结论段落、白话推理、时间线描述、共同练习不得出现任何分数、Koota、星曜、宫位、分盘、大运等术语;判断禁表、补充方法名、私有证据词全文都不得出现。术语只允许出现在「分析逻辑」的「盘面」行、折叠的「技术依据摘要」和对话里。质检必须拦住这些。
  4. 模型提示不得带出生日期、时间、出生地和经纬度;证据包快照只存在报告自己的行里,随报告删除;日志不打证据包和正文。
  5. 扣点:任务失败、超时、质检两轮不过、用户中途删报告 → 释放不扣;读已有报告不预留;英文版不扣点。模型 token 用量照常记账(kind: "synastry_full"),进成本统计。
  6. 动表的轮次真跑 npm run test:db;只加列、加枚举值、加键,不删旧列(AGENTS §7.6)。
  7. 一次等待一次揭幕:等待用报告现有等待面板;正文整份出现,揭幕后无 spinner / 骨架。
  8. 合同测试 fixture 用真实引擎输出:两位公开名人的证据包(可截取到测试需要的字段,写明截取规则),不得手造形状。

任务分解

# 内容 验收标准
T1 数据:personal_reports.report_type 加 synastry_full;加两人引用列(另一人 partner_chart_profile_id 或等价字段,本人用约定值)与 synastry_report_id(指向 #79 的 synastry_reports 行,带两人出生资料指纹与 rules_version)。证据包快照、中文九节(含分析逻辑)、质检回执存进 personal_report_sections(与个人报告快照同一套)。zod、v2 schema、列表标签同步 test:db:同一对人同指纹第二次找到已有报告;任一人改资料后找不到;删任一人后报告与合盘行一并消失
T2 计费键 synastry.full:一个迁移把键加进实证 5 列出的所有 SQL CHECK 与函数;TS 侧同步所有枚举与后台标签(「完整合盘报告」);不写价格 test:db:未发布价格时创建报告返回与个人报告同样的「暂未定价」错误;发布后预留、结算、释放三条路径都绿;后台定价页能看到新键
T3 创建入口:POST /api/reports 接受 reportType: "synastry_full" + 两个人物 id(同一人 400,不属于本人的 id 拒绝);命中已有报告直接返回不预留;compatibility 开关与 report_center 开关都要开;沿用每日上限、限流、同时只能生成一份 路由测试覆盖以上每条
T4 worker 分支:按上面的链路实现;阶段进度 loading_context → computing_charts → writing_report → checking_report → persisting_report;Python 429 走现有 upstream_busy 重试;每节模型调用有超时,单节失败只重试该节 单测:一节失败只重写这一节;质检两轮不过任务失败并释放预留;删报告释放预留
T5 质检 scripts/synastry_full_reader_gate.py(中英两套规则)+ 薄注册接口(或 worker 直接调 CLI,二选一写进 PROGRESS) pytest:准则文件第四节的示例(补够字数、补上原因的版本)通过;「盘面」行写错一处宫位(与证据不符)失败一次;分别塞进「金星」「七宫」「22 分」「注定」「2027 年结婚」「宿曜」「execution_receipt」、首句改成「这段关系的核心不是……」、删掉一节、去掉某节 claims,各失败一次;英文同理
T6 英文版按需生成:POST /api/reports/:id/english 建任务(不扣点);同一报告只会有一个英文任务;失败可重试 路由与 worker 测试;英文提示里不出现中文稿内容(断言提示文本不含中文九节正文)
T7 阅读页:合盘报告标题「A × B · 婚恋合盘」;九节正文(第一到七节结论后一个默认收起的「分析逻辑」;时间线每段同样带一个)→「对哪一段想再问清楚?」入口(E5d,对话侧未合入时隐藏)→「可以一起聊聊」四问 →「技术依据摘要」(默认折叠,E5b 六块,程序生成)→ 脚注(「合盘描述的是互动的质地,走不走、怎么走,仍然是你们两个人的选择」)。切英文时若还没生成,原地显示等待面板(一次揭幕),生成好整份替换 渲染测试用名人 golden;结论段落与「所以」行过一遍质检第 2、3 条禁表为空;分析逻辑与技术摘要默认收起;技术摘要六块齐全,第 ⑥ 块状态与证据包 execution_receipt 逐行一致;手机 390 px 无横向滚动;next build 后 / 仍 Static,首屏 gzip ±2%
T8 婚恋合盘页(从 #79 移来)与入口:侧栏「婚恋合盘」;选人页(两个自定义下拉,底行「+ 新增星盘」复用星盘档案新建表;居中按钮,按钮下点数说明读 synastry.full 定价);「开始合盘」创建报告并跳到阅读页;「之前的合盘」列出合盘报告;档案页「和我合盘」改跳本页并预选两人;报告中心列表里合盘报告的卡片写「A × B 婚恋合盘」;删人确认句加「K 份合盘」 渲染与路由测试;20 人时下拉可滚动
T9 上游同步与测试搬入:先把 skills/jyotish-full-synastry-reading/ 同步到 7a773d13(见实证 8b),diff 为空;搬入上游合盘测试(以 7a773d13 副本为准)+ #79 已有的 test_marriage_match_product.py;aspects.py 同步上游版本(方向角与速度判相位),跑全量 pytest 与基线逐条比对(相位值变了,报告类测试可能受影响,逐条写原因);依赖上游独有文件的测试改写为我们的等价断言(三栏说明) 上游合盘测试全过或有三栏说明;全量 pytest 无新增失败;Niryaana Shoola 一行在名人证据包里为 not_applicable
T10 记录:BUG-1373(#79 登记为 investigating,本单修复后改 resolved);DESIGN.md(选人页、阅读页、英文按需生成的等待态)、VOICE.md(引用准则文件;报告禁表、标题格式)、CHANGELOG、PROGRESS(与上游的差异:质检包装层、两条改写的测试)、docs/testing/synastry-full-report-20261018.md 真人清单(生成一份、再次打开不扣点、切英文、删人连带删除、失败退点、手机宽) 文档齐

让步顺序

先保 T1–T5(存对、扣对、写得出、拦得住);T7 不可省;T6 若来不及,英文切换先显示「英文版即将提供」并写进 BLOCKED,不得用机器翻译中文稿顶替;T9 的 aspects.py 同步若牵动过多既有测试,可拆成单独小单先行,但必须在本单合入前完成。任何一项过不了红线就停下写进 PROGRESS,不得放宽口径。

开工前置命令

git fetch origin --prune
git worktree add -b codex/synastry-full-report-20261018 .worktrees/synastry-full-report-20261018 origin/staging
cd .worktrees/synastry-full-report-20261018
git log --oneline -5 -- scripts/marriage_match.py           # 确认 #79 已合入
ls docs/research/upstream_full_synastry_bde5d615/skill docs/research/upstream_full_synastry_bde5d615/tests
grep -n "BUG-1[0-9][0-9][0-9]" docs/BUG_HISTORY.md | tail -3
python3 scripts/pre_work_check.py --remote-timeout 8 --command-timeout 45

验证:全量 pytest tests -n 4 与开工基线逐条比对;run_quality_gate.py --profile quick;前端 tsc --noEmit、npm run lint、npm test(含 grep 源码文本合同测试)、npm run test:db、npm run build。真实模型输出(无模型凭据时)写成环境缺口,部署后由 Claude 用名人盘在 staging 生成一份复核。

BUG 编号

本单是产品需求。只有在执行中发现并修复的真实缺陷才新增编号,起点为开工时 docs/BUG_HISTORY.md 最大号 + 1。