diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c0d5f77..86445943 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # 印度占星 Skill 更新日志 +## 2026-10-09 — 事业判断表旁边注明不按星性写方向(第二次回测仍有 1 次,未标待验收) + +- 事业判断表里,自然代表星、Jaimini 代表星、大运主星和宫主这几行旁边加了一句:只看力量与受冲,不按这颗星的本性写职业类型、做事方式或方向。其他领域不加。行和取值没有改。系统提示没有改。 +- 9 人 × 4 个领域 × 3 次,108 份都有回答。严重冲突仍是 1,对照组误报 0。27 份事业题里仍有 1 份把谋生写成靠能力和表达。不标待验收。Skill 版本不变。未推送,未合入,未上线。 + ## 2026-10-09 — 事业方向只写力量、阻力和时间;预热同一份资料只算一次(回测未达标,未推送,未合入,未上线) - 事业清单写明:「方向」只写力量、阻力、时间,以及机会出现、成形、公开落地这三件事。职业类型和工作方式仍问用户。系统提示没有改。判断表的行和取值没有改。 diff --git a/docs/BUG_HISTORY.md b/docs/BUG_HISTORY.md index 63956d8f..55260ba7 100644 --- a/docs/BUG_HISTORY.md +++ b/docs/BUG_HISTORY.md @@ -17490,16 +17490,16 @@ ## BUG-1303 | 事业题仍偶发按星性写方向(BUG-1301 残余) -- 状态:investigating(`TASK-consult-career-type-residual-20261009.md`;清单已加「方向」句,3 次抽样仍有 1 次行业冲突,按任务书停下,不标 resolved) +- 状态:investigating(`TASK-consult-career-type-residual-20261009.md`;清单句之后又在事业判断表行旁加了呈现句,3 次抽样仍有 1 次行业冲突,按任务书停下,不标 resolved) - 首次发现 / 最近更新:2026-10-09 / 2026-10-09 - 来源:BUG-1301 修复后的生平回测。 - 影响面:普通咨询事业回答。 -- 现象:上一轮 18 份事业回答中 1 份把方向写成「知识、表达、信息和资源」一类。本轮 27 份里仍有 1 份把方向写成信息和表达,与公开人物的实际职业类型不符。 +- 现象:上一轮 18 份事业回答中 1 份把方向写成「知识、表达、信息和资源」一类。清单句那一轮 27 份里仍有 1 份把方向写成信息和表达。判断表提示这轮 27 份里仍有 1 份把谋生写成靠能力和表达。都与公开人物的实际职业类型不符。 - 触发条件:判断表多处点名同一颗代表星;模型抽样波动。 -- 根因:上一句只禁止按星性推断,没有规定「方向」一节写什么。补上「方向只写力量、阻力、时间与三件事」之后,3 次抽样仍有 1 次越出这句。 -- 修复:事业清单把「方向」限定为力量、阻力、时间与三件事(机会出现 / 成形 / 公开落地),职业类型与工作方式仍问用户。不进系统提示,不删判断表,不改取值。回测未把行业类型冲突降到 0,所以不标 resolved。 -- 验证:`frontend/tests/consult-condensed-checklist-20260927.test.ts` 锁住这句,系统提示源码切片仍是 15,291 字符。生平回测 108 份、失败 0。阅读评分见 `docs/tasks/PROGRESS-consult-career-type-residual-20261009.md`:严重冲突 1,行业类型冲突 1,对照组误报 0。 -- 防复发:上述测试锁住句子。评分口径没有改。产品未授权再加一条或重跑口径。 +- 根因:清单句只规定「方向」写什么,模型仍会从事业宫主和自然代表星的星性写谋生方式。行旁的呈现句没有把这 1 次消掉。 +- 修复:事业清单把「方向」限定为力量、阻力、时间与三件事。事业判断表的自然代表星、Jaimini 代表星、大运主星、Narayana 主星和宫主行旁边加一句呈现文字,其他领域不加。不进系统提示,不删行,不改取值。两次回测都没有把行业类型冲突降到 0,所以不标 resolved。 +- 验证:`frontend/tests/consult-condensed-checklist-20260927.test.ts` 锁住清单句,系统提示源码切片仍是 15,291 字符。`frontend/tests/consult-card-full-source-20261007.test.ts` 锁住呈现句只出现在事业表,重复行仍留这句,行星取值不变。第二次生平回测 108 份、失败 0。阅读评分见 `docs/tasks/PROGRESS-consult-career-type-residual-20261009.md`:严重冲突 1,行业类型冲突 1,对照组误报 0。 +- 防复发:上述测试。评分口径没有改。任务书允许的两处都用过了,3 次抽样仍有 1 次,按让步顺序停下,不再加规则。 - 相关记录:BUG-1301、BUG-1176。 - 复发自:BUG-1301(残余,非新问题)。 - 修复版本:本分支提交。未推送,未部署。 diff --git a/docs/tasks/PROGRESS-consult-career-type-residual-20261009.md b/docs/tasks/PROGRESS-consult-career-type-residual-20261009.md index 7728f359..91576139 100644 --- a/docs/tasks/PROGRESS-consult-career-type-residual-20261009.md +++ b/docs/tasks/PROGRESS-consult-career-type-residual-20261009.md @@ -1,51 +1,52 @@ # PROGRESS · 事业方向残余 + 预热单飞(2026-10-09) -分支 `codex/consult-career-type-residual-20261009`,基线 `origin/staging` `cd625e8a`。状态:执行中。H1 的 3 次抽样仍有 1 次行业类型冲突,按任务书停下,不标待验收,不改口径。未推送,未合入,未上线。 +分支 `codex/consult-career-type-residual-20261009`。先接到 `origin/staging` `b4eedb77`(BUG-1305,保存时预热安排不上不再让保存失败)。状态:执行中。判断表提示之后,3 次抽样仍有 1 次行业类型冲突。按任务书停下,不标待验收,不改口径。这一版未推送,未合入,未上线。 ## 结论 -事业清单写明「方向」只写力量、阻力、时间和三件事。9 人 × 4 领域 × 3 次,108 份都有回答。严重冲突 1,对照组误报 0。27 份事业题里仍有 1 份把方向写成信息和表达(齐达内事业第 1 次),行业类型冲突不是 0。预热按缓存键单飞,同时最多 1 份,满了跳过。 +事业清单和判断表都写了「方向不按星性」。9 人 × 4 领域 × 3 次,108 份都有回答。严重冲突仍是 1,对照组误报 0。27 份事业题里仍有 1 份把谋生写成靠能力和表达(齐达内事业第 3 次)。预热按缓存键单飞,同时最多 1 份,这项已经做完。 ## 修复 | 项 | 做法 | 结果 | | --- | --- | --- | -| H1 方向一节 | 事业清单仍是 8 行。原来的「分三件事看」换成一句:方向只写力量、阻力、时间与三件事(机会出现 / 成形 / 公开落地),职业类型与工作方式一律问用户。不删判断表的行,不改取值,不改系统提示。 | 测试锁住这句。系统提示源码切片仍是 15,291 字符。回测未过,见下表。BUG-1303,仍 investigating。 | -| H2 预热单飞 | `get_or_build` 同一个缓存键只算一次,后到的等这份结果。预热自己的上限是 1,满了跳过,不返回 429。用户请求不占这个名额。不改 `jyotish_api_server.py`,不新增 API 类方法。 | 并发测试通过:三份不同键的预热只有 1 次构建、另外两份跳过;预热进行中,同键用户请求不再另建一份。BUG-1304。 | -| H3 | 本机不是 Linux。 | 快速门、Python 全量、npm test 全量、next build 没跑。不把上一轮 Linux 数字当成这一轮。 | +| H1 第一次 | 事业清单仍是 8 行。「方向」只写力量、阻力、时间与三件事,职业类型与工作方式问用户。 | 108 份,行业类型冲突 1。见下表「清单句」。 | +| H1 第二次 | 事业判断表的自然代表星、Jaimini 代表星、大运主星、Narayana 主星和宫主行旁边加一句:只看力量与受冲,不按这颗星的本性写职业类型、做事方式或方向。其他领域不加。不删行,不改取值,不改系统提示。 | 108 份,行业类型冲突仍是 1。见下表「判断表提示」。BUG-1303,仍 investigating。 | +| H2 预热单飞 | 同一个缓存键只算一次。预热上限 1,满了跳过。用户请求不占这个名额,也不因此收到 429。不改 `jyotish_api_server.py`。 | 并发测试通过。BUG-1304。 | +| H3 | 本机不是 Linux。 | 快速门、Python 全量、npm test 全量、next build 没跑。 | -`scripts/jyotish_api_server.py` 仍是 10,970 行。类方法数没有增加。`page.tsx` 没有改。系统提示文件没有改。判断表没有改。 +`scripts/jyotish_api_server.py` 没有改,仍是 10,970 行。类方法数没有增加。`page.tsx` 没有改。系统提示源码切片仍是 15,291 字符。最大卡仍是 16,281,低于 20,000。 ## 生平回测 模型 `deepseek-flash`。key 只在当次命令的环境变量里,没有写入仓库、日志正文或本文。引擎仍是 golden 桩,参照日 2026-09-27,岁差 raman,交点 mean。判断靠阅读,脚本只写确定性检查。口径与前两轮相同:两头都写到的范围不算误报;直接写成另一类职业,或写成幕后、不靠曝光,算行业类型冲突。 -| 项 | 这一轮(3 次) | -| --- | --- | -| 份数 / 失败 / 空答 | 108 / 0 / 0 | -| 墙钟 | 1399.2 秒 | -| 严重冲突 | 1 | -| 对照组误报 | 0 | -| 行业类型冲突 | 1 / 27 | -| 事业题请问一句 | 27 / 27 | -| 禁句 / 性别代词 / must_not 原文 | 0 / 0 / 7 | -| 平均秒 / 平均字数 / 平均卡 / 最大卡 | 50.6 / 734.9 / 14,129.8 / 16,281 | -| 平均输入 / 输出 / 推理 / 缓存输入 | 29,133.5 / 9,571.3 / 8,995.3 / 21,745.8 | +| 项 | 清单句 | 判断表提示 | +| --- | --- | --- | +| 份数 / 失败 / 空答 | 108 / 0 / 0 | 108 / 0 / 0 | +| 墙钟 | 1399.2 秒 | 1320.1 秒 | +| 严重冲突 | 1 | 1 | +| 对照组误报 | 0 | 0 | +| 行业类型冲突 | 1 / 27 | 1 / 27 | +| 事业题请问一句 | 27 / 27 | 27 / 27 | +| 禁句 / 性别代词 / must_not 原文 | 0 / 0 / 7 | 0 / 0 / 6 | +| 平均秒 / 平均字数 / 平均卡 / 最大卡 | 50.6 / 734.9 / 14,129.8 / 16,281 | 48.2 / 744.8 / 14,238.5 / 16,281 | +| 平均输入 / 输出 / 推理 / 缓存输入 | 29,133.5 / 9,571.3 / 8,995.3 / 21,745.8 | 29,375.7 / 9,243.0 / 8,660.1 / 21,915.3 | -齐达内事业第 1 次把方向写成信息和表达。第 2 次、第 3 次没有写成另一类职业,也没有写成幕后。其余 24 份事业题没有写成分析、顾问、技术,也没有写成幕后或不靠曝光。泰勒三次都没有写成不靠曝光。 +清单句那一轮:齐达内事业第 1 次把方向写成信息和表达。判断表提示这一轮:齐达内事业第 3 次把谋生写成靠能力和表达。乔布斯事业第 3 次写印象不太跟曝光绑在一起,同段又写跳到高曝光位置有阻力,没有写成幕后或不靠曝光,不计行业冲突。其余事业题没有写成分析、顾问、技术,也没有写成幕后。 -must_not 原文 7 处:布什父母两次、齐达内父母三次,都是「可能不在身边,也可能人在、只是话少」。奥巴马婚恋第 2 次的「离婚」是「离婚姻最近」里的字面重合,正文没有写离婚。这 7 处都不算对照组误报。 +must_not 原文 6 处都是「可能不在身边,也可能人在、只是话少」,或写明盘上看不出人在不在身边。这 6 处都不算对照组误报。没有把离婚姻写成离婚。 -用量合计:输入 3,146,423,输出 1,033,704,推理 971,496,缓存输入 2,348,544。 +用量合计:输入 3,172,579,输出 998,241,推理 935,295,缓存输入 2,366,848。 ## 本机检查 | 项 | 结果 | | --- | --- | -| 事业清单及相关测试 | 19 条通过。含新句子、系统提示长度、事业题仍问用户。 | -| 预热单飞、重计算名额、增长合同 | 通过。API 文件没有增长。 | +| 事业清单、判断表及相关测试 | 23 条通过,0 取消。含新句子、呈现句只在事业表、系统提示长度、事业题仍问用户。 | +| 预热单飞、重计算名额、增长合同 | 24 条通过。API 文件没有增长。 | | tsc | 0 | -| lint | 0 error,125 条原有 warning,没有清理。 | +| 改动文件的 lint | 0 error | | 快速门、Python 全量、npm test 全量、next build、Linux | 本轮没跑。 | -H1 未过。按让步顺序停下,交给产品决定。不改评分口径,不加第二条规则,不标待验收。 +H1 未过。任务书允许的两处都用过了。按让步顺序停下,交给产品决定。不改评分口径,不再加一条规则,不标待验收。 diff --git a/docs/tasks/README.md b/docs/tasks/README.md index 4225de3f..31b0b777 100644 --- a/docs/tasks/README.md +++ b/docs/tasks/README.md @@ -132,7 +132,7 @@ | `TASK-consult-card-full-source-20261007.md` | `PROGRESS-consult-card-full-source-20261007.md` | **对话数据卡与报告同源 + 领域综合判断表**:事业卡只有约 7,600 字符、无自然代表星、无大运主星关系、D10 联动只能补取、补取上限 1;全 12 领域;综合判断表、参与清单(不止 D1/D9,含精度闸)、补取 3 次、资料目录均已确认;KP 不上卡、不联网、不接书籍语料;排在 sync6 与报告单之后 | 验收未通过(`35dafd1e`,见 fix-20261009) | `codex/consult-card-full-source-20261007` | | `TASK-consult-card-full-source-fix-20261009.md` | `PROGRESS-consult-card-full-source-fix-20261009.md` | **对话卡同源修复**:缓存键无日期致当前大运冻结、整卡 37.5K(每格带内部路径)、星照整列 blocked(资料库其实有出处)、前端测试手写数据、无预热;合入前用临时 key 跑模型 A/B 与生平回测 | F1–F5 已验收,F6 未过(见 fix2) | `codex/consult-card-full-source-20261007` | | `TASK-consult-card-full-source-fix2-20261009.md` | `PROGRESS-consult-card-full-source-fix2-20261009.md` | **对话卡同源第二轮**:齐达内事业被「水星=说写算」带偏致严重冲突 1→3;事业清单加「代表星只看力量受冲、不按星性猜职业」后重跑回测(≤1 才合入);预热改为不占重计算名额 | 已验收并快进合入 staging(2026-10-09,产品按严重冲突 ≤ 1 放行;剩余 1 次见 career-type-residual) | `codex/consult-card-full-source-20261007` | -| `TASK-consult-career-type-residual-20261009.md` | `PROGRESS-consult-career-type-residual-20261009.md` | **事业题按星性写方向的残余 + 预热单飞**:方向一节只写力量、阻力、时间和三件事;预热按缓存键单飞、同时最多 1 份 | 执行中(H1:3 次抽样仍有 1 次行业冲突,未让步,未标待验收) | `codex/consult-career-type-residual-20261009` | +| `TASK-consult-career-type-residual-20261009.md` | `PROGRESS-consult-career-type-residual-20261009.md` | **事业题按星性写方向的残余 + 预热单飞**:方向一节只写力量、阻力、时间和三件事;判断表事业行旁加一句只看力量;预热按缓存键单飞、同时最多 1 份 | 执行中(H1:判断表提示后 3 次抽样仍有 1 次行业冲突,未让步,未标待验收) | `codex/consult-career-type-residual-20261009` | | — (产品 09-27 拍板 D1–D4,直接执行) | [PROGRESS](PROGRESS-home-landing-blank-20260927.md) | **登录后 / 裸 `/` 落空白首页**:真机登录后在「首页」提问其实问进了上一次生时校正。删登录返回存根(401 / 次级页链接写、登录后写回 `?c=` 打开),裸 `/` 不再落最近会话,一律当前人物的空白首页(复用空草稿);`?c=` / `?new=1` / 对话内刷新不变;推翻 BUG-1038 存根与 BUG-599 默认落点 | 已验收(真机欠) | `codex/home-landing-blank-20260927`(BUG-1052,本地未推) | | — (产品 09-26 口头拍板 D1–D3,直接执行) | `PROGRESS-consult-answer-truncation-20260926.md` | **普通咨询回答写到一半被掐断仍扣点(BUG-1051,复发自 BUG-305)**:工具循环与写回答共用 110 秒 signal;Mastra 1.50 超时不抛错(`abort` 块 + `finish(tripwire)` 后正常关流),结算只认抛错与 `length`。D1 写回答自有 70 秒时钟(首用起算,续写 / 回答重试共用,最坏 180 秒,`maxDuration` 240);D2 写回答的最后一个流不是 `stop` 且有正文 → `answer_truncated`、不扣点、记 abort 步、不冲半句,`length` 续写不变;D3 观测加 `composeFinishReason` / `composeAborted` / `answerVisibleChars` | 已验收(真机欠) | `codex/consult-answer-truncation-20260926`(本地,未推送);新回归 15 条用真实 Mastra `Agent`(修复前 11 条红);全量失败名单 0 新增;Python 948/1;`/` ○、gzip 0%;真机清单 `docs/testing/consult-answer-truncation-20260926.md` | | `TASK-consult-evidence-card-research-20260927.md` | `PROGRESS-consult-evidence-card-research-20260927.md` | **普通对话数据卡调研**:引擎输出逐项分五类计量(现约 4 万 token、父母问题相关约 3.5%);四处领域→技法来源对账并起草各领域数据卡(家庭拆父母/子女);卡体量与逐字一致性;按卡算的提速空间;反馈迭代埋点方案。只调研不改线上 | 已验收(待产品拍板 7 项) | `codex/consult-evidence-card-research-20260927` 快进 staging;报告 `docs/research/consult_evidence_card_research_2026_09_27.md`;投影缺口记 BUG-1054 investigating | diff --git a/frontend/src/lib/consultation-judgment-table.ts b/frontend/src/lib/consultation-judgment-table.ts index c037fb42..0ad816b6 100644 --- a/frontend/src/lib/consultation-judgment-table.ts +++ b/frontend/src/lib/consultation-judgment-table.ts @@ -16,8 +16,20 @@ export const JUDGMENT_TABLE_LABELS = { relationOccupant: "落入", relationAspect: "照", relationConjunctLord: "与宫主同宫", + careerSignificatorReading: "这行只看力量与受冲,不按这颗星的本性写职业类型、做事方式或方向。", } as const; +const CAREER_JAIMINI_ROLES = new Set(["AmK", "DK", "PK", "MK", "PiK"]); + +/** Presentation only. Career significator rows keep their copied values. */ +function careerSignificatorReading(domain: string, role: string | null): { reading: string } | Record { + if (domain !== "career" || !role) return {}; + const dasha = role.startsWith("Vimshottari"); + const named = role === "natural" || role === "lord" || role === "Narayana MD lord" || CAREER_JAIMINI_ROLES.has(role); + if (!dasha && !named) return {}; + return { reading: JUDGMENT_TABLE_LABELS.careerSignificatorReading }; +} + const MALEFIC_BODIES = ["Saturn", "Mars", "Rahu", "Ketu"] as const; const ASPECT_MALEFICS = ["Saturn", "Mars"] as const; const DUSTHANA = new Set([6, 8, 12]); @@ -247,6 +259,7 @@ export function assembleJudgmentTable(facts: unknown): JsonRecord { const body = record(facts); const planets = record(body.planets); const houses = record(body.houses); + const domain = typeof body.domain === "string" ? body.domain : ""; const rows: Json[] = []; const factors = Array.isArray(body.factors) ? body.factors : []; for (const factor of factors) { @@ -276,6 +289,7 @@ export function assembleJudgmentTable(facts: unknown): JsonRecord { role: "lord", citation, ...planetColumns(lord, planets, houses, "lord"), + ...careerSignificatorReading(domain, "lord"), }); } else { rows.push({ id: `${id}:lord`, kind: "gap", citation, path: leaf(house.lord).path ?? null }); @@ -283,12 +297,14 @@ export function assembleJudgmentTable(facts: unknown): JsonRecord { continue; } if (item.kind === "planet" && typeof item.planet === "string") { + const role = typeof item.role === "string" ? item.role : null; rows.push({ id, kind: "planet", - role: typeof item.role === "string" ? item.role : null, + role, citation, - ...planetColumns(item.planet, planets, houses, typeof item.role === "string" ? item.role : undefined), + ...planetColumns(item.planet, planets, houses, role ?? undefined), + ...careerSignificatorReading(domain, role), }); continue; } @@ -312,7 +328,7 @@ export function assembleJudgmentTable(facts: unknown): JsonRecord { } return { header_note: typeof body.header_note === "string" ? body.header_note : JUDGMENT_TABLE_LABELS.rahuNote, - domain: typeof body.domain === "string" ? body.domain : "", + domain, domain_varga: typeof body.domain_varga === "string" ? body.domain_varga : "", rows, }; @@ -374,6 +390,7 @@ function compactPlanetRows(rows: Json[]): Json[] { const repeat: JsonRecord = { id: row.id, kind: row.kind, planet: row.planet }; if (typeof row.role === "string") repeat.role = row.role; if (typeof row.citation === "string") repeat.citation = row.citation; + if (typeof row.reading === "string") repeat.reading = row.reading; if (row.affliction !== undefined) repeat.affliction = row.affliction; return repeat; }); diff --git a/frontend/tests/consult-card-full-source-20261007.test.ts b/frontend/tests/consult-card-full-source-20261007.test.ts index e2437201..d9034cf9 100644 --- a/frontend/tests/consult-card-full-source-20261007.test.ts +++ b/frontend/tests/consult-card-full-source-20261007.test.ts @@ -7,7 +7,7 @@ import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; import { buildEvidenceCard } from "../src/lib/consultation-evidence-card.ts"; -import { assembleJudgmentTable, JUDGMENT_TABLE_LABELS } from "../src/lib/consultation-judgment-table.ts"; +import { assembleJudgmentTable, judgmentTableModelView, JUDGMENT_TABLE_LABELS } from "../src/lib/consultation-judgment-table.ts"; import type { ConsultationDomain } from "../src/lib/consultation-domain-registry.ts"; import { createConsultationPlan } from "../src/lib/consultation-plan.ts"; import { @@ -129,6 +129,47 @@ test("the judgment table copies leaves and does not turn the count into a verdic assert.deepEqual((rahu.co_tenants as { value: string[] }).value, ["Mars"]); const blocked = rows.find((row) => row.id === "jaimini"); assert.equal(blocked?.kind, "blocked"); + const house = rows.find((row) => row.id === "house:10"); + assert.equal(house?.reading, undefined); + assert.equal(lord.reading, JUDGMENT_TABLE_LABELS.careerSignificatorReading); + assert.equal(rahu.reading, JUDGMENT_TABLE_LABELS.careerSignificatorReading); +}); + +// Assertion change: +// 原值: 事业判断表的行星行没有呈现句;重复出现的行星只留 id、角色、引用和受冲计数。 +// 新值: 事业的自然代表星、Jaimini 代表星、大运主星、Narayana 主星和宫主行带同一句呈现文字。婚恋表不带。重复行仍丢掉度数,但留下这句。行星的星座、宫位、旺弱不变。 +// 原因: 清单句之后仍有 1 次按星性写方向。模型读的是事业宫主行。只加呈现文字,不改取值(TASK-consult-career-type-residual-20261009)。 +test("career significator rows name strength only, and other domains stay unchanged", () => { + const career = assembleJudgmentTable({ + ...facts, + factors: [ + ...facts.factors, + { id: "jaimini:AmK", kind: "planet", planet: "Saturn", citation: "jaimini", role: "AmK" }, + { id: "jaimini:DK", kind: "planet", planet: "Saturn", citation: "jaimini", role: "DK" }, + { id: "vimshottari:md", kind: "planet", planet: "Sun", citation: "vim", role: "Vimshottari MD" }, + { id: "narayana:lord", kind: "planet", planet: "Sun", citation: "narayana", role: "Narayana MD lord" }, + ], + }); + const rows = career.rows as Array>; + for (const id of ["house:10:lord", "natural:Saturn", "jaimini:AmK", "jaimini:DK", "vimshottari:md", "narayana:lord"]) { + assert.equal(rows.find((row) => row.id === id)?.reading, JUDGMENT_TABLE_LABELS.careerSignificatorReading, id); + } + const lord = rows.find((row) => row.id === "house:10:lord") as Record; + assert.equal(lord.planet, "Sun"); + assert.equal((lord.d1_sign as { value: string }).value, "Aries"); + assert.equal((lord.d1_house as { value: number }).value, 10); + assert.equal((lord.d1_dignity as { value: string }).value, "落陷(Debilitated)"); + const marriage = assembleJudgmentTable({ ...facts, domain: "marriage" }); + const marriageEncoded = JSON.stringify(marriage); + assert.equal(marriageEncoded.includes(JUDGMENT_TABLE_LABELS.careerSignificatorReading), false); + assert.equal((marriage.rows as Array>).find((row) => row.id === "house:10:lord")?.planet, "Sun"); + const view = judgmentTableModelView(career) as { rows: Array> }; + const repeat = view.rows.find((row) => row.id === "vimshottari:md"); + assert.equal(repeat?.planet, "Sun"); + assert.equal(repeat?.role, "Vimshottari MD"); + assert.equal(repeat?.reading, JUDGMENT_TABLE_LABELS.careerSignificatorReading); + assert.equal(repeat?.d1_sign, undefined); + assert.equal(repeat?.d1_dignity, undefined); }); test("a card without facts keeps the old section, and facts attach one table", () => { @@ -219,6 +260,11 @@ test("engine facts make a model card without internal paths", () => { assert.equal(encoded.includes("core_chart."), false, `${chart} ${domain}`); assert.equal(encoded.includes("\"check\""), false, `${chart} ${domain}`); assert.equal(encoded.includes("两个及以上"), false); + assert.equal( + encoded.includes(JUDGMENT_TABLE_LABELS.careerSignificatorReading), + domain === "career", + `${chart} ${domain}`, + ); assert.ok(encoded.length <= 20_000, `${chart} ${domain} ${encoded.length}`); const rows = (card.domain_sections[domain as ConsultationDomain]?.judgment_table?.rows ?? []) as Array>; const hasAspect = rows.some((row) => (