diff --git a/docs/BUG_HISTORY.md b/docs/BUG_HISTORY.md index 0bdc38a0..9e5c9b38 100644 --- a/docs/BUG_HISTORY.md +++ b/docs/BUG_HISTORY.md @@ -7483,8 +7483,8 @@ - 触发条件:请求含财富主题;引擎 `modules.varga_full` 使用 `D2_Hora` / `D11_Rudramsa` 这类带流派后缀的键;工作流把 D2/D11 标成已执行。财富章因此被标成 write,终态文档却没有 `charts[].id` 为 D2/D11。 - 根因:两层叠加。提取只把 `^D\d{1,3}$` 当成分盘 id,丢掉 `D2_Hora` / `D11_Rudramsa`,`readVargaHouses` 也只认 D9/D10。主题计划仍可用 D2/D11 回执闭合财富章。组装后 `findThemeCoverageViolations` 要求财富主题必须带结构化 D2 与 D11,最终 parse 失败,公开码压成 `report_schema_invalid`。 - 修复:把文档允许的分盘键归一成 D2/D9/D10/D11/D24(含 `D2_Hora` 等别名),占据星体走 celestial allowlist。write 主题若缺少对应结构化分盘,降成 blocked disclosure,不再组装成会解析失败的文档。终态 parse 失败时把缺分盘与 hash 失配从笼统的 `final_parse_rejected` 分出来;日志仍只写 allowlist token,不含用户或模型原文。不放宽 `.strict()` 文档 schema,也不改 assertionLevel 规则。 -- 验证:`frontend/tests/personal-report-generation.test.ts` 用 `D2_Hora` / `D11_Rudramsa` 抽出 D2/D11 且财富章成为 claim;`frontend/tests/personal-report-generation-v2.test.ts` 财富 write 只有 D1 时 ready 且财富进 blocked;`frontend/tests/personal-report-contract.test.ts` 财富主题缺 D2/D11 仍拒绝。三组 focused 测试 85/85,`./node_modules/.bin/tsc --noEmit` 通过。未用登录会话在 staging 重生。 -- 防复发:文档分盘 id 必须从引擎 `varga_full` 别名归一,不得只认 `D2` 这种短键。财富/事业/婚恋/教育 write 主题缺对应结构化分盘时必须降级 blocked,不得把 `final_parse_rejected` 当正常完成。新增分盘别名必须同时覆盖提取与文档 `CHART_IDS`。 +- 验证:`frontend/tests/personal-report-generation.test.ts` 用 `D2_Hora` / `D11_Rudramsa` 抽出 D2/D11 且财富章成为 claim;`frontend/tests/personal-report-generation-v2.test.ts` 财富 write 只有 D1 时 ready 且财富进 blocked;`frontend/tests/personal-report-contract.test.ts` 财富主题缺 D2/D11 仍拒绝。Gitea run 2298 在 `report-interpretive-facts.test.ts` 6 fail:夹具只有 D2/D11/D24 回执、没有 `varga_full`,降级后 wealth/education 卡被拿掉。补上结构化 D2/D11/D24 后该文件本地绿,并锁「仅回执则 demote」。未用登录会话在 staging 重生。 +- 防复发:文档分盘 id 必须从引擎 `varga_full` 别名归一,不得只认 `D2` 这种短键。财富/事业/婚恋/教育 write 主题缺对应结构化分盘时必须降级 blocked,不得把 `final_parse_rejected` 当正常完成。新增分盘别名必须同时覆盖提取与文档 `CHART_IDS`。改 `REQUIRED_THEME_CHARTS` 或 demote 后必须跑 `frontend/tests/report-interpretive-facts.test.ts`;期望 write 主题 claim card 的夹具必须带对应结构化分盘,不能只标 machine section used。 - 相关记录:BUG-352、BUG-451、BUG-467 - 复发自:无 - 修复版本:待发布 diff --git a/frontend/tests/report-interpretive-facts.test.ts b/frontend/tests/report-interpretive-facts.test.ts index 720a297d..228f4a99 100644 --- a/frontend/tests/report-interpretive-facts.test.ts +++ b/frontend/tests/report-interpretive-facts.test.ts @@ -91,10 +91,21 @@ function machineSections(): JsonRecord { }; } +function engineVarga(ascIndex: number): JsonRecord { + return { + _meta: { fixture: true }, + Ascendant: { sign: SIGNS[ascIndex], sign_idx: ascIndex, degree_in_sign: 10.5 }, + Sun: { sign: SIGNS[ascIndex], sign_idx: ascIndex }, + Moon: { sign: SIGNS[(ascIndex + 1) % 12], sign_idx: (ascIndex + 1) % 12 }, + Mars: { sign: SIGNS[(ascIndex + 2) % 12], sign_idx: (ascIndex + 2) % 12 }, + }; +} + type FixtureOptions = Readonly<{ interpretive?: boolean; financeNarrative?: boolean; pollutedNames?: boolean; + structuredVargas?: boolean; }>; function workflowFixture(options: FixtureOptions = {}): JsonRecord { @@ -112,6 +123,16 @@ function workflowFixture(options: FixtureOptions = {}): JsonRecord { modules: {}, }; const modules = chart.modules as JsonRecord; + // Machine sections already mark D2/D11/D24 executed. Without the actual + // varga_full objects, demoteThemesMissingRequiredCharts strips wealth and + // education claim cards (Gitea run 2298). + if (options.structuredVargas !== false) { + modules.varga_full = { + D2: engineVarga(ASC_INDEX), + D11: engineVarga((ASC_INDEX + 2) % 12), + D24: engineVarga((ASC_INDEX + 4) % 12), + }; + } if (interpretive) { chart.yogas = [ { name: "Amala Yoga", planets: ["Mercury"], category: "extended" }, @@ -294,6 +315,21 @@ test("bundle carries the interpretive layers the engine actually computes", () = assert.deepEqual([...facts.convergenceDomains], []); }); +test("machine-section D2/D11/D24 receipts without varga_full demote wealth and education", () => { + const bundle = buildBundle({ structuredVargas: false }); + assert.equal(bundle.claimCards.some((card) => card.theme === "wealth"), false); + assert.equal(bundle.claimCards.some((card) => card.theme === "education"), false); + assert.ok(bundle.blockedSections.some((section) => section.theme === "wealth")); + assert.ok(bundle.blockedSections.some((section) => section.theme === "education")); + assert.ok(bundle.claimCards.some((card) => card.theme === "general")); + assert.deepEqual( + bundle.themeNarrativeSeeds.map((seed) => seed.theme).filter((theme) => ( + theme === "wealth" || theme === "education" + )), + [], + ); +}); + test("interpretive extraction rejects polluted planet names and over-long narrative text", () => { const bundle = buildBundle({ pollutedNames: true, financeNarrative: true }); const benefics = bundle.interpretiveFacts.functionalRoles