# PROGRESS · 上游同步第五轮 + 生时校正 4 件 3 域(2026-10-02) - 任务书:`docs/tasks/TASK-upstream-sync5-20261002.md`(`origin/staging` `677d40f9`)。 - 执行方式:产品授权直接执行。Claude(PM)先在本会话移植上游代码(U2–U8 各一或两个提交);子代理完成 U8 后续、R1、测试修复、记忆化 golden、研究记录重新冻结、评测、golden、回测与记录。只 commit,未 push。 - 分支 / worktree:`codex/upstream-sync5-20261002` / `.worktrees/upstream-sync5-20261002`,基线 `677d40f9`(代码同已部署 `3d0c3cdf`)。 - 上游参照:`/workspace/yinduzhanxing` `origin/main` `c593354f`(只读;未从上游复制文案,本地 ref 未改)。 - BUG 编号:开工核对 `origin/staging` 最大号 BUG-1191;本单 BUG-1192~1198(提交前再核对,未被占用)。 ## 提交 | 提交 | 项 | 内容 | BUG | | --- | --- | --- | --- | | `cb9d942b`(PM) | U2 | 8 星制 Chara Karaka 罗睺按 30° 减度数排位(上游 `197672e3`) | 1194 | | `bdbff99a`(PM) | U3 | 五大人格燃烧顺 / 逆行表(上游 `eaf6918b`) | 1195 | | `533c3a30`、`9959e514`(PM) | U6 | 诅咒格 / Rashi Tulya 健康措辞 + 测试(上游 `b40c7fde`、`28f41912`) | 1197 | | `765ff66c`(PM) | U8 | Badhaka 12 上升测试(上游 `6abfe983`) | 1192 | | `cf78d4ca`(PM) | U3 | 全引擎燃烧表统一到 `COMBUSTION_ORBS` / `is_combust`,含月亮、逆行与普通对话层(上游 `080c286c`,改编) | 1195 | | `5172620c`(PM) | U5 | Yogini 按月宿起运(上游 `cb7fe017`) | 1196 | | `45d6dbf6`(PM) | U7 | 水星燃烧时 Budha-Aditya 降为有条件(上游 `994638f0` 只取此段) | 1198 | | `54f6930b`、`67aa1b33`(PM) | U4 | 三方净化、单宫主净化(上游 `9531d1dc`、`868f1dad`) | — | | `7fb165ec` | U8 后续 | 诅咒格 Badhaka 宫按上升取 11/9/7、删早夭 / 早逝措辞 | 1192 | | `758fee95` | R1 | 生时校正交付门槛 4 件 3 域;政策 v4;记忆化 golden v3;30 个前端测试文件 + 4 个 Python 测试 | 1193 | | `1029cbaa` | R1 / ERR-110 | 研究记录重新冻结(标签 `upstream_sync5_2026_10_02`) | 1193 | | `917e18a0` | — | 普通对话回测 golden 重生成 | — | | `03d60e29` | U3 后续 | 两张燃烧表相等测试 | 1195 | | `90dcafe7` | R1 后续 | 隐私扫描按行号钉的两处碰撞保持原行 | 1193 | | (本提交) | — | 记录:BUG 历史、CHANGELOG、本文件、任务索引、回测第七轮 | — | ## 逐项 ### U1 Narayana 默认版本 —— **不搬**(产品延后) 上游 `acdc3551` 只改了 `scripts/cmd_narayana_dasha.py` 一行:`variant_profile` 默认值 `legacy_local` → `kn_rao`。本仓的 `cmd_narayana_dasha.py` 根本没有 `variant_profile` 开关(上游更早加入的 K.N. Rao profile 本仓未同步),生时校正(`event_probes`、`active_rectification_event_engine`)直接调 `narayana_dasha.calc_narayana_mahadasha` 等函数,也不经过这个命令入口;要「默认 K.N. Rao」得先整体移植 profile 机制,不是一行默认值;而「产品是否改用 K.N. Rao 版本」是占星师待裁决项(乙表),改函数默认会同时改生时校正打分。本轮不移植,留待占星师确认后单独立单(需过 v5 评测与历史可打开核对)。 ### U2 Chara Karaka 罗睺(BUG-1194) PM 移植。生时校正只用 `jaimini.calc_arudha_padas`,不读 Karaka;77 例 A/B 分数相同。 ### U3 燃烧(BUG-1195) - PM 移植两提交。全引擎现在两张表:`jyotish_engine.COMBUSTION_ORBS`(全读、普通对话 graha drishti 层)与 `pancha_mahapurusha.COMBUSTION_ORB`(上游形状)。数值相同;`03d60e29` 加测试锁两表相等,并在每个容许度边界验证两个函数。`scripts/validate_yoga_accuracy.py` 另有一张简化表(只顺行),是离线校验脚本,不在产品路径,未改。 - 普通对话卡上的变化:9 位回测名人中只有齐达内一颗逆行金星由燃烧改为不燃烧。 ### U4 八分法净化 —— 函数已加,未接入输出 上游只新增 `calc_trikona_shodhana` / `calc_ekadhipatya_shodhana`(上游仓内也没有调用方)。本仓同样不接入报告、数据卡与生时校正,SAV 原值不变;函数可供后续调用。`scripts/ashtakavarga.py` 在生时校正冻结身份里,字节变化按 ERR-110 重新冻结。 ### U5 Yogini(BUG-1196)、U6 诅咒格措辞(BUG-1197)、U7 Budha-Aditya(BUG-1198) PM 移植;测试见各提交。U5 上游对本仓不存在的测试文件的改动未带;U7 上游同一 hunk 里其它卡片闸门字段来自别的提交,未带。 ### U8 Badhaka(BUG-1192) - 核对:本仓两处 Badhaka。`yoga_engine.YogaContext.is_badhaka` 已按动 / 定 / 变取 11 / 9 / 7(上游测试 `765ff66c` 通过);`curse_yoga_detector` 亡灵格(Preta Yoga 两条)加重宫位写死 `[8, 11, 6]`、注释「11宫=Badhaka」—— 记忆里说的「写死 11 并输出过早死亡风险」就是这里(「过早死亡风险」已由 U6 去掉)。 - 修复:`yoga_engine.badhaka_house()` 成为唯一定义,`is_badhaka` 与诅咒格都调它;Preta 两条的加重宫位改为按上升展开(D9 检测用 D9 上升;上升未知时 Badhaka 宫不参与加重,原 `is_badhaka` 对未知上升返回 7 宫,现在返回 False)。 - 死亡措辞 grep:`rg "过早死亡|死亡风险|早夭|早逝|夭折|血脉.*(中断|停止)|死亡之神" scripts references/*.json` 后剩余:诅咒格「家族影响」两条(子女早夭、家族血脉停止)→ 改为责任 / 传承主题;`references/yoga_rules.json` 母亲不利格效果「母亲早逝 / Early loss of mother」→ 删除。剩余命中只在 `scripts/misconceptions.py`、`scripts/case_validator.py`(李小龙案例笔记,供内部案例校验,不进用户输出)与 `references/*.md` 讲稿资料。前端两份报告阅读 fixture(`report-reader-main-fictional*.json`)是历史引擎输出,仍含「母亲早逝」,未重采(不影响线上)。 - 测试:`tests/test_curse_yoga_badhaka_house.py`(12 个上升、未知上升、源码与实际输出正则扫描)。 ### R1 生时校正 4 件 3 域(BUG-1193) **门槛位置(改前 → 改后)** | 位置 | 改前 | 改后 | | --- | --- | --- | | `references/rectification_policy.v1.json` `minConfirmationEvents / minConfirmationDomains` | 4 / 3(只被确认门读) | 不变,成为唯一定义 | | `frontend/src/lib/rectification-agentic/core/types.ts` `MIN_TRAINING_EVENTS = 3`(收敛判定,只数训练件) | 3 件 | 删除;新 `MIN_DATED_EVENTS / MIN_DATED_DOMAINS` 读政策文件;收敛判定数训练 + 留出,并加领域条件 | | `core/rectification-decision.ts` `MIN_STANDALONE_DATED_EVENTS / DOMAINS`(区间交付门、出牌前「继续采集」) | 3 / 2 | = `MIN_DATED_*`(4 / 3) | | `v9/evidence-model.ts` `MIN_ACCEPTANCE_EVENTS / DOMAINS` + `trainingScoreableGate`(训练门:何时开始出选择题、出卡) | 3 / 2,只数训练件 | = `MIN_DATED_*`,数训练 + 留出的全部带日期主经历 | | `v9/decision-from-dossier.ts` 推断后 `trainingGateOpen` | 训练件 3 / 2 | 训练 + 留出 4 / 3 | | `scripts/rectification/decision_policy.py` `MIN_ACCEPTANCE_EVENTS / DOMAINS`(回执 `event_quality` / `domain_diversity`,决定 `acceptance_allowed` / `selection_allowed`) | 3 / 2,只数训练件 | = `MIN_CONFIRMATION_*`(政策文件),数全部可计分经历;`POLICY_VERSION` v3 → v4 | | 其它(未改,含义不同) | `case_holdout.py` / `split-holdout.ts` 第 4 件起留出 1 件;`candidate_contrast.MIN_DISCRIMINATOR_*`(区分题供给);研究脚本 `holdout_v5_build` 7 件 4 域(数据集入选标准);`minute_rectification_holdout_validator` 每例 3 件(固定协议) | — | - **计数口径**:产品口径「至少 4 件事」按用户给出的带日期主经历算,包括被留作 holdout 的那一件(留出规则不变:第 4 件起留 1 件)。若改成只数训练件,用户要说 5 件才够。 - **遗留(待产品)**:留出规则优先把只有一件的领域留作 holdout,刚好 4 件 3 域时,实际参与打分的常是 2 个领域 3 件事。要不要改成「训练件也要 3 个领域」(等于要 5 件以上),请产品定。 - **打分不变**:77 例 v5 用 `score_candidates`(±10,分钟步长 2,冻结日期)A/B 逐分相同;记忆化 fixture 分数与 v2 golden 相同,只有回执变(`policy_version` v4、`event_quality.minimum` 3 → 4、`domain_diversity.minimum` 2 → 3 且 2 域不再通过、代表候选 UUID 随 policy 命名空间变、`feature_hash` 随 ERR-111 变)。按 BUG-1181 先例「同输入出不同分才升算法版本」,**不升 `rectification-v5-matrix-scoring-11`**;`ALGORITHM_VERSION` 仍 scoring-10,policy 身份 v4。记忆化 golden 按先例由 `write_golden` 写 v3,v2 冻结并按 sha256 钉住(新测试断言 v2 / v3 分数相同、只回执 policy 变)。 - **历史 Case 能否打开(BUG-621)**:算法版本与 Skill 版本都没变;policy 版本只参与「缓存分数是否可复用」与候选 UUID,旧结果按存储的身份只读打开、重新比较时按 v4 算。前端 `rectification-history-open` / `engine-version-cross-midnight` / `dated-algorithm-generation` 等测试全过(npm test 失败名单与基线相同)。未做浏览器级验证。 - **采集文案**:`preciseGapNarration` 不再一律「再来一件……就能开始筛」,按缺口写「再来两件记得大概年月的事,其中至少一件不是上学或工作的,就能开始筛」「再来四件……、至少涉及三个方面,就能开始筛」,只差一件时仍是「再来一件……」。保留「就能开始筛」字样(多处合同测试按它识别缺口句)。`VOICE.md` 第 293 行同步。`user-copy.ts` 的 `insufficientEventsGate` / `insufficientDomainsGate`(「再来一件……」)只被 `acceptanceGateNarration` 用,而该函数在 `answer-choice.ts` 只 import 未调用,未改(死代码,记一笔)。遗留表单 `components/birth-time-life-events.tsx`(无引用方)文案与校验同步为 4 条、3 个领域。Skill 文本里没有写 3 件 2 域(只有「propose_allowed 需要可评分事件≥4、领域≥3」),Skill 版本不变。 - **采集流程**:训练门(`trainingScoreableGate`)同时决定「继续口述采集」与「开始出选择题 / 出卡」,门槛抬到 4 / 3 后,采集线会一直问到 4 件 3 域(原 3 件 2 域就转选择题)。BUG-1048 是出题闸门最小间隔问题,与件数无关;BUG-647 的「holdout 不饿死训练」不变(4 件时训练仍 3 件)。 **测试三栏(摘要;逐条注释写在测试里)** | 文件 | 用例 | 原值 | 新值 | 原因 | | --- | --- | --- | --- | --- | | `rectification-convergence-budget` | insufficient standalone evidence keeps collecting | {2/2}→dated、{3/1}→domains | {3/3}→dated、{4/2}→domains | R1 门槛下方各取一格 | | 同上 | standalone delivery floor(改名 shares the 4/3 policy…) | 3、2 | 4、3 且与政策同源 | R1 单一定义 | | 同上 | dossier wiring | 3 条 career → insufficient_domains | 3 career + 1 relationship → insufficient_domains | 3 件会先落 insufficient_dated_events | | `rectification-decide-next-action` | discrimination waits for…(改名 four dated events across three domains) | three.open=true、3 件出区分题 | false、继续采集;新增 4 件 2 域门关 | R1 | | `rectification-decision-authority` | invariant 2 | 事件 [0,2,3] × 领域 [0,1,2] | 事件 [0,2,3,4] × 领域 [0,1,2,3],3 件 2 域也必须挡 | R1(测试更严) | | `rectification-eight-method` | three dated events with one holdout… | holdoutCount=0 | 1(补第 4 件,训练仍 3) | R1 | | `rectification-occupation-dated-answer` | raises the training gate by one career event | 训练 2 → 3 | 训练 + 留出 = 改前 + 1,holdout=1 | R1 按训练 + 留出计 | | `rectification-probe-pool-exhausted` | T4 标题 | 「对照了 4 件经历」 | 「对照了 5 件经历」 | 夹具多一件第 3 域经历 | | `rectification-collect-stall` | revision 5… | holdout `e-rel-start` | holdout `e-finance`、`e-rel-start` 断言为训练 | 夹具补第 3 域后单件域被留出 | | `rectification-block-scan` | 题名 three dated events in two domains… | — | four dated events in three domains(断言不变) | R1 | | `tests/test_rectification_confirmation_and.py` | four events two domains(改名 neither selects nor proposes) | selection_allowed=True | False,`domain_diversity` 不过、minimum 3 | R1 | | `tests/test_rectification_relative_support.py`、`test_rectification_v5_services.py` | policy 版本 | v3 | v4 | R1 | | `tests/test_rectification_engine_memoization.py` | 当前 golden | v2 | v3(v2 冻结 + 分数相等测试) | R1 回执变 | 其余 23 个前端文件只改夹具(补第 4 件 / 第 3 域,保持原场景),每处带「R1(BUG-1193)」注释;没有删断言。 ## 生时校正评测(v5 77 例) - `python3 scripts/research/holdout_v5_baseline.py --dataset v5 --out-dir `,改前在 `origin/staging` `677d40f9` 一次性 worktree、改后在本分支 `917e18a0` 一次性 worktree(引擎与 R1 全部改动之后)各跑一次:sweep / cluster_width / futile_collect 三段 JSON 逐字节相同(只差耗时),数字与第四轮 `a18f8d4e` 相同。 | 指标(v5 77 例) | ±10 | ±30 | ±60 | | --- | --- | --- | --- | | 先验头名 前 / 后 | 0.1818 / 0.1818 | 0.0909 / 0.0909 | 0.0779 / 0.0779 | | 六题回放头名 前 / 后 | 0.6364 / 0.6364 | 0.4935 / 0.4935 | 0.3117 / 0.3117 | | 线上区间回放头名 前 / 后 | 0.6623 / 0.6623 | 0.5584 / 0.5584 | 0.4026 / 0.4026 | | 覆盖 前 / 后 | 0.987 / 0.987 | 0.987 / 0.987 | 0.987 / 0.987 | | 中位宽度(线上回放)前 / 后 | 13 / 13 | 33 / 33 | 65 / 65 | 红线(任一格下降 > 1 pp)未触发。 - **R1 对交付的影响**:评测按每例全部经历打分,不经过交付门,所以数字不随 R1 变。按数据集本身核算:77 例每例都有 ≥ 7 件、≥ 4 个领域的主经历,**给全经历时 0 / 77 被 4 件 3 域挡住**。若按经历发生先后逐件说出:旧门槛 3 件 2 域中位在第 3 件达到,新门槛中位在第 4 件;55 例只需多说 1 件,12 例多 2 件,2 例多 3 件,8 例要多 4–7 件(前几件集中在一两个领域,第 3 个领域出现得晚)。 - 另:77 例 `score_candidates` A/B(±10、冻结日期)逐分相同;reported offset 900 例 0 例变化、sealed holdout 20 例逐条相同(见重新冻结提交)。 ## 普通对话 golden 与回测 - `consult-evidence-card-golden.json`、`consult-biography-backtest-golden.json` 用 capture 脚本各跑两次(`PYTHONHASHSEED=0`、缓存 TTL 0),逐字节相同。证据卡 golden 与提交版相同;回测 golden 只有齐达内逆行金星不再燃烧(graha drishti 四条路由各一处)。 - 名人回测第七轮:`docs/testing/consult-affliction-backtest-20261001.md`「改动后(第七轮,上游同步五)」。72 + 14 份全部读完。事业类型冲突 9 → 13 / 18(8 位名人卡未变,属模型波动);父母严重冲突 0、禁句 0、纠正追问 8 / 8、事业追问 6 / 6;对照组轻度误报 6 → 8、读反 2 → 5(都在健康开场)、1 份全篇两处请求。 ## 门禁 | 项 | 结果 | | --- | --- | | ruff(门禁清单 5 个文件)、`py_compile scripts/*.py jyotish_vedic/*.py mcp_server.py` | 通过 | | `run_quality_gate.py --profile quick --skip-yoga-logic --skip-frontend-runtime` | failures [],exit 0(1,046 passed、1 skipped;在含记录的提交上跑) | | 隐私扫描 `tests/test_repo_privacy_markers.py` | 通过(R1 改测试时一度把按行号钉住的两处碰撞挤下一行,`90dcafe7` 修回) | | Python 全量(`pytest tests -n 4`) | 63 个失败,全部在开工基线 67 个之内;基线里 5 个(prashna 系列,依赖本机文件)本次通过。新增失败 0 | | `tsc --noEmit` | 0 错 | | `npm run lint` | 0 error(126 warnings) | | `npm test`(Node 22) | 4,921 项,fail 24、cancelled 0;失败名单与同机 `origin/staging` 基线逐条相同(基线另有 1 条答题钟计时用例偶发失败);名字表只少 3 个改名用例(新名字都在) | | `next build --webpack` | `/` 仍 `○ Static`;首屏 gzip 607,159 B,基线 607,110 B(+49 B,+0.008%) | | 数据库套件 | 未跑(无 Docker;本单不改表结构、无迁移) | ## 上游未搬 / 不在本单 - U1 Narayana 默认(见上)。 - 任务书 §2.3 不搬:上游生时校正显式权重 / 行运锚点 / 双轨门、五大人格规则 profile、父母 / 事业盲测与现实校准合同。 - U7 同一 hunk 里上游其它卡片闸门字段(来自别的提交)。 - U4 两个函数不接入任何输出(上游同样未接)。 ## 遗留 / 待产品 1. R1 计数口径:刚好 4 件 3 域时打分常只用 2 域 3 件(holdout 留走单件域)。是否要求训练件也 3 域。 2. `acceptanceGateNarration`(`user-copy.ts`)只 import 未调用,`insufficientEventsGate` / `insufficientDomainsGate` 两句仍写「再来一件」,属死代码;按「多余入口宁可删」可另开小单删除。 3. `report-reader-main-fictional*.json` 两份前端 fixture 是历史引擎输出,仍含「母亲早逝」;下次重采报告 fixture 时自然消失。 4. 名人回测事业类型冲突仍远高于通过线(13 / 18),需提示词或卡片单处理。 5. 真机:部署后在 staging 新建一次校正,只说 3 件(2 个方面),确认不出时间卡且提示写「还差……」;补到 4 件 3 个方面后出选择题 / 卡。历史校正记录能从列表打开。