docs: functional v2 wrap-up — BUG-1158 resolved, BUG-1180~1182, ERR-114, backtest round 5, progress

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N4f2nya58RoRu4yEmJgRGE
This commit is contained in:
Jesse_Chen
2026-10-02 13:01:55 +08:00
co-authored by Claude Opus 5.5
parent dae2b99fe2
commit 51f3f47bbe
6 changed files with 267 additions and 4 deletions
@@ -0,0 +1,127 @@
# PROGRESS · 上游功能吉凶 v2 收尾:生时校正算法身份、普通对话 golden、第四轮残留、第五轮回测(2026-10-02)
- 执行方式:产品负责人要求子代理直接执行(Claude 子代理);无单独任务书,决策记录见下。只 commit、未 push。
- 分支 / worktree:`codex/consult-upstream-functional-v2-20261002` / `.worktrees/consult-upstream-functional-v2-20261002`
- 基线:`57782aea`(Claude/PM 按产品授权移植上游功能吉凶 profile v2、Pancha 自合修正、瑜伽引擎读同一吉凶表;父提交 `f8579466` = 事业单分支合入 `origin/staging`)
- BUG 编号:开工核对 `origin/staging` 与各 worktree 最大号 BUG-1179;本单 BUG-1180~1182,更新 BUG-1158(→ resolved)、BUG-1165(复发指向 1182)、BUG-1176(第五轮)。
- 上一单:`docs/tasks/TASK-consult-career-yoga-functional-20261002.md` / `PROGRESS-consult-career-yoga-functional-20261002.md`。
## 决策记录
- **D1 功能吉凶采用上游 profile v2**(产品 2026-10-02 授权,PM 已在 `57782aea` 移植):`bphs_ch34_general_with_sign_exceptions_v2`。推翻上一单第二次决策「吉 / 凶 / 中性分组不变」与 T1 分支逐格对照表方案。v5 77 例评测(PM 实测,命令 `python3 scripts/research/holdout_v5_baseline.py --dataset v5 --out-dir <dir>`):先验头名 ±10/30/60 0.1818/0.0779/0.0649 → 0.1818/0.0909/0.0779;六题回放 0.6364/0.4935/0.2597 → 0.6364/0.4935/0.3117;线上区间回放 0.6753/0.4935/0.3247 → 0.6623/0.5584/0.4026;覆盖 0.987/0.987/0.974 → 0.987/0.987/0.987。产品接受(一格 −1.3 pp)。
- **D2 生时校正算法身份升到 scoring-10**(本会话按先例决定,见 T1)。
- **D3 第四轮残留本轮修**(产品批准):事业落宫不翻成幕后、父母反向安慰、逆行禁句、两处请求;只改单一定义处的规则文字,不对模型输出做正则后处理。
## T1 生时校正打分合同(BUG-1181)
### 要不要升版本:要
- 先例:`TASK-rectification-engine-convergence-20260901` 硬红线 3「打分语义变化必须 bump `ALGORITHM_VERSION`」;跨午夜修复按此 7 → 8 → 9(BUG-981~985,`PROGRESS-rectification-cross-midnight-20260920.md`)。BUG-621 的教训是「升版本后历史必须还能打开」,BUG-984 已把版本不一致的历史结果做成只读 + 「重新比较」。
- 事实:功能吉凶分组经 `active_rectification_event_engine.py` 的 `*_functional_benefic_auxiliary` / `*_functional_malefic_auxiliary` 规则进入事件矩阵。同一输入(记忆化测试的公开虚构请求)候选 12:00 的分数 8.6274 → 8.4977;跨午夜虚构 golden 23:59 的分数 0.7893 → 0.9213。
- 不升的后果:`cachedEngineScoreIsReusable` 按算法身份 + 证据指纹决定能否复用,证据未变的历史 Case 会把旧分组的分数当当前结果。
- 结论:升到 `rectification-v5-matrix-scoring-10`;策略版本 `rectification-candidate-policy-v3`、输入合同 v5、Skill 版本都不变;历史结果不重标。
### 升版本时发现的第二个问题
前端五处、数据库一处用字面量 `=== "rectification-v5-matrix-scoring-9"` 判断「这是带日期窗合同的结果」(`engine-client.ts` score / diagnostics 的 `dated`、`tool-service.ts` 候选行解析与 legacy 段选择、`score-persist.ts` 是否先补日期窗、`rectification-candidate-result.ts`、`validate_dated_rectification_candidate`)。直接升到 10 会让新结果按旧的同日结果解析(跨午夜候选丢日期),数据库守卫对 scoring-10 失效。处理:按代数判断(≥ 9)。
### 改动
| 位置 | 改动 |
| --- | --- |
| `scripts/rectification/scoring_service.py` | `ALGORITHM_VERSION` → `rectification-v5-matrix-scoring-10`(注释写明原因) |
| `frontend/src/lib/rectification-agentic/core/candidate-window.ts` | 新增 `FIRST_DATED_SCORING_GENERATION = 9`、`isDatedScoringAlgorithmVersion()` |
| `engine-client.ts` / `tool-service.ts` / `score-persist.ts` / `rectification-candidate-result.ts` | 五处字面量改用上面的函数;`v9EngineVersion()` 缺省串 → scoring-10 |
| `frontend/supabase/migrations/20261002010000_rectification_dated_algorithm_generation.sql` | 只 `create or replace` `validate_dated_rectification_candidate`,守卫改 `~ '^rectification-v5-matrix-scoring-(9|[1-9][0-9]+)$'`;其余与 `20260920020000` 逐字节相同(去 CR 后 diff 只有这一行);不动表、列、权限、历史行;对当前已部署代码向后兼容 |
| `tests/golden/rectification_engine_memoization_v2.json` | 用测试文件自带的 `write_golden` 由真实引擎写出(scoring-10,`source_commit` = `57782aea`);v1(scoring-8)原样保留并以 sha256 钉住 |
| `frontend/tests/fixtures/rectification-midnight-date-anchor.delivery.scoring-10.native.json` | `python3 scripts/research/midnight_date_anchor_regression.py --baseline <本 worktree> --output <scratch> --golden --golden-path …`:真实引擎,虚构资料;同机独立进程 A/B 3 例 × 7 字段逐位相等。scoring-9 的旧 golden 保留不动 |
### 校正研究记录重新冻结(ERR-110 / ERR-114)
只改 `ALGORITHM_VERSION` 一行就让 `test_rectification_validation_integrity_gate.py` 等 8 项失败:`scoring_service.py` 在冻结身份 `PRODUCTION_FILES` 里。反过来,`57782aea` 改的 `functional_benefics.py` 不在清单里,分数变了门禁却没红(记 ERR-114)。按 09-29 BUG-1088 先例(`56b51e21`)处理:
| 步骤 | 内容 |
| --- | --- |
| 清单 | `sealed_holdout_rerun.py` `PRODUCTION_FILES` 补 `scripts/functional_benefics.py`;新增 `test_functional_roles_are_part_of_the_production_identity`(并进快速门 bridge) |
| 新标签 | 两个脚本的 `FREEZE` / `REPORT` 指向 `*_functional_v2_2026_10_02`;`IDENTITY_FORBIDDEN_PREFIXES` 加两个新前缀 |
| 冻结后重放(`PYTHONHASHSEED=0`) | `sealed_holdout_rerun.py --freeze` 后重放 20 例:与 09-29 逐条相同(top-1 0.45 / top-3 0.5 / MAE 6.45,影子 fact ranker 不读功能吉凶);`reported_offset_sweep.py --freeze` 后重放 900 例(6 分 12 秒):230 例变化,窗口内平均头名 ±15/30/60 不变(0.0636 / 0.06 / 0.05),交付覆盖 0.9864 / 0.99 / 1.0 → 0.9955 / 0.9967 / 1.0,平均分钟误差 10.02 / 16.88 / 26.96 → 10.31 / 16.72 / 25.71 |
| 合同 | `references/rectification_sealed_holdout.v1.json`:current_tree_scorer 的 extended_identity / source_report / metrics、current_tree_fixed_protocol_rerun、current_tree_reported_offset_replay 换成新记录;09-29 记录挪到 previous(`superseded_reason` 写明);`status: not_ready`、六个发布键不变;旧报告与冻结文件逐字节保留 |
这些记录是「已曝光、不可用于调参」的敏感性研究,不是发布指标,确认门照旧关闭。
### 改动的既有断言(原值 / 新值 / 原因)
| 文件 · 测试 | 原值 | 新值 | 原因 |
| --- | --- | --- | --- |
| `tests/test_sealed_holdout_contract_freshness.py` `IDENTITY_FORBIDDEN_PREFIXES` | 到 `quality_wording_2026_09_29` | 加 `functional_v2_2026_10_02` 两个前缀 | 新冻结标签(与 09-29 同法) |
| `frontend/tests/rectification-confirmation-gate.test.ts` 当前重跑报告路径(两处) | `…sealed_holdout_rerun_quality_wording_2026_09_29.json` | `…sealed_holdout_rerun_functional_v2_2026_10_02.json` | 同上;第一次快速门里 npm 多出这 1 项失败才发现(`406d81b8` 先例),改后 npm 全量与基线同名 |
| `tests/test_rectification_engine_memoization.py` 对比目标 | v1 golden(scoring-8)+ 投影 scoring-9 新增字段 | v2 golden(scoring-10)直接比;新增「v1 冻结且已不是当前」测试 | 分数变了,v1 不能再当当前;按「历史 golden 不改写、新增版本文件」处理 |
| `tests/test_dasha_transition_proximity_cross_midnight.py` | `== "…scoring-9"` | `== "…scoring-10"` | BUG-1181 |
| `tests/test_rectification_relative_support.py` | `"…scoring-9"` | `"…scoring-10"` | BUG-1181 |
| `frontend/tests/rectification-engine-version-cross-midnight.test.ts` | `LIVE_CURRENT = scoring-9`,scoring-9 dated golden 兼作「当前」 | `LIVE_CURRENT = scoring-10`;scoring-9 golden 记为 `DATED_HISTORICAL`;新增「scoring-9 缓存在 scoring-10 下不可复用」 | BUG-1181 |
| `frontend/tests/rectification-midnight-date-anchor.test.ts` 跨午夜贯穿 | 只跑 scoring-9 golden | scoring-9 与 scoring-10 两份都跑 | 证明新代仍按日期窗解析 |
| `frontend/tests/database-adopted-birth-date.test.ts` | 只验 scoring-9 去掉日期合同被拒 | 另验 scoring-10 / 11 被拒、scoring-8 仍按旧同日结果返回空 | 迁移守卫 |
### 验证
- 记忆化文件 22 项全过;`write_golden` 再写一次,`candidate_scores` / `decision_receipt` 相同,只有未参与比较的特征哈希不同(BUG-733 / 985 的已知跨进程浮点差,测试本就不比)。
- 红绿:把 `isDatedScoringAlgorithmVersion` 临时改回字面量 scoring-9,scoring-10 跨午夜贯穿测试即红;去掉迁移文件,`database-adopted-birth-date` 2 项即红;`57782aea^` 的旧代码跑同一记忆化请求得 8.6274,新代码 8.4977。
- 数据库(本机 PostgreSQL 17 + docker 替身,方法见记忆「local-postgres-without-docker」):`npm run test:db` 全量 80 项,基线 `57782aea` 一次性 worktree 与本分支都是 74 过 / 6 败,失败名单逐条相同(env validator、个人报告 RLS、会话标题 updated_at、admin 身份、staging 备份、stdin 大 SQL——替身不支持 pg_dump / 部署脚本等)。
- **历史打开(BUG-621 教训)**:`rectification-*.test.ts` + `skill-registry*.test.ts` 1,872 项 0 失败(含 BUG-621 按会话用绑定 Skill 打开、BUG-984 版本不一致只读 + 「重新比较」、历史回执原样读取);新增用例:真实 scoring-9 结果在 scoring-10 下 `read_only = true`、`can_recompare = true`、候选仍带日期,在 scoring-9 下不只读。
- 部署侧待办(不在本会话):核实 staging / 生产没有设置 `RECTIFICATION_ENGINE_VERSION` / `RECTIFICATION_ALGORITHM_VERSION`(`deploy/`、`.gitea/` 里没有);迁移随部署自动应用;真机走一遍「打开一条旧校正 → 只读 → 重新比较」。
## T2 普通对话 golden 与功能吉凶断言(BUG-1158)
- `PYTHONHASHSEED=0 JYOTISH_API_CHART_CACHE_TTL_SECONDS=0` 跑 `capture_consult_evidence_card_golden.py`、`capture_consult_biography_backtest_golden.py`,各两次逐字节相同;diff 只在功能吉凶字段(分组、`profile`、`role_basis`、`lagna_lord` 等新字段、Technique Audit 的 `key_functional_*`)。瑜伽字段无变化。
- 九位名人分组变化:乔布斯(处女)木星中→凶、太阳凶→中;奥巴马(摩羯)月亮中→凶;泰勒、琵雅芙(天蝎)太阳中→吉、火星吉→中、金星凶→中;嘉兰(双子)木星中→凶;卡罗(狮子)月亮凶→中;齐达内(金牛)太阳中→吉、火星凶→中、金星吉→凶;梦露、布什(巨蟹)不变。
- Python:全量跑过,功能吉凶相关测试(mcp strict 系列、consumer context、report parity、active rectification 等)没有新失败(见门禁)。pyjhora / oracle 测试的失败都在基线名单里。
- 前端改动的断言:`consult-card-affliction-data-20261001.test.ts` 乔布斯父亲代表星太阳 原值 `malefic` / 新值 `neutral` / 原因:v2 只管 12 宫的星不自动判凶(三栏写在测试注释)。
- 新增 `frontend/tests/consult-upstream-functional-v2-20261002.test.ts`:12 份 golden 的 profile、泰勒(天蝎)与乔布斯(处女)卡上分组。
## T3 第四轮残留
| 项 | 查到的原因 | 改动 |
| --- | --- | --- |
| a 事业 9/18「不是站台式 / 往深处做」(BUG-1176) | 规则说了「不断定公众 / 幕后」,但没点名 A10 / AL / 太阳 / 10 宫主落 4、6、8、12 宫这类读法本身就是在下类型结论 | `CAREER_FIELD_ASK_RULE` 加一句:这些落宫只说阻力或代价(要经手、要绕路、来得晚),不翻成「不在台前」「不在台面上」「不靠曝光」「幕后」「不是站台式」「往深处做」,也不把落宫翻成行业 |
| b 梦露 ×2 / 琵雅芙 ×1 母亲「在管、在安排、近」(BUG-1182) | 卡上没有解读性文字;**系统提示三处示例把「4 宫主落 10 宫」配成「母亲的注意力在你的前途上 / 离你近、上心」,梦露盘 4 宫主正落 10 宫**;`AFFLICTION_RANGE_RULE` 只禁安慰词,不禁拿宫主落宫 / 宫内吉星描写人物行为,也不管行动建议 | `AFFLICTION_RANGE_RULE` 加「不拿宫主落哪一宫、宫里有吉星去描写这个人对你做什么……行动建议也不写给他打电话、去问他」;三处示例括号写明「母亲这条线没有受冲」 |
| c 禁句 1(琵雅芙健康「逆行 → 拖出来」) | 通用读法只写「逆行不等于反复、打回来」 | 改为「也不等于拖得久、拖出来,推论里不拿逆行当理由(依据里可以列)」 |
| c 三份两处请求 | `AFFLICTION_RANGE_RULE` 要「这一段末尾」问,和事业段末那一问叠加 | 加「问过之后别的段落不再另问」 |
改动的既有断言:`consult-no-presupposition-20261001.test.ts` `AFFLICTION_RANGE_TEXT`(原值:止于「全篇这类请求最多一处。」/ 新值:加上面两句 / 原因:BUG-1182)与是非题例正则(原值 `/不是。(4 宫主落 10 宫)…/` / 新值 `/不是。(母亲这条线没有受冲;4 宫主落 10 宫)…/`);`consultation-voice-contract.test.ts` 父母例与是非题例两处(同上,三栏在注释)。`frontend/docs/VOICE.md` 同步(不揣测、§7 ③、§8、示例一节、对照表两行)。
回测脚本 `consult-biography-backtest.mts`:`--correction-domain` 支持逗号分隔多个领域,`--correction` / `--correction-texts` 支持 `领域:名人` 写法,一次跑完父母纠正与事业追问。
## T4 第五轮回测
详表见 `docs/testing/consult-affliction-backtest-20261001.md`「改动后(第五轮)」。key 只从环境变量读,没有写入文件、日志或提交;模型输出在 `scratch/consult-biography-backtest/round5-20261002/`(gitignore)。
| 通过线 | 第四轮 | 第五轮 | 达到 |
| --- | --- | --- | --- |
| 事业行业 / 类型与生平冲突 ≤ 1 / 18 | 9 / 18 | **5 / 18** | 否 |
| 事业段末问一句 ≥ 16 / 18 | 18 / 18 | **17 / 18** | 是 |
| 严重冲突 ≤ 5 / 72 | 12 / 72 | **5 / 72**(全在事业;父母 3 → 0) | 是(压线) |
| 对照组严重误报 0 | 0 | **0** | 是 |
| 对照组轻度误报 ≤ 2 | 3 | **4** | 否 |
| 禁句 0 | 1 | **0** | 是 |
| 每份只问一次 | 3 份两处 | **72 / 72 一处** | 是 |
追问:父母纠正 8 / 8、事业追问 6 / 6。未达的两项见测试文档「读出来的东西(第五轮)」:事业剩下的是同义词逃逸(「不靠场面」「不是一阵热闹」),对照组轻度误报是把「迹象」写成一句现实结论,交产品决定下一步(建议方向写在测试文档)。
## 门禁
| 项 | 基线(`57782aea` 一次性 worktree) | 本分支 |
| --- | --- | --- |
| `tsc --noEmit` | — | 0 错 |
| `npm run lint` | — | 0 error / 126 warning |
| `npm test`(Node 22) | 4,911 项:pass 4,846 / fail 24 / cancelled 0 / skip 41 | 4,921 项:pass 4,856 / fail 24 / cancelled 0 / skip 41;失败名单逐条同名(都是需要 Docker / PostgreSQL / 部署环境的套件) |
| `npm run test:db`(本机 PG17 替身) | 80:74 过 / 6 败 | 80:74 过 / 6 败,名单相同 |
| `next build` | `/` ○ Static | `/` ○ Static |
| 首屏 gzip(预渲染 `index.html` 引用的 32 个 `/_next/` 资源 gzip-9 求和,同法实测) | 635,351 B | 635,380 B(+0.005%) |
| Python 全量(`pytest tests -n 6`) | 67 项失败(`f8579466` 基线名单;`57782aea` 只多出 8 项记忆化失败) | 62 项失败,全部在基线名单内、新增 0;记忆化 8 项转绿;另有 5 项 prashna 研究测试转绿,与本单无关(工作树里 4 个 `references/oracle/prashna_*` 文件开工前已被改写,见下) |
| 快速门 `run_quality_gate.py --profile quick`(系统 python3 + Node 22) | — | pytest 1,043 passed / 1 skipped;门内 `npm test` 4,921 项 fail 24,名单与基线同名;门禁总状态因这 24 项环境失败为 failed(与上一单相同) |
环境:`frontend/node_modules` 用硬链接拷贝(`cp -al`),未提交。工作树里 `references/oracle/prashna_*_2026_07_20.json` 四个文件开工前就是已修改状态(来源不明,疑为别的测试运行改写),本单未提交。一次性基线 worktree `.worktrees/tmp-base-57782aea-20261002` 用完删除。
## 提交
`codex/consult-upstream-functional-v2-20261002`(未 push):`3733b978`(BUG-1181 算法身份、日期窗代数判断、迁移、记忆化 golden v2、研究记录重新冻结)、`3a40d422`(BUG-1180 回归测试)、`7806c72f`(普通对话 golden、第四轮残留规则、回测脚本)、`dae2b99f`(确认门测试读新冻结报告)及本文档提交。
+1
View File
@@ -408,3 +408,4 @@
| `TASK-consult-affliction-reading-20261001.md` | `PROGRESS-consult-affliction-reading-20261001.md` | 三单之二:全领域「先看受冲、再看旺弱」通用读法,补齐父母/子女/学业/迁居/家庭/年运/综合/健康中文清单,健康改名「健康」先讲体质底子(预留 BUG-1160~1166) | **已实现待验收**(Claude 子代理直接执行,BUG-1160~1163;未推送);年运 / 时运字数口径产品 10-02 已定(同事业 / 婚姻 / 财富),实现在第三单分支 `ec06d422` | 分支 `codex/consult-affliction-reading-20261001` |
| `TASK-consult-no-presupposition-and-backtest-20261001.md` | `PROGRESS-consult-no-presupposition-backtest-20261001.md` | 三单之三:写作形状去掉「对象在场」预设、受冲时请用户确认、删被照抄的「逆行」示例、用户纠正时不辩护;建立名人生平回测作为普通对话提示词改动的固定验收(预留 BUG-1167~1172) | **已实现待验收**(Claude 子代理直接执行,BUG-1164~1168;未推送);基于第二单分支 + staging;名人回测已跑改动后:严重冲突 39→19、禁句 7→0、纠正追问 8/8,对照组误报 1→15;产品 10-02 改「迹象 + 范围」口径(`066558a6`)后第二轮:严重 15/72、误报 6(轻)、排查 5 份 8/10、新增名人严重 6/32、事业 12/18,修订通过线仍只有禁句达到;含产品 10-02 年运 / 时运字数口径(第二单 blocker 解除) | 分支 `codex/consult-no-presupposition-backtest-20261001` |
| `TASK-consult-career-yoga-functional-20261002.md` | `PROGRESS-consult-career-yoga-functional-20261002.md` | 三单回测遗留三项:事业题按星的本性看行业、按名声信号看公众/幕后;开场守「只说迹象」;功能吉凶改 BPHS 第 34 章对照表(推翻 v1 公式,命主不再一律 yogakaraka);瑜伽检测不跳过;大格局以「传统格局,本站未验证」上卡(产品授权放宽 §8.4 仅限此栏)(BUG-1174 起) | **已实现待验收**(Claude 子代理直接执行,BUG-1174~1177;未推送);产品 10-02 二次决定后:yogakaraka 窄修已合入(生时校正评测逐项不变),BPHS 全表留 T1 分支待顾问;事业改「不猜行业,问用户」;第四轮回测请问一句 18/18 达到、行业 / 类型冲突 9/18 与严重 12/72 未达;上游 e9beae6b 移植因权限未做 | 分支 `codex/consult-career-yoga-functional-20261002` |
| —(产品要求子代理直接执行,无任务书;决策见进度记录) | `PROGRESS-consult-upstream-functional-v2-20261002.md` | 上游功能吉凶 profile v2(`57782aea`)收尾:生时校正算法身份升 scoring-10、「日期窗合同」改按代数判断(新迁移只改函数体)、记忆化 golden 新增 v2、校正研究记录重新冻结并把 `functional_benefics.py` 补进冻结身份;普通对话 golden 重生成;第四轮残留(事业落宫翻成幕后、受冲母亲写成在管、逆行写成拖、两处请求);第五轮名人回测(BUG-1180~1182,BUG-1158 → resolved) | **已实现待验收**(Claude 子代理直接执行;未推送):门禁 tsc 0 / lint 0 error / npm test 失败名单与基线同名 / test:db 与基线同名 / build `/` Static;第五轮:父母严重冲突 3 → 0、禁句 0、每份一问,事业类型冲突 5/18 与对照组轻度误报 4 未达线,交产品 | 见进度记录「提交」 |